using System; using System.Diagnostics; namespace MathInterpreter.Core; /// /// Represents a mathematical expression /// public sealed class MathExpression { private double? _leftOperand; private Operator? _operator; private double? _result; private double? _rightOperand; /// /// Creates a new instance of from the provided expression string. /// Validates the provided expression and throws an exception if it is invalid. /// /// The expression as a string /// Thrown if a problem with the operator is found /// Thrown if a problem with the expression format is found /// Thrown if a problem with the value of a number is found public MathExpression(string expressionString) { ValidateAndParse(expressionString.Trim().AsSpan()); } /// /// Gets the calculated result of the expression /// /// /// Thrown if validation during construction did not guard against an unexpected, invalid expression /// public double Result { get { if (_result.HasValue) { return _result.Value; } if (!_leftOperand.HasValue || !_operator.HasValue || !_rightOperand.HasValue) { throw new UnreachableException("Expression is not fully parsed"); } double left = _leftOperand.Value; double right = _rightOperand.Value; _result = _operator.Value switch { Operator.Plus => left + right, Operator.Minus => left - right, Operator.Multiply => left * right, Operator.Divide => left / right, _ => throw new UnreachableException("Unknown operator") }; return _result.Value; } } /// /// Returns a string representation of the expression - this is not the originally provided string /// but the cleaned up version /// /// String representation of the expression /// /// Thrown if validation during construction did not guard against an unexpected, invalid expression /// and left this instance in an invalid state /// public override string ToString() { if (!_leftOperand.HasValue || !_operator.HasValue || !_rightOperand.HasValue) { throw new UnreachableException("Expression is not fully parsed"); } string operatorSymbol = _operator.Value switch { Operator.Plus => "+", Operator.Minus => "-", Operator.Multiply => "*", Operator.Divide => "/", _ => throw new UnreachableException("Unknown operator") }; return $"{_leftOperand.Value} {operatorSymbol} {_rightOperand.Value}"; } private void ValidateAndParse(ReadOnlySpan expression) { int index = 0; while (index < expression.Length && char.IsWhiteSpace(expression[index])) { index++; } if (index < expression.Length) { char firstChar = expression[index]; if (firstChar is '+' or '*' or '/') { throw new OperatorException("Operator is missing"); } } try { var leftOperandResult = ExpressionScanner.ScanOperand(expression); var operatorResult = ExpressionScanner.ScanOperator(leftOperandResult.RemainingExpression); _leftOperand = leftOperandResult.Value; _operator = operatorResult.Value; try { var rightOperandResult = ExpressionScanner.ScanOperand(operatorResult.RemainingExpression); _rightOperand = rightOperandResult.Value; if (_operator == Operator.Divide && _rightOperand == 0) { throw new NumberValueException("Division by zero is not allowed"); } } catch (ExpressionFormatException) { throw new ExpressionFormatException("Right operand is missing"); } } catch (ExpressionException) { throw; } catch (Exception ex) { throw new ExpressionFormatException("Unable to parse expression", ex); } } }