Exemplo n.º 1
0
        public static VerifiedExpression Parse(string expr, CalcEnvironment env)
        {
            if (env == null)
            {
                throw new ArgumentNullException(nameof(env));
            }

            var tokenExpr = TokenExpression.Parse(expr);

            if (tokenExpr.Success)
            {
                return(Verifier.VerifyExpression(tokenExpr.BackingArray, env));
            }
            else
            {
                return(new VerifiedExpression(tokenExpr.ErrorMessage, tokenExpr.ErrorPosition));
            }
        }
Exemplo n.º 2
0
        public static VerifiedExpression Create(TokenExpression tokenExpr, CalcEnvironment env)
        {
            if (tokenExpr == null)
            {
                throw new ArgumentNullException(nameof(tokenExpr));
            }
            if (!tokenExpr.Success)
            {
                throw new ArgumentException("Expression must be successfully parsed.", nameof(tokenExpr));
            }
            if (env == null)
            {
                throw new ArgumentNullException(nameof(env));
            }

            var arr = tokenExpr.Expression.ToArray();

            return(Verifier.VerifyExpression(arr, env));
        }
Exemplo n.º 3
0
        public static VerifiedExpression VerifyExpression(CalcToken[] tokens, CalcEnvironment env)
        {
            int currentDepth = 0;
            // Depth/subexpression -> Argument counter & function metadata.
            // A stack based solution might be better?
            var funcTracking = new Dictionary <int, FunctionMetadata>();

            for (int i = 0; i < tokens.Length; i++)
            {
                var token = tokens[i];
                // Used as `out` argument in processing types ArgSeperator and ParenClose.
                // The compiler complains if we define it in both cases, which are counted as the same scope.
                FunctionMetadata funcMeta;

                switch (token.Type)
                {
                // --- Verify symbol (variables and constants) ---

                case TokenType.Symbol:
                    double numVal;
                    if (env.VarOrConst(token.Value, out numVal))
                    {
                        // Resolve symbol to value.
                        var symbol = (CalcSymbolToken)token;
                        tokens[i] = symbol.New(numVal);
                        break;
                    }
                    else
                    {
                        return(new VerifiedExpression("Undefined symbol: " + token.Value,
                                                      token.OriginIndex));
                    }


                // --- Verify function symbol ---

                case TokenType.Function:
                    CalcFunction func;
                    if (env.Function(token.Value, out func))
                    {
                        // Resolve function symbol to function.
                        var funcToken = (CalcFuncToken)token;
                        tokens[i] = funcToken.New(func);

                        // Record that the next subexpression (delimited by parentheses) is associated
                        // with a function.
                        funcTracking[currentDepth + 1] = new FunctionMetadata(func, funcToken);
                        break;
                    }
                    else
                    {
                        return(new VerifiedExpression("Undefined function symbol: " + token.Value,
                                                      token.OriginIndex));
                    }


                // --- Keep count of arguments to functions ---

                case TokenType.ArgSeperator:
                    if (funcTracking.TryGetValue(currentDepth, out funcMeta))
                    {
                        // Another seperator, another function argument.
                        // The previous stage assures that there will be an operand.
                        funcMeta.ArgCounter++;
                    }
                    break;


                // --- Record opening and closing of parentheses ---

                case TokenType.ParenOpen:
                    currentDepth++;
                    break;

                case TokenType.ParenClose:
                    // If the subexpression we're closing/leaving is associated with a function we need to
                    // verify the function argument count.
                    if (funcTracking.TryGetValue(currentDepth, out funcMeta))
                    {
                        if (!funcMeta.CountsAreEqual)
                        {
                            // And this is why we kept around that metadata, so we could report the appropriate
                            // information on encountering an discrepancy in argument count.
                            return(new VerifiedExpression(
                                       string.Format("Invalid number of arguments for {0} (got {1} expected {2})",
                                                     funcMeta.FuncSymbol, funcMeta.ArgCounter, funcMeta.RequiredArgs),
                                       funcMeta.OriginIndex));
                        }

                        // We're leaving the current depth, we no longer need this information.
                        funcTracking.Remove(currentDepth);
                    }
                    currentDepth--;
                    break;


                // --- The rest is not important for this stage ---
                default:
                    continue;
                } // switch
            }     // for

            // We have successfully stepped through the expression without encountering undefined symbols or functions
            // and the functions were supplied the correct number of arguments. Hurray!
            return(new VerifiedExpression(tokens));
        }