1
0
mirror of https://github.com/chylex/Query.git synced 2024-10-16 07:42:46 +02:00
Query/Calculator/CalculatorExpressionVisitor.cs

72 lines
2.3 KiB
C#

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);
}
}