Exemplo n.º 1
0
        public List <Step> ParseTokens() // Due to the size of this method I am not using IEnumerable 'yield return' as it is hard to track nested return statements.
        {
            List <Step> EvaluationSteps = new List <Step>();

            while (tokQueue.More())                  // While more tokens in queue (returns bool)
            {
                Token nextTok = tokQueue.MoveNext(); // pop next out of TokenQueue

                if (nextTok.Type().Equals("identifier"))
                // All statements in our language begin with identifiers.
                // We do not know what we have at this point, so let's check the identifier to see which tokens should follow after.
                {
                    if (Syntax.IsType(nextTok.Value()))
                    // If it is a var type, e.g "int", "string" - if it is, this is a variable declaration ("int x = 0;")
                    {
                        /*
                         * EXPECTED PATTERN: varType varName = expr;
                         * e.g int x = 2 + y*10;
                         * e.g string testing = "Hello World!";
                         */

                        VarDeclare varDeclare = CaptureVarDeclare(nextTok.Value()); // Call method with argument storing the type of var being declared, e.g 'string'

                        EvaluationSteps.Add(varDeclare);                            // Add Event object to the overall list of 'Steps' for the Evaluator module
                    }
                    else if (nextTok.Value().ToLower().Equals("if"))
                    // Start of an if statement
                    {
                        /*
                         * EXPECTED PATTERN: if(operands) { codeblock }
                         * e.g if (x > 0) {
                         *      output(x);
                         *      x = 0;
                         *     }
                         */

                        IfStatement ifState = CaptureIfStatement(); // Capture all useful information of the following if statements

                        // We COULD have an else statement, so let's check the next token
                        // First check there are still MORE tokens to check to avoid out of range errors
                        // Then check it's an IDENTIFIER ('else')
                        if (tokQueue.More() && tokQueue.Next().Type().Equals("identifier") && tokQueue.Next().Value().Equals("else"))
                        {
                            // If next token is 'else' and an identifier
                            ElseStatement elseState = CaptureElseStatement();
                            EvaluationSteps.Add(ifState);
                            EvaluationSteps.Add(elseState);
                            // Add if state then else directly after (ordered list!)
                        }
                        else
                        {
                            EvaluationSteps.Add(ifState);  // if no 'else' statement exists just add the if statement
                        }
                    }

                    else if (nextTok.Value().ToLower().Equals("while"))
                    {
                        IfStatement template  = CaptureIfStatement(); // Trick the program to think it's capturing an if statement
                        WhileLoop   whileLoop = new WhileLoop(template.GetCBContents(), template.GetOp1(), template.GetOp2(), template.GetComparator());
                        // Reuse code from the if statement because while & if follow the exact same structure:
                        // while (condition) { codeblock }
                        // if (condition) { codeblock }
                        // We just captured an if statement 'template' then used the information it collected to create a while loop instead

                        EvaluationSteps.Add(whileLoop);
                    }


                    else if (GrammarTokenCheck(tokQueue.Next(), "("))
                    // This condition will also return true if it finds an if/while statement, so it is AFTER the check for those.
                    // As we're using else if, if the program didn't recognise a 'while' or 'if' statement, we will reach this check
                    // We can GUARANTEE now that this must be a function call as 'if(){}' and 'while(){}' have been ruled out
                    {
                        /*
                         * EXPECTED PATTERN: funcName(expr); // Can take any expression!
                         * e.g output("Testing");
                         * e.g output(1 + 23);
                         * e.g output(x);
                         */

                        tokQueue.MoveNext(); // Skip the '(' token
                        // Remember, nextTok still holds the value of the token before '('
                        // This is the name of our function ('funcName')

                        FuncCall funcCall = CaptureFunctionCall(nextTok.Value()); // Pass the function name, e.g 'output'
                        EvaluationSteps.Add(funcCall);
                    }
                    else if (GrammarTokenCheck(tokQueue.Next(), "=")) // .Next() is PEEK not POP.
                    // Check if the token AFTER this one is "="
                    {
                        /*
                         * EXPECTED PATTERN: varName = expr;
                         * e.g x = 2 + y*10;
                         * e.g testing = "Hello World!";
                         */

                        tokQueue.MoveNext(); // Skip the '=' token
                        // Remember, nextTok still holds the value of the token before the '='
                        // This is the name of our variable to change ('varName')

                        VarChange varChan = CaptureVarChange(nextTok.Value());
                        EvaluationSteps.Add(varChan);
                    }
                    else
                    {
                        throw new SyntaxError();
                    }
                    // If there is a rogue 'else' statement it will be caught in this
                    // Else statements are not 'looked' for on there own, they are only recognised when an if statement is found
                }
                else
                {
                    throw new SyntaxError();    // Statement doesn't begin with identifier - throw error.
                }
            }

            return(EvaluationSteps);
        }
