public Evaluation EvaluateOperator( ExpressionOptions options, ValueToken op, Token?left, Token?right) { var validationError = ValidateOperatorTokens(options, op, left, right); if (validationError != null) { return(validationError); } var leftEval = left != null?Evaluate(options, left) : null; if (leftEval?.Error != null) { return(leftEval); } var rightEval = right != null?Evaluate(options, right) : null; if (rightEval?.Error != null) { return(rightEval); } return(ExecuteOperator.Invoke(op.Value, leftEval?.Result, rightEval?.Result)); }
public static ResultCode AssignParents(ExpressionOptions options, List <Token> root) { var stack = new Stack <CollectionToken>(); // Tokens in the root list have 'null' parent. foreach (var token in root) { if (token is CollectionToken collection) { stack.Push(collection); } } while (stack.Count > 0) { var parent = stack.Pop(); foreach (var token in parent) { token.Parent = parent; if (token is CollectionToken collection) { stack.Push(collection); } } } return(ResultCode.Ok); }
private Evaluation ListToCollection(ExpressionOptions options, List <Token> tokens) { int valueCount = 0; for (int i = 0; i < tokens.Count; i++) { if (tokens[i].Type != TokenType.ListSeparator) { valueCount++; } } var evaluations = new object?[valueCount]; int evalIndex = 0; for (int i = 0; i < tokens.Count; i++) { if (tokens[i].Type == TokenType.ListSeparator) { continue; } var eval = Evaluate(options, tokens[i]); if (eval.Error != null) { return(eval); } evaluations[evalIndex++] = eval.Result; } return(new Evaluation(evaluations)); }
public static ResultCode Parse(ExpressionOptions options, List <Token> tokens) { if (tokens == null) { throw new ArgumentNullException(nameof(tokens)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } if (tokens.Count == 0) { return(ResultCode.NoTokens); } // TODO: check for valid placement of list separators ResultCode code; if ((code = MakeLists(tokens)) != ResultCode.Ok || (code = MakeFunctions(options, tokens)) != ResultCode.Ok || (code = MakeImplicitMultiplications(options, tokens)) != ResultCode.Ok || (code = MakeOperatorGroups(options, tokens)) != ResultCode.Ok || (code = AssignParents(options, tokens)) != ResultCode.Ok) { return(code); } return(code); }
private static ResultCode ReduceLists(ExpressionOptions options, List <Token> tokens) { var listStack = new Stack <List <Token> >(); listStack.Push(tokens); while (listStack.Count > 0) { var currentTokens = listStack.Pop(); for (int i = 0; i < currentTokens.Count; i++) { var token = currentTokens[i]; if (token.Type == TokenType.List) { var listToken = (ListToken)token; if (listToken.Count == 1) { var listChild = listToken[0]; currentTokens[i] = listChild; i--; } listStack.Push(listToken.Children); } } } return(ResultCode.Ok); }
private bool ValidateOperatorList( ExpressionOptions options, List <Token> tokens, [MaybeNullWhen(false)] out ValueToken opToken, out Token?leftToken, out Token?rightToken) { if (tokens.Count == 2) { for (int opIndex = 0; opIndex < tokens.Count; opIndex++) { var token = tokens[opIndex]; if (token.Type != TokenType.Operator) { continue; } var opDefinitions = options.OpDefinitions.Span; for (int j = 0; j < opDefinitions.Length; j++) { var opDef = opDefinitions[j]; if (opDef.Associativity == OperatorSidedness.Both) { continue; } opToken = (ValueToken)token; for (int k = 0; k < opDef.Names.Length; k++) { var name = opDef.Names[k]; if (!name.Span.SequenceEqual(opToken.Value)) { continue; } leftToken = opIndex == 0 ? null : tokens[0]; rightToken = opIndex == 1 ? null : tokens[1]; return(true); } } break; } } if (tokens.Count == 3 && tokens[1] is ValueToken middleToken && middleToken.Type == TokenType.Operator) { opToken = middleToken; leftToken = tokens[0]; rightToken = tokens[2]; return(true); } opToken = null; leftToken = null; rightToken = null; return(false); }
private static ResultCode MakeImplicitMultiplications( ExpressionOptions options, List <Token> tokens) { for (int i = 0; i < tokens.Count; i++) { var token = tokens[i]; if (token is CollectionToken collectionToken) { var code = MakeImplicitMultiplications(options, collectionToken.Children); if (code != ResultCode.Ok) { return(code); } } else if (token.Type != TokenType.Name) { // Skip as this type is not allowed to have an implicit factor prefix. continue; } if (i - 1 < 0) { continue; // We are at the list's beginning. } var leftToken = tokens[i - 1]; if (leftToken.Type == TokenType.Operator || leftToken.Type == TokenType.Name || leftToken.Type == TokenType.ListSeparator) { continue; } if (leftToken.Type != TokenType.DecimalDigit && leftToken.Type != TokenType.DecimalNumber && leftToken.Type != TokenType.List) { return(ResultCode.InvalidTokenBeforeList); } var multiplyOpDef = options.GetOperatorDefinition(OperatorType.Multiply); if (multiplyOpDef == null) { return(ResultCode.MissingMultiplicationDefinition); } var opToken = new ValueToken(TokenType.Operator, multiplyOpDef.Names[0]); tokens.Insert(i, opToken); } return(ResultCode.Ok); }
public Evaluation EvaluateList(ExpressionOptions options, List <Token> tokens) { if (tokens.Count == 0) { return(new Evaluation(EErrorCode.Empty)); } if (tokens.Count == 1) { return(Evaluate(options, tokens[0])); } if (ValidateOperatorList( options, tokens, out var opToken, out var leftToken, out var rightToken)) { return(EvaluateOperator(options, opToken, leftToken, rightToken)); } return(ListToCollection(options, tokens)); }
static ExpressionOptions() { var opDefs = new Dictionary <OperatorType, OperatorDefinition>(); void AddOpDef(int precedence, OperatorType type, OperatorSidedness sidedness, params char[] names) { var opDef = new OperatorDefinition(precedence, type, sidedness, names); opDefs.Add(type, opDef); } AddOpDef(0, OperatorType.Add, OperatorSidedness.OptionalLeft, '+'); AddOpDef(0, OperatorType.Subtract, OperatorSidedness.OptionalLeft, '-', '–'); AddOpDef(1, OperatorType.Multiply, OperatorSidedness.Both, '*', '×'); AddOpDef(1, OperatorType.Divide, OperatorSidedness.Both, '/', ':', '÷'); AddOpDef(1, OperatorType.Modulo, OperatorSidedness.Both, '%'); AddOpDef(2, OperatorType.Exponent, OperatorSidedness.Both, '^'); AddOpDef(2, OperatorType.Factorial, OperatorSidedness.Left, '!'); var opInverses = new List <OperatorInverse>(); void AddOpInverse(OperatorType source, OperatorType inverse) { var sourceOp = opDefs[source]; var inverseOp = opDefs[inverse]; opInverses.Add(new OperatorInverse(sourceOp, inverseOp)); } AddOpInverse(OperatorType.Add, OperatorType.Subtract); AddOpInverse(OperatorType.Subtract, OperatorType.Add); AddOpInverse(OperatorType.Multiply, OperatorType.Divide); AddOpInverse(OperatorType.Divide, OperatorType.Multiply); Default = new ExpressionOptions( opDefs.Values, opInverses, doImplicitMultiplications: true); }
public Evaluation Evaluate(ExpressionOptions options, Token token) { if (token is ListToken listToken) { return(EvaluateList(options, listToken.Children)); } if (token is ValueToken valueToken) { string value = valueToken.Value; switch (valueToken.Type) { case TokenType.DecimalDigit: return(new Evaluation(CharUnicodeInfo.GetNumericValue(value[0]))); case TokenType.DecimalNumber: if (value.Length == 1) { return(new Evaluation(CharUnicodeInfo.GetNumericValue(value[0]))); } else { var parsed = double.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); return(new Evaluation(parsed)); } case TokenType.Name: return(ResolveReference.Invoke(valueToken.Value)); default: return(new Evaluation(EErrorCode.SyntaxError)); } } else if (token is FunctionToken funcToken) { return(EvaluateFunction(options, funcToken)); } return(new Evaluation(EErrorCode.Undefined)); }
public static ResultCode Reduce(ExpressionOptions options, List <Token> tokens) { // TODO: the reducer can currently break expressions pretty hard, so fix it throw new NotImplementedException(); if (tokens == null) { throw new ArgumentNullException(nameof(tokens)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } ResultCode code; if ((code = ReduceLists(options, tokens)) != ResultCode.Ok) { return(code); } return(code); }
public static EError?ValidateOperatorTokens( ExpressionOptions options, ValueToken op, Token?left, Token?right) { var opDef = options.GetOperatorDefinition(op.Value); if (opDef != null) { if (left == null && ( opDef.Associativity == OperatorSidedness.Both || opDef.Associativity.HasFlag(OperatorSidedness.Left))) { return(EErrorCode.OperatorMissingLeftValue); } if (right == null && ( opDef.Associativity == OperatorSidedness.Both || opDef.Associativity.HasFlag(OperatorSidedness.Right))) { return(EErrorCode.OperatorMissingRightValue); } } return(null); }
public Evaluation EvaluateFunction(ExpressionOptions options, FunctionToken function) { var arguments = new JToken?[function.ArgumentCount]; int valueIndex = 0; for (int i = 0; i < function.Children.Count; i++) { var arg = function.Children[i]; if (arg.Type == TokenType.ListSeparator) { continue; } var eval = Evaluate(options, arg); if (eval.Error != null) { return(eval); } arguments[valueIndex] = eval.Result; valueIndex++; } return(ExecuteFunction.Invoke(function.Name.Value, arguments)); }
private static ResultCode MakeOperatorGroups(ExpressionOptions options, List <Token> tokens) { var listStack = new Stack <List <Token> >(); listStack.Push(tokens); var opIndices = new List <(int index, ValueToken token, OperatorDefinition definition)>(); var opShifts = new List <(int index, int shift)>(); while (listStack.Count > 0) { var currentTokens = listStack.Pop(); for (int j = 0; j < currentTokens.Count; j++) { var token = currentTokens[j]; if (token is CollectionToken collectionToken) { listStack.Push(collectionToken.Children); } } // Gather operators so we can sort them by priority rules. opIndices.Clear(); for (int j = 0; j < currentTokens.Count; j++) { var token = currentTokens[j]; if (token.Type != TokenType.Operator) { continue; } var opToken = (ValueToken)token; var opDef = options.GetOperatorDefinition(opToken.Value); if (opDef == null) { return(ResultCode.UnknownSymbol); } opIndices.Add((index: j, opToken, opDef)); } opIndices.Sort((x, y) => { int xPriority = x.definition?.Precedence ?? 0; int yPriority = y.definition?.Precedence ?? 0; // Sort types in descending order. int priorityCompare = yPriority.CompareTo(xPriority); if (priorityCompare != 0) { return(priorityCompare); } // Sort indices of same type in ascending order. return(x.index.CompareTo(y.index)); }); // Merge token triplets with a center operator or // pairs with a leading operator. opShifts.Clear(); for (int i = 0; i < opIndices.Count; i++) { var(opIndex, opToken, opDef) = opIndices[i]; // Offset "opIndex" by shifts caused by previous operator merges. for (int j = 0; j < opShifts.Count; j++) { var(shiftIndex, shift) = opShifts[j]; if (shiftIndex < opIndex) { opIndex += shift; } } Token?leftToken = null; Token?rightToken = null; int left = opIndex - 1; if (opDef?.Associativity != OperatorSidedness.Right) { if (left < 0) { if (opDef != null && opDef.Associativity.HasFlag(OperatorSidedness.Left)) { return(ResultCode.OperatorMissingLeftValue); } } else { leftToken = currentTokens[left]; } } if (leftToken?.Type == TokenType.Operator) { continue; } int right = opIndex + 1; if (opDef != null && opDef.Associativity != OperatorSidedness.Left) { if (right >= currentTokens.Count) { if (opDef.Associativity.HasFlag(OperatorSidedness.Right)) { return(ResultCode.OperatorMissingRightValue); } } else { rightToken = currentTokens[right]; if (rightToken.Type == TokenType.Operator) { continue; } // Mitigates operators with following operators. if (rightToken.Type == TokenType.Operator) { int secondRight = opIndex + 2; if (secondRight < currentTokens.Count) { var subToken = new ListToken(new List <Token>(2) { rightToken, currentTokens[secondRight] }); rightToken = subToken; currentTokens[right] = rightToken; currentTokens.RemoveAt(secondRight); opShifts.Add((right, -1)); opIndices.RemoveAll(x => x.index == right); } else { return(ResultCode.OperatorMissingRightValue); } } } } int sideTokenCount = 0; if (leftToken != null) { sideTokenCount++; } if (rightToken != null) { sideTokenCount++; } // Try to skip making a 1-item list. int resultCount = 1 + sideTokenCount; if (resultCount == currentTokens.Count) { continue; } var resultList = new List <Token>(resultCount); if (leftToken != null) { resultList.Add(leftToken); } resultList.Add(opToken); if (rightToken != null) { resultList.Add(rightToken); } int firstIndex = opIndex - (leftToken != null ? 1 : 0); var resultToken = new ListToken(resultList); currentTokens[firstIndex] = resultToken; currentTokens.RemoveRange(firstIndex + 1, resultList.Count - 1); int nextShift = 1 - resultList.Count; opShifts.Add((opIndex, nextShift)); } } return(ResultCode.Ok); }
private static ResultCode MakeFunctions(ExpressionOptions options, List <Token> tokens) { var stack = new Stack <List <Token> >(); stack.Push(tokens); var argumentAccumulator = new List <Token>(); var argumentListAccumulator = new List <Token>(); while (stack.Count > 0) { bool hasList = false; var list = stack.Pop(); for (int i = list.Count; i-- > 0;) { var token = list[i]; if (hasList) { hasList = false; if (token.Type == TokenType.Operator || token.Type == TokenType.DecimalDigit || token.Type == TokenType.DecimalNumber || token.Type == TokenType.List) { continue; } if (token.Type != TokenType.Name) { return(ResultCode.InvalidTokenBeforeList); } void FlushArgumentAccumulator() { if (argumentAccumulator.Count == 0) { return; } argumentListAccumulator.Add(new ListToken(argumentAccumulator)); argumentAccumulator = new List <Token>(); } var arguments = (ListToken)list[i + 1]; foreach (var argument in arguments) { if (argument.Type == TokenType.ListSeparator) { FlushArgumentAccumulator(); argumentListAccumulator.Add(argument); } else { argumentAccumulator.Add(argument); } } FlushArgumentAccumulator(); var name = (ValueToken)token; var func = new FunctionToken(name, argumentListAccumulator); argumentListAccumulator = new List <Token>(); list[i] = func; list.RemoveAt(i + 1); } if (token is ListToken listToken) { hasList = true; stack.Push(listToken.Children); } } } return(ResultCode.Ok); }
public ExpressionTree(ExpressionOptions options, List <Token> tokens) { Options = options ?? throw new ArgumentNullException(nameof(options)); Tokens = tokens ?? throw new ArgumentNullException(nameof(tokens)); MetaList = new ListToken(Tokens); }
public ExpressionTree(ExpressionOptions options) : this(options, new List <Token>()) { }