ex-ex-01-math-interpreter/MathInterpreter/Core/MathExpression.cs
2025-06-05 14:36:50 +02:00

141 lines
5 KiB
C#

using System;
using System.Diagnostics;
namespace MathInterpreter.Core;
/// <summary>
/// Represents a mathematical expression
/// </summary>
public sealed class MathExpression
{
private double? _leftOperand;
private Operator? _operator;
private double? _result;
private double? _rightOperand;
/// <summary>
/// Creates a new instance of <see cref="MathExpression" /> from the provided expression string.
/// Validates the provided expression and throws an exception if it is invalid.
/// </summary>
/// <param name="expressionString">The expression as a string</param>
/// <exception cref="OperatorException">Thrown if a problem with the operator is found</exception>
/// <exception cref="ExpressionFormatException">Thrown if a problem with the expression format is found</exception>
/// <exception cref="NumberValueException">Thrown if a problem with the value of a number is found</exception>
public MathExpression(string expressionString)
{
ValidateAndParse(expressionString.Trim().AsSpan());
}
/// <summary>
/// Gets the calculated result of the expression
/// </summary>
/// <exception cref="UnreachableException">
/// Thrown if validation during construction did not guard against an unexpected, invalid expression
/// </exception>
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;
}
}
/// <summary>
/// Returns a string representation of the expression - this is not the originally provided string
/// but the cleaned up version
/// </summary>
/// <returns>String representation of the expression</returns>
/// <exception cref="UnreachableException">
/// Thrown if validation during construction did not guard against an unexpected, invalid expression
/// and left this instance in an invalid state
/// </exception>
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<char> 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);
}
}
}