141 lines
5 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|