private void MultiLineComment() { // we start inside a multi-line comment. // but it could be nested while ((Peek() == '*' && PeekNext() == '/') == false && !isAtEnd()) { // Move through the body of the multi if (Peek() == '\n') { Line++; } // We are already in a comment so eating chars is safe // We start this method INSIDE the comment. if (Match('/') && Match('*')) { MultiLineComment(); } Advance(); } if (isAtEnd()) { CSLox.Error(this.Line, "Unterminated multi-line comment"); } Advance(); Advance(); }
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.QUESTION); break; case ':': AddToken(TokenType.SEMICOLON); 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 a line. while (Peek() != '\n' && !isAtEnd()) Advance(); } else if (Match('*')) { MultiLineComment(); } else { AddToken(TokenType.SLASH); } break; case ' ': case '\r': case '\t': break; // ignoring whitespace and carry on. case '\n': this.Line++; // update the line, and carry on. break; case '"': String(); break; default: if (Char.IsDigit(c)) Number(); else if (IsAlpha(c)) { Identifier(); } else { CSLox.Error(this.Line, $"Unexpected Character '{c}'"); } break; } }
public Unit VisitThisExpr(Expr.This expr) { if (this.CurrentClass == ClassType.NONE) { CSLox.Error(expr.Keyword, "Can't use 'this' outside of a class."); return(new Unit()); } ResolveLocal(expr, expr.Keyword); return(new Unit()); }
public Unit VisitReturnStmt(Stmt.Return stmt) { if (CurrentFunction == FunctionType.NONE) { CSLox.Error(stmt.Keyword, "Can't return from top-level code"); } if (stmt.Value != null) { Resolve(stmt.Value); } return(new Unit()); }
public Unit VisitVariableExpr(Expr.Variable expr) { if (Scopes.Count != 0) { var scope = Scopes.Peek(); if (scope.ContainsKey(expr.Name.Lexeme) && scope[expr.Name.Lexeme] == false) { CSLox.Error(expr.Name, "Can't read local variable in it's own initalizer"); } } ResolveLocal(expr, expr.Name); return(new Unit()); }
public Unit VisitSuperExpr(Expr.Super expr) { if (CurrentClass == ClassType.NONE) { CSLox.Error(expr.Keyword, "Can't use 'super' outside of a class."); } else if (CurrentClass != ClassType.SUBCLASS) { CSLox.Error(expr.Keyword, "Can't use 'super' in a class with no superclass"); } ResolveLocal(expr, expr.Keyword); return(new Unit()); }
public Unit VisitVariableExpr(Expr.Variable expr) { if (Scopes.Count != 0) { var scope = Scopes.Peek(); if (scope.ContainsKey(expr.Name.Lexeme) && scope[expr.Name.Lexeme] == false) { CSLox.Error(expr.Name, "Can't read local variable in it's own initalizer"); } } // Where a variable is referenced // we send that info into to the interpreter ResolveLocal(expr, expr.Name); return(new Unit()); }
public Unit VisitClassStmt(Stmt.Class stmt) { ClassType enclosingClass = this.CurrentClass; CurrentClass = ClassType.CLASS; Declare(stmt.Name); Define(stmt.Name); if (stmt.SuperClass != null && stmt.Name.Lexeme == stmt.SuperClass.Name.Lexeme) { CSLox.Error(stmt.SuperClass.Name, "A class can't inherit from itself."); } /// Resolver doesn't resolve Global variables and that environment. (Where most inheritence will occur.) /// Lox allows delcaraions inside of existing blocks, and as they are variable must be resolved. if (stmt.SuperClass != null) { CurrentClass = ClassType.SUBCLASS; Resolve(stmt.SuperClass); } if (stmt.SuperClass != null) { BeginScope(); Scopes.Peek().Add("super", true); } BeginScope(); Scopes.Peek().Add("this", true); foreach (Stmt.Function method in stmt.Methods) { FunctionType declaration = FunctionType.METHOD; if (method.Name.Lexeme.Equals("init")) { declaration = FunctionType.INITIALISER; } ResolveFunction(method, declaration); } EndScope(); if (stmt.SuperClass != null) { EndScope(); } this.CurrentClass = enclosingClass; return(new Unit()); }
private void Declare(Token name) { if (Scopes.Count == 0) { return; } var scope = Scopes.Peek(); if (scope.ContainsKey(name.Lexeme)) { CSLox.Error(name, "Already a variable with this name in this scope."); } else { scope.Add(name.Lexeme, false); } }
private void String() { while (Peek() != '"' && !isAtEnd()) { if (Peek() == '\n') Line++; Advance(); } if (isAtEnd()) { CSLox.Error(Line, "Unterminated string"); return; } Advance(); // ignore the surrounding quotes! var value = Source.Substring(Start + 1, (Current - 1) - (Start + 1)); AddToken(TokenType.STRING, value); }
public Unit VisitReturnStmt(Stmt.Return stmt) { if (CurrentFunction == FunctionType.NONE) { CSLox.Error(stmt.Keyword, "Can't return from top-level code"); } if (stmt.Value != null) { // Small hack to stop 'erberts return from a constructor. // Terminates current instancec if (CurrentFunction == FunctionType.INITIALISER) { CSLox.Error(stmt.Keyword, "Can't return a value from an initialiser."); } Resolve(stmt.Value); } return(new Unit()); }
private void NaiveMultiLineComment(int nested = 1) { // we start inside a multi-line comment. // but it could be nested while (!isAtEnd()) { // Move through the body of the multi if (Peek() == '\n') { Line++; } if (Peek() == '*' && PeekNext() == '/') { Advance(); break; } if (Peek() == '/' && PeekNext() == '*') { Advance(); Advance(); NaiveMultiLineComment(nested + 1); } // Annoying this cannot be done until the end. // We have not checked the current char Advance(); } if (isAtEnd()) { CSLox.Error(this.Line, "Unterminated multi-line comment"); } else { // Here we are at the end of the multi line comment // munch the last char. Advance(); } }
/// <summary> /// A string has been started " /// this collects all the characters until the end. /// </summary> private void StringSequence() { // Move until we reach the end of a string; while (Peek() != '"' && !isAtEnd()) { if (Peek() == '\n') { Line++; } Advance(); } if (isAtEnd()) { CSLox.Error(Line, "Unterminated string"); return; } // Move past the final " Advance(); // ignore the surrounding quotes, and create the token var value = Source.Substring(Start + 1, (Current - 1) - (Start + 1)); AddToken(STRING, value); }
private Exception Error(Token token, string message) { CSLox.Error(token, message); return(new ParserError(message)); }
private void ScanToken() { char c = Advance(); switch (c) { case '(': AddToken(LEFT_PAREN); break; case ')': AddToken(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(Match('=') ? BANG_EQUAL : BANG); break; case '=': AddToken(Match('=') ? EQUAL_EQUAL : EQUAL); break; case '<': AddToken(Match('=') ? LESS_EQUAL : LESS); break; case '>': AddToken(Match('=') ? GREATER_EQUAL : GREATER); break; case '/': if (Match('/')) { // A comment goes until the end of a line. while (Peek() != '\n' && !isAtEnd()) { Advance(); } } else if (Match('*')) { NaiveMultiLineComment(); } else { AddToken(SLASH); } break; case ' ': case '\r': case '\t': break; // ignoring whitespace and carry on. case '\n': this.Line++; // update the line nunber, and carry on. break; case '"': StringSequence(); break; // Capturing all Numbers is simpler than 1 case per number. case char val when(Char.IsDigit(val)): Number(); break; // Capturing all a-Z AND _ case char val when IsAlpha(val): Identifier(); break; default: CSLox.Error(this.Line, "Unexpected Character"); break; } }