// For now, the scripts execute directly from the // abstract syntax tree. This is relatively simple. // A more robust approach would be to "compile" the // scripts to a bytecode. That would allow more errors // to be caught with better error messages, as well as // make the scripts execute more quickly. public Script(ASTNode root) { // Set current to the first statement _statement = root.FirstChild(); // Create a default scope _scope = new Scope(null, _statement); }
static void PrintNode(ASTNode node, int indent) { while (node != null) { if (node.Lexeme.Length > 0) { Console.WriteLine("{0}{1} {2}", "".PadLeft(indent * 4), node.Type, node.Lexeme); } else { Console.WriteLine("{0}{1}", "".PadLeft(indent * 4), node.Type); } var child = node.FirstChild(); if (child != null) { PrintNode(child, indent + 1); } node = node.Next(); } }
private bool EvaluateExpression(ref ASTNode expr) { if (expr == null || (expr.Type != ASTNodeType.UNARY_EXPRESSION && expr.Type != ASTNodeType.BINARY_EXPRESSION && expr.Type != ASTNodeType.LOGICAL_EXPRESSION)) { throw new RunTimeError(expr, "No expression following control statement"); } var node = expr.FirstChild(); if (node == null) { throw new RunTimeError(expr, "Empty expression following control statement"); } switch (expr.Type) { case ASTNodeType.UNARY_EXPRESSION: return(EvaluateUnaryExpression(ref node)); case ASTNodeType.BINARY_EXPRESSION: return(EvaluateBinaryExpression(ref node)); } bool lhs = EvaluateExpression(ref node); node = node.Next(); while (node != null) { // Capture the operator var op = node.Type; node = node.Next(); if (node == null) { throw new RunTimeError(node, "Invalid logical expression"); } bool rhs; var e = node.FirstChild(); switch (node.Type) { case ASTNodeType.UNARY_EXPRESSION: rhs = EvaluateUnaryExpression(ref e); break; case ASTNodeType.BINARY_EXPRESSION: rhs = EvaluateBinaryExpression(ref e); break; default: throw new RunTimeError(node, "Nested logical expressions are not possible"); } switch (op) { case ASTNodeType.AND: lhs = lhs && rhs; break; case ASTNodeType.OR: lhs = lhs || rhs; break; default: throw new RunTimeError(node, "Invalid logical operator"); } node = node.Next(); } return(lhs); }
public bool ExecuteNext() { if (_statement == null) { return(false); } if (_statement.Type != ASTNodeType.STATEMENT) { throw new RunTimeError(_statement, "Invalid script"); } var node = _statement.FirstChild(); if (node == null) { throw new RunTimeError(_statement, "Invalid statement"); } int depth = 0; switch (node.Type) { case ASTNodeType.IF: { PushScope(node); var expr = node.FirstChild(); var result = EvaluateExpression(ref expr); // Advance to next statement _statement = _statement.Next(); // Evaluated true. Jump right into execution. if (result) { break; } // The expression evaluated false, so keep advancing until // we hit an elseif, else, or endif statement that matches // and try again. depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.IF) { depth++; } else if (node.Type == ASTNodeType.ELSEIF) { if (depth == 0) { expr = node.FirstChild(); result = EvaluateExpression(ref expr); // Evaluated true. Jump right into execution if (result) { _statement = _statement.Next(); break; } } } else if (node.Type == ASTNodeType.ELSE) { if (depth == 0) { // Jump into the else clause _statement = _statement.Next(); break; } } else if (node.Type == ASTNodeType.ENDIF) { if (depth == 0) { break; } depth--; } _statement = _statement.Next(); } if (_statement == null) { throw new RunTimeError(node, "If with no matching endif"); } break; } case ASTNodeType.ELSEIF: // If we hit the elseif statement during normal advancing, skip over it. The only way // to execute an elseif clause is to jump directly in from an if statement. depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.IF) { depth++; } else if (node.Type == ASTNodeType.ENDIF) { if (depth == 0) { break; } depth--; } _statement = _statement.Next(); } if (_statement == null) { throw new RunTimeError(node, "If with no matching endif"); } break; case ASTNodeType.ENDIF: PopScope(); _statement = _statement.Next(); break; case ASTNodeType.ELSE: // If we hit the else statement during normal advancing, skip over it. The only way // to execute an else clause is to jump directly in from an if statement. depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.IF) { depth++; } else if (node.Type == ASTNodeType.ENDIF) { if (depth == 0) { break; } depth--; } _statement = _statement.Next(); } if (_statement == null) { throw new RunTimeError(node, "If with no matching endif"); } break; case ASTNodeType.WHILE: { // When we first enter the loop, push a new scope if (_scope.StartNode != node) { PushScope(node); } var expr = node.FirstChild(); var result = EvaluateExpression(ref expr); // Advance to next statement _statement = _statement.Next(); // The expression evaluated false, so keep advancing until // we hit an endwhile statement. if (!result) { depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.WHILE) { depth++; } else if (node.Type == ASTNodeType.ENDWHILE) { if (depth == 0) { PopScope(); // Go one past the endwhile so the loop doesn't repeat _statement = _statement.Next(); break; } depth--; } _statement = _statement.Next(); } } break; } case ASTNodeType.ENDWHILE: // Walk backward to the while statement _statement = _statement.Prev(); depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.ENDWHILE) { depth++; } else if (node.Type == ASTNodeType.WHILE) { if (depth == 0) { break; } depth--; } _statement = _statement.Prev(); } if (_statement == null) { throw new RunTimeError(node, "Unexpected endwhile"); } break; case ASTNodeType.FOR: { // The iterator variable's name is the hash code of the for loop's ASTNode. var iterName = node.GetHashCode().ToString(); // When we first enter the loop, push a new scope if (_scope.StartNode != node) { PushScope(node); // Grab the arguments var max = node.FirstChild(); if (max.Type != ASTNodeType.INTEGER) { throw new RunTimeError(max, "Invalid for loop syntax"); } // Create a dummy argument that acts as our loop variable var iter = new ASTNode(ASTNodeType.INTEGER, "0", node, 0); _scope.SetVar(iterName, new Argument(this, iter)); } else { // Increment the iterator argument var arg = _scope.GetVar(iterName); var iter = new ASTNode(ASTNodeType.INTEGER, (arg.AsUInt() + 1).ToString(), node, 0); _scope.SetVar(iterName, new Argument(this, iter)); } // Check loop condition var i = _scope.GetVar(iterName); // Grab the max value to iterate to node = node.FirstChild(); var end = new Argument(this, node); if (i.AsUInt() < end.AsUInt()) { // enter the loop _statement = _statement.Next(); } else { // Walk until the end of the loop _statement = _statement.Next(); depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.FOR || node.Type == ASTNodeType.FOREACH) { depth++; } else if (node.Type == ASTNodeType.ENDFOR) { if (depth == 0) { PopScope(); // Go one past the end so the loop doesn't repeat _statement = _statement.Next(); break; } depth--; } _statement = _statement.Next(); } } } break; case ASTNodeType.FOREACH: { // foreach VAR in LIST // The iterator's name is the hash code of the for loop's ASTNode. var varName = node.FirstChild().Lexeme; var listName = node.FirstChild().Next().Lexeme; var iterName = node.GetHashCode().ToString(); // When we first enter the loop, push a new scope if (_scope.StartNode != node) { PushScope(node); // Create a dummy argument that acts as our iterator object var iter = new ASTNode(ASTNodeType.INTEGER, "0", node, 0); _scope.SetVar(iterName, new Argument(this, iter)); // Make the user-chosen variable have the value for the front of the list var arg = Interpreter.GetListValue(listName, 0); if (arg != null) { _scope.SetVar(varName, arg); } else { _scope.ClearVar(varName); } } else { // Increment the iterator argument var idx = _scope.GetVar(iterName).AsInt() + 1; var iter = new ASTNode(ASTNodeType.INTEGER, idx.ToString(), node, 0); _scope.SetVar(iterName, new Argument(this, iter)); // Update the user-chosen variable var arg = Interpreter.GetListValue(listName, idx); if (arg != null) { _scope.SetVar(varName, arg); } else { _scope.ClearVar(varName); } } // Check loop condition var i = _scope.GetVar(varName); if (i != null) { // enter the loop _statement = _statement.Next(); } else { // Walk until the end of the loop _statement = _statement.Next(); depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.FOR || node.Type == ASTNodeType.FOREACH) { depth++; } else if (node.Type == ASTNodeType.ENDFOR) { if (depth == 0) { PopScope(); // Go one past the end so the loop doesn't repeat _statement = _statement.Next(); break; } depth--; } _statement = _statement.Next(); } } break; } case ASTNodeType.ENDFOR: // Walk backward to the for statement _statement = _statement.Prev(); while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.FOR || node.Type == ASTNodeType.FOREACH) { break; } _statement = _statement.Prev(); } if (_statement == null) { throw new RunTimeError(node, "Unexpected endfor"); } break; case ASTNodeType.BREAK: // Walk until the end of the loop _statement = _statement.Next(); depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.WHILE || node.Type == ASTNodeType.FOR || node.Type == ASTNodeType.FOREACH) { depth++; } else if (node.Type == ASTNodeType.ENDWHILE || node.Type == ASTNodeType.ENDFOR) { if (depth == 0) { PopScope(); // Go one past the end so the loop doesn't repeat _statement = _statement.Next(); break; } depth--; } _statement = _statement.Next(); } PopScope(); break; case ASTNodeType.CONTINUE: // Walk backward to the loop statement _statement = _statement.Prev(); depth = 0; while (_statement != null) { node = _statement.FirstChild(); if (node.Type == ASTNodeType.ENDWHILE || node.Type == ASTNodeType.ENDFOR) { depth++; } else if (node.Type == ASTNodeType.WHILE || node.Type == ASTNodeType.FOR || node.Type == ASTNodeType.FOREACH) { if (depth == 0) { break; } depth--; } _statement = _statement.Prev(); } if (_statement == null) { throw new RunTimeError(node, "Unexpected continue"); } break; case ASTNodeType.STOP: _statement = null; break; case ASTNodeType.REPLAY: _statement = _statement.Parent.FirstChild(); break; case ASTNodeType.QUIET: case ASTNodeType.FORCE: case ASTNodeType.COMMAND: if (ExecuteCommand(node)) { _statement = _statement.Next(); } break; } return((_statement != null) ? true : false); }