Exemplo n.º 2
0
        public void Evaluate(List <Step> evaluationSteps)
        {
            for (int index = 0; index < evaluationSteps.Count; index++)
            {
                Step evalStep = evaluationSteps[index];
                // .Type() can only be "VAR_DECLARE", "VAR_CHANGE", "FUNC_CALL", "IF_STATEMENT", "WHILE_LOOP"
                // It could also be "ELSE_STATEMENT", but we should only check for that DIRECTLY after an IF_STATEMENT
                if (evalStep.Type().Equals("IF_STATEMENT"))
                {
                    // Evaluate if statement - contains OPERAND1, OPERAND2, COMPARISON, codeBlockContents
                    IfStatement ifState         = (IfStatement)evalStep;                                                                           // Cast as we know it is now an IfStatement obj
                    bool        conditionResult = CompareExpressions(ifState.GetOp1(), ifState.GetOp2(), ifState.GetComparator());
                    bool        hasElse         = index + 1 < evaluationSteps.Count && evaluationSteps[index + 1].Type().Equals("ELSE_STATEMENT"); // No chance of index out of range error as set to False before reaching it

                    if (conditionResult)
                    // If the 'IfStatement' condition is TRUE
                    {
                        Evaluate(ifState.GetCBContents()); // 'run' the contents of the if statement - this is RECURSIVE
                        if (hasElse)
                        {
                            evaluationSteps.RemoveAt(index + 1);
                        }
                        // If we have an ELSE_STATEMENT after this, we need to remove it as the IF_STATEMENT has triggered (therefore the ELSE will not be triggered).
                    }
                    else if (hasElse)
                    {
                        // If the CONDITION is FALSE and the next Step obj is an ELSE_STATEMENT type

                        ElseStatement elseState = (ElseStatement)evaluationSteps[index + 1];
                        // Cast to else
                        Evaluate(elseState.GetCBContents()); // 'run' the contents of the else (RECURSION)

                        evaluationSteps.RemoveAt(index + 1); // Remove ELSE_STATEMENT as we have used it and do not want to go over it again.
                    }
                }
                else if (evalStep.Type().Equals("WHILE_LOOP"))
                {
                    WhileLoop whileLoop = (WhileLoop)evalStep;
                    // Similar to if statement evaluation though no need to set a 'condition' variable because that condition may change
                    // Basically just reusing the C# while loop with the template of the Interpreted one
                    while (CompareExpressions(whileLoop.GetOp1(), whileLoop.GetOp2(), whileLoop.GetComparator()))
                    {
                        // While the condition is true, evaluate code inside
                        Evaluate(whileLoop.GetCBContents());
                    }
                }
                else if (evalStep.Type().Equals("VAR_DECLARE"))
                // Declare a variable in the variableScope
                {
                    VarDeclare varDecl = (VarDeclare)evalStep; // Cast as we know it's a VarDeclare obj
                    if (variableScope.ContainsKey(varDecl.GetName()))
                    {
                        throw new DeclareError();
                    }
                    // If scope already has a variable that name, you cannot redeclare it as it already exists.
                    // Potential endpoint if variable exists - entire program will stop (crash).
                    Token varExpr = ResolveExpression(varDecl.Value());

                    if (!varExpr.Type().Equals(varDecl.GetVarType()))
                    {
                        throw new TypeError();
                    }
                    // Value of variable does not match type with declared one. e.g 'int x = "Hello";'


                    variableScope.Add(varDecl.GetName(), varExpr);
                    // Type of variable can be found out by the .Type() of the key's Token.
                    // e.g 'int x = 1 + 2;'
                    // if we want to find variable 'x' type, we find variableScope[x].Type() which will return 'number', with variableScope[x].Value() being '3'
                }
                else if (evalStep.Type().Equals("VAR_CHANGE"))
                // Change a pre-existing variable
                {
                    VarChange varChan = (VarChange)evalStep; // Cast as we know it is a VarChange obj

                    if (!variableScope.ContainsKey(varChan.GetName()))
                    {
                        throw new ReferenceError();
                    }
                    // If variable is NOT in the variableScope then we cannot change it as it doesn't exist.
                    // Potential endpoint for program crash
                    string varType  = variableScope[varChan.GetName()].Type();
                    Token  newValue = ResolveExpression(varChan.Value());

                    if (!varType.Equals(newValue.Type()))
                    {
                        throw new TypeError();
                    }
                    // If the new value of the variable is not the right type, then crash.
                    // Potential endpoint
                    // e.g int x = 0; x = "hi"; will cause this error
                    variableScope[varChan.GetName()] = newValue; // Assign new value (Token)
                }
                else if (evalStep.Type().Equals("FUNC_CALL"))
                // Call a function
                {
                    FuncCall functionCall = (FuncCall)evalStep;                                                   // Cast as we know it is a FuncCall obj now
                    if (!functionCall.GetName().Equals("inputstr") && !functionCall.GetName().Equals("inputint")) // If NOT calling 'input' function
                    {
                        CallFunction(functionCall.GetName(), ResolveExpression(functionCall.GetArguments()));
                        // Call function with name and *resolved* list of arguments
                        // Resolve function always outputs a single token which is the result of an expression (list of tokens) being evaluated
                    }
                    else
                    // SPECIAL CASE: Calling inputStr or inputInt functions indicates that the 'argument' is NOT an expression to be resolved, but rather a variable name to store input value in.
                    // This means functionCall.Argumnets() will only have 1 token:
                    {
                        CallFunction(functionCall.GetName(), functionCall.GetArguments()[0]); // Pass in first value in Arguments as there only should be one - the variable to be input to
                    }
                }
                else
                {
                    throw new SyntaxError();  // Unrecognised Step, crash program.
                }
            }
        }