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 Advance(); // 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) { Advance(); break; } } } else if (node.Type == ASTNodeType.ELSE) { if (depth == 0) { // Jump into the else clause Advance(); break; } } else if (node.Type == ASTNodeType.ENDIF) { if (depth == 0) { break; } depth--; } Advance(); } 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--; } Advance(); } if (_statement == null) { throw new RunTimeError(node, "If with no matching endif"); } break; case ASTNodeType.ENDIF: PopScope(); Advance(); 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--; } Advance(); } 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 Advance(); // 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 Advance(); break; } depth--; } Advance(); } } 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 Advance(); } else { // Walk until the end of the loop Advance(); 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 Advance(); break; } depth--; } Advance(); } } } 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 Advance(); } else { // Walk until the end of the loop Advance(); 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 Advance(); break; } depth--; } Advance(); } } 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 Advance(); 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 Advance(); break; } depth--; } Advance(); } 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)) { Advance(); } break; } return((_statement != null) ? true : false); }
public void Advance() { Interpreter.ClearTimeout(); _statement = _statement.Next(); }
private void PushScope(ASTNode node) { _scope = new Scope(_scope, node); }
public Argument(Script script, ASTNode node) { _node = node; _script = script; }
public RunTimeError(ASTNode node, string error) : base(error) { Node = node; }
public Scope(Scope parent, ASTNode start) { Parent = parent; StartNode = start; }
private bool EvaluateBinaryExpression(ref ASTNode node) { // Evaluate the left hand side var lhs = EvaluateBinaryOperand(ref node); // Capture the operator var op = node.Type; node = node.Next(); // Evaluate the right hand side var rhs = EvaluateBinaryOperand(ref node); if (lhs.GetType() != rhs.GetType()) { // Different types. Try to convert one to match the other. // Special case for rhs doubles because we don't want to lose precision. if (rhs is double) { double tmp = (double)lhs; lhs = tmp; } else { var tmp = Convert.ChangeType(rhs, lhs.GetType()); rhs = (IComparable)tmp; } } try { // Evaluate the whole expression switch (op) { case ASTNodeType.EQUAL: return(lhs.CompareTo(rhs) == 0); case ASTNodeType.NOT_EQUAL: return(lhs.CompareTo(rhs) != 0); case ASTNodeType.LESS_THAN: return(lhs.CompareTo(rhs) < 0); case ASTNodeType.LESS_THAN_OR_EQUAL: return(lhs.CompareTo(rhs) <= 0); case ASTNodeType.GREATER_THAN: return(lhs.CompareTo(rhs) > 0); case ASTNodeType.GREATER_THAN_OR_EQUAL: return(lhs.CompareTo(rhs) >= 0); } } catch (ArgumentException e) { throw new RunTimeError(node, e.Message); } throw new RunTimeError(node, "Unknown operator in expression"); }