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. } } }