using Calculator.Math; using Calculator.Parser; namespace Calculator; public sealed class CalculatorExpressionVisitor : ExpressionVisitor<NumberWithUnit> { public NumberWithUnit VisitNumber(Expression.Number number) { return new NumberWithUnit(number.NumberToken.Value, null); } public NumberWithUnit VisitNumbersWithUnits(Expression.NumbersWithUnits numbersWithUnits) { NumberWithUnit result = new Number.Rational(0); foreach ((Token.Number number, Unit unit) in numbersWithUnits.NumberTokensWithUnits) { result += new NumberWithUnit(number.Value, unit); } return result; } public NumberWithUnit VisitGrouping(Expression.Grouping grouping) { return Evaluate(grouping.Expression); } public NumberWithUnit VisitUnary(Expression.Unary unary) { (Token.Simple op, Expression right) = unary; return op.Type switch { SimpleTokenType.PLUS => +Evaluate(right), SimpleTokenType.MINUS => -Evaluate(right), _ => throw new CalculatorException("Unsupported unary operator: " + op.Type) }; } public NumberWithUnit VisitBinary(Expression.Binary binary) { (Expression left, Token.Simple op, Expression right) = binary; return op.Type switch { SimpleTokenType.PLUS => Evaluate(left) + Evaluate(right), SimpleTokenType.MINUS => Evaluate(left) - Evaluate(right), SimpleTokenType.STAR => Evaluate(left) * Evaluate(right), SimpleTokenType.SLASH => Evaluate(left) / Evaluate(right), SimpleTokenType.PERCENT => Evaluate(left) % Evaluate(right), SimpleTokenType.CARET => Evaluate(left).Pow(Evaluate(right)), _ => throw new CalculatorException("Unsupported binary operator: " + op.Type) }; } public NumberWithUnit VisitUnitAssignment(Expression.UnitAssignment unitAssignment) { (Expression left, Unit right) = unitAssignment; NumberWithUnit number = Evaluate(left); if (number.Unit is null) { return number with { Unit = right }; } else { throw new CalculatorException("Expression already has a unit, cannot assign a new unit: " + right); } } public NumberWithUnit VisitUnitConversion(Expression.UnitConversion unitConversion) { (Expression left, Unit unit) = unitConversion; return Evaluate(left).ConvertTo(unit); } private NumberWithUnit Evaluate(Expression expression) { return expression.Accept(this); } }