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<char, int> operationPrecedence = new Dictionary<char, int>(); 
 | 
        private Stack<Operation> resultStack = new Stack<Operation>(); 
 | 
        private Stack<Token> operatorStack = new Stack<Token>(); 
 | 
        private Stack<int> parameterCount = new Stack<int>(); 
 | 
  
 | 
        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<Token> 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<Operation> operations = new List<Operation>(); 
 | 
                    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; 
 | 
        } 
 | 
    } 
 | 
} 
 |