private void MultilineComment() { // A /* comment goes until it hits the closing */ while (!IsAtEnd()) { if (Peek() == '\n') { line++; } // Check for comment end. if (Peek() == '*') { Advance(); if (Peek() == '/' && !IsAtEnd()) { Advance(); return; } } else { Advance(); } } if (IsAtEnd()) { CsLox.Error(line, "Unterminated comment."); } }
private void String() { while (Peek() != '"' && !IsAtEnd()) { if (Peek() == '\n') { line++; } Advance(); } // Unterminated string. if (IsAtEnd()) { CsLox.Error(line, "Unterminated string."); return; } // The closing ". Advance(); // Trim the surrounding quotes. string value = source.Substring(start + 1, current - (start + 2)); AddToken(TokenType.STRING, value); }
public object VisitBreakStmt(Break stmt) { if (currentLoop == LoopType.NONE) { CsLox.Error(stmt.Keyword, "Break statement must be inside a loop."); } return(null); }
public object VisitThisExpr(This expr) { if (currentClass == ClassType.NONE) { CsLox.Error(expr.Keyword, "Cannot use 'this' outside of a class."); } ResolveLocal(expr, expr.Keyword); return(null); }
public void Interpret(List <Stmt> statements) { try { foreach (Stmt statement in statements) { Execute(statement); } } catch (RuntimeException re) { CsLox.RuntimeError(re); } }
public object VisitVariableExpr(Variable expr) { if (scopes.Count > 0 && scopes.Peek().TryGetValue(expr.Name.Lexeme, out bool value)) { if (!value) { CsLox.Error(expr.Name, "Cannot read local variable in its own initializer."); } } ResolveLocal(expr, expr.Name); return(null); }
public object VisitSuperExpr(Super expr) { if (currentClass == ClassType.NONE) { CsLox.Error(expr.Keyword, "Cannot use 'super' outside of a class."); } else if (currentClass != ClassType.SUBCLASS) { CsLox.Error(expr.Keyword, "Cannot use 'super' in a class with no superclass."); } ResolveLocal(expr, expr.Keyword); return(null); }
private void Declare(Token name) { if (scopes.Count == 0) { return; } Dictionary <string, bool> scope = scopes.Peek(); if (scope.ContainsKey(name.Lexeme)) { CsLox.Error(name, "Variable with this name already declared in this scope."); } scope[name.Lexeme] = false; }
public object VisitClassStmt(Class stmt) { ClassType enclosingClass = currentClass; currentClass = ClassType.CLASS; Declare(stmt.Name); Define(stmt.Name); if (stmt.Superclass != null && stmt.Name.Lexeme.Equals(stmt.Superclass.Name.Lexeme)) { CsLox.Error(stmt.Superclass.Name, "A class cannot inherit from itself."); } if (stmt.Superclass != null) { currentClass = ClassType.SUBCLASS; Resolve(stmt.Superclass); BeginScope(); scopes.Peek()["super"] = true; } BeginScope(); scopes.Peek()["this"] = true; foreach (Function method in stmt.Methods) { FunctionType declaration = FunctionType.METHOD; if (method.Name.Lexeme.Equals("init")) { declaration = FunctionType.INITIALIZER; } ResolveFunction(method, declaration); } EndScope(); if (stmt.Superclass != null) { EndScope(); } currentClass = enclosingClass; return(null); }
public object VisitReturnStmt(Return stmt) { if (currentFunction == FunctionType.NONE) { CsLox.Error(stmt.Keyword, "Cannot return from top-level code."); } if (stmt.Value != null) { if (currentFunction == FunctionType.INITIALIZER) { CsLox.Error(stmt.Keyword, "Cannot return a value from an initializer."); } Resolve(stmt.Value); } return(null); }
private ParseException Error(Token token, string message) { CsLox.Error(token, message); return(new ParseException()); }
private void ScanToken() { char c = Advance(); switch (c) { case '(': AddToken(TokenType.LEFT_PAREN); break; case ')': AddToken(TokenType.RIGHT_PAREN); break; case '{': AddToken(TokenType.LEFT_BRACE); break; case '}': AddToken(TokenType.RIGHT_BRACE); break; case ',': AddToken(TokenType.COMMA); break; case '.': AddToken(TokenType.DOT); break; case '-': AddToken(TokenType.MINUS); break; case '+': AddToken(TokenType.PLUS); break; case ';': AddToken(TokenType.SEMICOLON); break; case '*': AddToken(TokenType.STAR); break; case '?': AddToken(TokenType.QUERY); break; case ':': AddToken(TokenType.COLON); break; case '!': AddToken(Match('=') ? TokenType.BANG_EQUAL : TokenType.BANG); break; case '=': AddToken(Match('=') ? TokenType.EQUAL_EQUAL : TokenType.EQUAL); break; case '<': AddToken(Match('=') ? TokenType.LESS_EQUAL : TokenType.LESS); break; case '>': AddToken(Match('=') ? TokenType.GREATER_EQUAL : TokenType.GREATER); break; case '/': if (Match('/')) { // A comment goes until the end of the line. while (Peek() != '\n' && !IsAtEnd()) { Advance(); } } else if (Match('*')) { MultilineComment(); } else { AddToken(TokenType.SLASH); } break; case '%': AddToken(TokenType.PERCENT); break; case ' ': case '\r': case '\t': // Ignore whitespace. break; case '\n': line++; break; case '"': String(); break; default: if (IsDigit(c)) { Number(); } else if (IsAlpha(c)) { Identifier(); } else { CsLox.Error(line, "Unexpected character."); } break; } }