private void Collect(KalkExpressionSimplifier simplifier) { Collect(simplifier._value); if (simplifier._powers != null) { foreach (var power in simplifier._powers) { Collect(power); } } }
public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, SourceSpan leftSpan, object leftValue, SourceSpan rightSpan, object rightValue, out object result) { var leftExpr = leftValue as KalkExpression; if (leftExpr is null && !KalkValue.IsNumber(leftValue)) { throw new ScriptRuntimeException(leftSpan, "Expecting a number, vector or matrix"); } var rightExpr = rightValue as KalkExpression; if (rightExpr is null && !KalkValue.IsNumber(rightValue)) { throw new ScriptRuntimeException(rightSpan, "Expecting a number, vector or matrix"); } result = null; switch (op) { case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareNotEqual: case ScriptBinaryOperator.CompareLessOrEqual: case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareGreater: if (leftExpr != null && rightExpr != null) { var leftSimplifier = new KalkExpressionSimplifier(context); var(leftValueMultiplier, newLeftExpr) = leftSimplifier.Canonical(leftExpr); var rightSimplifier = new KalkExpressionSimplifier(context); var(rightValueMultiplier, newRightExpr) = rightSimplifier.Canonical(rightExpr); var exprEquals = Equals(context, newLeftExpr, newRightExpr); if (exprEquals) { var almostEqual = KalkValue.AlmostEqual(leftValueMultiplier, rightValueMultiplier); switch (op) { case ScriptBinaryOperator.CompareEqual: result = almostEqual; break; case ScriptBinaryOperator.CompareNotEqual: result = !almostEqual; break; case ScriptBinaryOperator.CompareLessOrEqual: result = almostEqual || ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v1 && v1; break; case ScriptBinaryOperator.CompareGreaterOrEqual: result = almostEqual || ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v2 && v2; break; case ScriptBinaryOperator.CompareLess: result = ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v3 && v3; break; case ScriptBinaryOperator.CompareGreater: result = ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v4 && v4; break; } } else { result = op == ScriptBinaryOperator.CompareNotEqual; } } else { if (op == ScriptBinaryOperator.CompareEqual || op == ScriptBinaryOperator.CompareNotEqual) { result = op == ScriptBinaryOperator.CompareNotEqual; } else { throw NotMatching(leftSpan, leftExpr, rightSpan, rightExpr); } } return(true); case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.Power: { if (op != ScriptBinaryOperator.Power || rightExpr == null) { var simplifier = new KalkExpressionSimplifier(context); var(valueMul, valueExpr) = simplifier.Canonical(new KalkBinaryExpression(leftValue, op, rightValue)); var valueBinExpr = valueExpr as KalkBinaryExpression; result = KalkValue.AlmostEqual(valueMul, 1.0) && valueBinExpr == null ? valueExpr ?? (object)1.0 : valueExpr == null ? valueMul : (object)new KalkBinaryExpression(valueMul, ScriptBinaryOperator.Multiply, valueExpr) { OriginalExpression = new KalkBinaryExpression(leftValue, op, rightValue) }; return(true); } else { throw new ScriptRuntimeException(rightSpan, "Cannot use a unit as an exponent."); } } case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: if (leftExpr != null && rightExpr != null) { var leftSimplifier = new KalkExpressionSimplifier(context); var(leftValueMultiplier, newLeftExpr) = leftSimplifier.Canonical(leftExpr); var rightSimplifier = new KalkExpressionSimplifier(context); var(rightValueMultiplier, newRightExpr) = rightSimplifier.Canonical(rightExpr); if (!Equals(context, newLeftExpr, newRightExpr)) { throw new ScriptRuntimeException(span, $"Cannot {(op == ScriptBinaryOperator.Add ? "add" : "subtract")} the expression. Units are not matching. The left expression with unit `{newLeftExpr}` is not matching the right expression with unit `{newRightExpr}`."); } result = new KalkBinaryExpression(ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier), ScriptBinaryOperator.Multiply, newLeftExpr) { OriginalExpression = new KalkBinaryExpression(leftValue, op, rightValue) }; } else { throw NotMatching(leftSpan, leftExpr, rightSpan, rightExpr); } return(true); } return(false); }
private void Collect(object value) { if (value == null) { return; } if (value is KalkUnit symbol) { if (symbol.Value != null) { Collect(symbol.Value); } else { if (_powers == null) { _powers = new List <KalkBinaryExpression>(); } _powers.Add(new KalkBinaryExpression(symbol, ScriptBinaryOperator.Power, 1)); } } else if (value is KalkBinaryExpression binary) { if (binary.Operator == ScriptBinaryOperator.Multiply) { var leftSimplifier = new KalkExpressionSimplifier(_context); leftSimplifier.Collect(binary.Value); Collect(leftSimplifier); var rightSimplifier = new KalkExpressionSimplifier(_context); rightSimplifier.Collect(binary.Unit); Collect(rightSimplifier); } else if (binary.Operator == ScriptBinaryOperator.Divide) { Collect(binary.Value); if (binary.Unit is KalkExpression) { Collect(new KalkBinaryExpression(binary.Unit, ScriptBinaryOperator.Power, -1)); } else { var result = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Divide, 1, binary.Unit); SquashValue(result); } } else { Debug.Assert(binary.Operator == ScriptBinaryOperator.Power); if (_powers == null) { _powers = new List <KalkBinaryExpression>(); } // (a * b^2) ^ 5 // a ^ 5 * b ^ 10 var subSimplifier = new KalkExpressionSimplifier(_context); subSimplifier.Collect(binary.Value); var subValue = _context.ToObject <double>(_context.CurrentSpan, subSimplifier._value); if (!KalkValue.AlmostEqual(subValue, 1.0)) { var result = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Power, subSimplifier._value, binary.Unit); SquashValue(result); } if (subSimplifier._powers != null) { foreach (var powerExpression in subSimplifier._powers) { var powerResult = ScriptBinaryExpression.Evaluate(_context, _context.CurrentSpan, ScriptBinaryOperator.Multiply, powerExpression.Unit, binary.Unit); var newPower = new KalkBinaryExpression(powerExpression.Value, ScriptBinaryOperator.Power, powerResult); _powers.Add(newPower); } } } } else { SquashValue(value); } }