using System; using System.Collections.Generic; using System.Linq; using System.Text; using Jace.Execution; using Jace.Operations; using Jace.Tokenizer; using Jace.Util; namespace Jace { public class AstBuilder { private readonly IFunctionRegistry functionRegistry; private readonly IConstantRegistry localConstantRegistry; private readonly bool caseSensitive; private Dictionary operationPrecedence = new Dictionary(); private Stack resultStack = new Stack(); private Stack operatorStack = new Stack(); private Stack parameterCount = new Stack(); public AstBuilder(IFunctionRegistry functionRegistry, bool caseSensitive, IConstantRegistry compiledConstants = null) { if (functionRegistry == null) throw new ArgumentNullException("functionRegistry"); this.functionRegistry = functionRegistry; this.localConstantRegistry = compiledConstants ?? new ConstantRegistry(caseSensitive); this.caseSensitive = caseSensitive; operationPrecedence.Add('(', 0); operationPrecedence.Add('&', 1); operationPrecedence.Add('|', 1); operationPrecedence.Add('<', 2); operationPrecedence.Add('>', 2); operationPrecedence.Add('≤', 2); operationPrecedence.Add('≥', 2); operationPrecedence.Add('≠', 2); operationPrecedence.Add('=', 2); operationPrecedence.Add('+', 3); operationPrecedence.Add('-', 3); operationPrecedence.Add('*', 4); operationPrecedence.Add('/', 4); operationPrecedence.Add('%', 4); operationPrecedence.Add('_', 6); operationPrecedence.Add('^', 5); } public Operation Build(IList tokens) { resultStack.Clear(); operatorStack.Clear(); parameterCount.Clear(); foreach (Token token in tokens) { object value = token.Value; switch (token.TokenType) { case TokenType.Integer: resultStack.Push(new IntegerConstant((int)token.Value)); break; case TokenType.FloatingPoint: resultStack.Push(new FloatingPointConstant((double)token.Value)); break; case TokenType.Text: if (functionRegistry.IsFunctionName((string)token.Value)) { operatorStack.Push(token); parameterCount.Push(1); } else { string tokenValue = (string)token.Value; if (localConstantRegistry.IsConstantName(tokenValue)) { resultStack.Push(new FloatingPointConstant(localConstantRegistry.GetConstantInfo(tokenValue).Value)); } else { if (!caseSensitive) { tokenValue = tokenValue.ToLowerFast(); } resultStack.Push(new Variable(tokenValue)); } } break; case TokenType.LeftBracket: operatorStack.Push(token); break; case TokenType.RightBracket: PopOperations(true, token); //parameterCount.Pop(); break; case TokenType.ArgumentSeparator: PopOperations(false, token); parameterCount.Push(parameterCount.Pop() + 1); break; case TokenType.Operation: Token operation1Token = token; char operation1 = (char)operation1Token.Value; while (operatorStack.Count > 0 && (operatorStack.Peek().TokenType == TokenType.Operation || operatorStack.Peek().TokenType == TokenType.Text)) { Token operation2Token = operatorStack.Peek(); bool isFunctionOnTopOfStack = operation2Token.TokenType == TokenType.Text; if (!isFunctionOnTopOfStack) { char operation2 = (char)operation2Token.Value; if ((IsLeftAssociativeOperation(operation1) && operationPrecedence[operation1] <= operationPrecedence[operation2]) || (operationPrecedence[operation1] < operationPrecedence[operation2])) { operatorStack.Pop(); resultStack.Push(ConvertOperation(operation2Token)); } else { break; } } else { operatorStack.Pop(); resultStack.Push(ConvertFunction(operation2Token)); } } operatorStack.Push(operation1Token); break; } } PopOperations(false, null); VerifyResultStack(); return resultStack.First(); } private void PopOperations(bool untillLeftBracket, Token? currentToken) { if (untillLeftBracket && !currentToken.HasValue) throw new ArgumentNullException("currentToken", "If the parameter \"untillLeftBracket\" is set to true, " + "the parameter \"currentToken\" cannot be null."); while (operatorStack.Count > 0 && operatorStack.Peek().TokenType != TokenType.LeftBracket) { Token token = operatorStack.Pop(); switch (token.TokenType) { case TokenType.Operation: resultStack.Push(ConvertOperation(token)); break; case TokenType.Text: resultStack.Push(ConvertFunction(token)); break; } } if (untillLeftBracket) { if (operatorStack.Count > 0 && operatorStack.Peek().TokenType == TokenType.LeftBracket) operatorStack.Pop(); else throw new ParseException(string.Format("No matching left bracket found for the right " + "bracket at position {0}.", currentToken.Value.StartPosition)); } else { if (operatorStack.Count > 0 && operatorStack.Peek().TokenType == TokenType.LeftBracket && !(currentToken.HasValue && currentToken.Value.TokenType == TokenType.ArgumentSeparator)) throw new ParseException(string.Format("No matching right bracket found for the left " + "bracket at position {0}.", operatorStack.Peek().StartPosition)); } } private Operation ConvertOperation(Token operationToken) { try { DataType dataType; Operation argument1; Operation argument2; Operation divisor; Operation divident; switch ((char)operationToken.Value) { case '+': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new Addition(dataType, argument1, argument2); case '-': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new Subtraction(dataType, argument1, argument2); case '*': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new Multiplication(dataType, argument1, argument2); case '/': divisor = resultStack.Pop(); divident = resultStack.Pop(); return new Division(DataType.FloatingPoint, divident, divisor); case '%': divisor = resultStack.Pop(); divident = resultStack.Pop(); return new Modulo(DataType.FloatingPoint, divident, divisor); case '_': argument1 = resultStack.Pop(); return new UnaryMinus(argument1.DataType, argument1); case '^': Operation exponent = resultStack.Pop(); Operation @base = resultStack.Pop(); return new Exponentiation(DataType.FloatingPoint, @base, exponent); case '&': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new And(dataType, argument1, argument2); case '|': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new Or(dataType, argument1, argument2); case '<': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new LessThan(dataType, argument1, argument2); case '≤': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new LessOrEqualThan(dataType, argument1, argument2); case '>': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new GreaterThan(dataType, argument1, argument2); case '≥': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new GreaterOrEqualThan(dataType, argument1, argument2); case '=': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new Equal(dataType, argument1, argument2); case '≠': argument2 = resultStack.Pop(); argument1 = resultStack.Pop(); dataType = RequiredDataType(argument1, argument2); return new NotEqual(dataType, argument1, argument2); default: throw new ArgumentException(string.Format("Unknown operation \"{0}\".", operationToken), "operation"); } } catch (InvalidOperationException) { // If we encounter a Stack empty issue this means there is a syntax issue in // the mathematical formula throw new ParseException(string.Format("There is a syntax issue for the operation \"{0}\" at position {1}. " + "The number of arguments does not match with what is expected.", operationToken.Value, operationToken.StartPosition)); } } private Operation ConvertFunction(Token functionToken) { try { string functionName = ((string)functionToken.Value).ToLowerInvariant(); if (functionRegistry.IsFunctionName(functionName)) { FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(functionName); int numberOfParameters; if (functionInfo.IsDynamicFunc) { numberOfParameters = parameterCount.Pop(); } else { parameterCount.Pop(); numberOfParameters = functionInfo.NumberOfParameters; } List operations = new List(); for (int i = 0; i < numberOfParameters; i++) operations.Add(resultStack.Pop()); operations.Reverse(); return new Function(DataType.FloatingPoint, functionName, operations, functionInfo.IsIdempotent); } else { throw new ArgumentException(string.Format("Unknown function \"{0}\".", functionToken.Value), "function"); } } catch (InvalidOperationException) { // If we encounter a Stack empty issue this means there is a syntax issue in // the mathematical formula throw new ParseException(string.Format("There is a syntax issue for the function \"{0}\" at position {1}. " + "The number of arguments does not match with what is expected.", functionToken.Value, functionToken.StartPosition)); } } private void VerifyResultStack() { if(resultStack.Count > 1) { Operation[] operations = resultStack.ToArray(); for (int i = 1; i < operations.Length; i++) { Operation operation = operations[i]; if (operation.GetType() == typeof(IntegerConstant)) { IntegerConstant constant = (IntegerConstant)operation; throw new ParseException(string.Format("Unexpected integer constant \"{0}\" found.", constant.Value)); } else if (operation.GetType() == typeof(FloatingPointConstant)) { FloatingPointConstant constant = (FloatingPointConstant)operation; throw new ParseException(string.Format("Unexpected floating point constant \"{0}\" found.", constant.Value)); } } throw new ParseException("The syntax of the provided formula is not valid."); } } private bool IsLeftAssociativeOperation(char character) { return character == '*' || character == '+' || character == '-' || character == '/'; } private DataType RequiredDataType(Operation argument1, Operation argument2) { return (argument1.DataType == DataType.FloatingPoint || argument2.DataType == DataType.FloatingPoint) ? DataType.FloatingPoint : DataType.Integer; } } }