// constructs a token with a string literal private string MakeString(int startIndex) { while (Peek() != '"' && !IsAtEnd()) { if (Peek() == '\n') { line++; } Advance(); } // Unterminated string. if (IsAtEnd()) { Basil.Error(line, "Unterminated string."); return(""); } // The closing ". Advance(); // Trim the surrounding quotes. string value = source.Substring(startIndex, (current) - (startIndex + 1)); return(value); }
// constructs a token with a string literal private void MakeString() { while (Peek() != '"' && !IsAtEnd()) { if (Peek() == '\n') { line++; } Advance(); } // Unterminated string. if (IsAtEnd()) { Basil.Error(line, "Unterminated string."); return; } // The closing ". Advance(); // Trim the surrounding quotes. string value = source.Substring(start + 1, (current - 1) - (start + 1)); AddToken(Token.TokenType.String, value); }
public object VisitVariableExpr(Expr.Variable expr) { if (scopes.Count != 0 && scopes.Peek().ContainsKey(expr.name.lexeme) && scopes.Peek()[expr.name.lexeme] == false) { Basil.Error(expr.name, "Cannot read local variable in its own initializer."); } ResolveLocal(expr, expr.name); return(null); }
public object VisitThisExpr(Expr.This expr) { if (currentClass == ClassType.None) { Basil.Error(expr.Keyword, "Cannot use 'this' outside of a class."); return(null); } ResolveLocal(expr, expr.Keyword); return(null); }
private void Declare(Token name) { if (scopes.Count == 0) { return; } if (scopes.Peek().ContainsKey(name.lexeme)) { Basil.Error(name, "Variable with this name is already declared in this scope."); } scopes.Peek()[name.lexeme] = false; }
public object VisitSuperExpr(Expr.Super expr) { if (currentClass == ClassType.None) { Basil.Error(expr.Keyword, "Cannot use 'super' outside of a class."); } else if (currentClass != ClassType.Subclass) { Basil.Error(expr.Keyword, "Cannot use 'super' in a class with no superclass."); } ResolveLocal(expr, expr.Keyword); return(null); }
public void Interpret(List <Stmt> statements) { try { // statements be empty? foreach (Stmt statement in statements) { Execute(statement); } } catch (RuntimeError error) { Basil.RuntimeError(error); } }
public object VisitReturnStmt(Stmt.Return stmt) { if (currentFunction == FunctionType.None) { Basil.Error(stmt.keyword, "Cannot return from top level code."); } if (stmt.value != null) { if (currentFunction == FunctionType.Initializer) { Basil.Error(stmt.keyword, "Cannot return a value from an initializer."); } Resolve(stmt.value); } return(null); }
private void ImportToken(int tokenStart) { //var c = peek(); while (IsWhiteSpace(Peek())) { //c = peek(); Advance(); } if (Peek() != '"') { Basil.Error(line, "Unexpected character."); return; } else { Advance(); string path = MakeString(current); // import if (!imports.Contains(path)) { imports.Add(path); // scrub preprocessing from code var tokenLength = current - tokenStart; source = source.Remove(tokenStart, tokenLength); current -= tokenLength; string newSource = System.IO.File.ReadAllText(path); Importer importer = new Importer(newSource); string preprocessedNewSource = importer.Process(); source = source.Insert(current, preprocessedNewSource); current += preprocessedNewSource.Length + 1; } else { // scrub preprocessing from code source = source.Remove(tokenStart, current - tokenStart); } } }
// scan an individual token private void ScanToken() { char c = Advance(); switch (c) { // Single-character case '(': AddToken(Token.TokenType.LeftParenthesis); break; case ')': AddToken(Token.TokenType.RightParenthesis); break; case '{': AddToken(Token.TokenType.LeftBrace); break; case '}': AddToken(Token.TokenType.RightBrace); break; //case '[': addToken(Token.TokenType.LeftBracket); break; //case ']': addToken(Token.TokenType.RightBracket); break; case ',': AddToken(Token.TokenType.Comma); break; case '.': AddToken(Token.TokenType.Dot); break; //case '-': AddToken(Token.TokenType.Minus); break; //case '+': AddToken(Token.TokenType.Plus); break; case ';': AddToken(Token.TokenType.Semicolon); break; //case '*': AddToken(Token.TokenType.Star); break; //case '%': AddToken(Token.TokenType.Percent); break; case '-': if (Match('-')) { AddToken(Token.TokenType.MinusMinus); } else if (Match('=')) { AddToken(Token.TokenType.MinusEqual); } else { AddToken(Token.TokenType.Minus); } break; case '+': if (Match('+')) { AddToken(Token.TokenType.PlusPlus); } else if (Match('=')) { AddToken(Token.TokenType.PlusEqual); } else { AddToken(Token.TokenType.Plus); } break; case '*': AddToken(Match('=') ? Token.TokenType.StarEqual : Token.TokenType.Minus); break; case '%': AddToken(Match('=') ? Token.TokenType.PercentEqual : Token.TokenType.Minus); break; //case '?': addToken(Token.TokenType.If); break; //case '|': addToken(Token.TokenType.Else); break; // Single or double characters case '!': AddToken(Match('=') ? Token.TokenType.BangEqual : Token.TokenType.Bang); break; case '=': AddToken(Match('=') ? Token.TokenType.EqualEqual : Token.TokenType.Equal); break; case '<': AddToken(Match('=') ? Token.TokenType.LessEqual : Token.TokenType.Less); break; case '>': AddToken(Match('=') ? Token.TokenType.GreaterEqual : Token.TokenType.Greater); break; case '/': if (Match('/')) { // A comment goes until the end of the line. while (Peek() != '\n' && !IsAtEnd()) { Advance(); } } else if (Match('*')) { // A comment goes until the end of the block while (!IsAtEnd()) { if (Peek() == '*' && PeekNext() == '/') { Advance(); Advance(); break; } Advance(); } } else if (Match('=')) { AddToken(Token.TokenType.SlashEqual); } else { AddToken(Token.TokenType.Slash); } break; // Literals case '"': MakeString(); break; // White-space case ' ': case '\r': case '\t': // Ignore whitespace. break; case '\n': line++; break; // if not a language token, determine what the user has entered and if it is valid default: if (IsDigit(c)) { MakeNumber(); } else if (IsAlpha(c)) { Identifier(); } else { Basil.Error(line, "Unexpected character."); } break; } }
// reports an error about a token private ParseError Error(Token token, string message) { Basil.Error(token, message); return(new ParseError()); }
private Expr Assignment() { Expr expr = Or(); if (Match(Token.TokenType.PlusPlus)) { Token variable = Previous(); if (expr is Expr.Variable v) { Token name = v.name; return(new Expr.Assign(name, new Expr.Binary(expr, new Token(Token.TokenType.Plus, "+", null, variable.line), new Expr.Literal(1.0)))); } else if (expr is Expr.Get) { Expr.Get get = (Expr.Get)expr; return(new Expr.Set(get.Objekt, get.Name, new Expr.Binary(expr, new Token(Token.TokenType.Plus, "+", null, variable.line), new Expr.Literal(1.0)))); } Error(variable, "Invalid assignment target."); } if (Match(Token.TokenType.MinusMinus)) { Token variable = Previous(); if (expr is Expr.Variable v) { Token name = v.name; return(new Expr.Assign(name, new Expr.Binary(expr, new Token(Token.TokenType.Plus, "+", null, variable.line), new Expr.Literal(-1.0)))); } else if (expr is Expr.Get) { Expr.Get get = (Expr.Get)expr; return(new Expr.Set(get.Objekt, get.Name, new Expr.Binary(expr, new Token(Token.TokenType.Plus, "+", null, variable.line), new Expr.Literal(-1.0)))); } Error(variable, "Invalid assignment target."); } if (Match(Token.TokenType.Equal, Token.TokenType.MinusEqual, Token.TokenType.PlusEqual, Token.TokenType.StarEqual, Token.TokenType.SlashEqual, Token.TokenType.PercentEqual, Token.TokenType.PlusPlus)) { Token equals = Previous(); Expr tempValue = Assignment(); Expr assignedValue = tempValue; switch (equals.type) { case Token.TokenType.Equal: break; case Token.TokenType.MinusEqual: assignedValue = new Expr.Binary(expr, new Token(Token.TokenType.Minus, "-", null, equals.line), tempValue); break; case Token.TokenType.PlusEqual: assignedValue = new Expr.Binary(expr, new Token(Token.TokenType.Plus, "+", null, equals.line), tempValue); break; case Token.TokenType.StarEqual: assignedValue = new Expr.Binary(expr, new Token(Token.TokenType.Star, "*", null, equals.line), tempValue); break; case Token.TokenType.SlashEqual: assignedValue = new Expr.Binary(expr, new Token(Token.TokenType.Slash, "/", null, equals.line), tempValue); break; case Token.TokenType.PercentEqual: assignedValue = new Expr.Binary(expr, new Token(Token.TokenType.Percent, "%", null, equals.line), tempValue); break; default: Basil.Error(Previous().line, "Tried to create assignment statement but tokens were invalid."); break; } if (expr is Expr.Variable v) { Token name = v.name; return(new Expr.Assign(name, assignedValue)); } else if (expr is Expr.Get) { Expr.Get get = (Expr.Get)expr; return(new Expr.Set(get.Objekt, get.Name, assignedValue)); } Error(equals, "Invalid assignment target."); } //else if (Match(Token.TokenType.MinusEqual)) //{ // Token equals = Previous(); // Expr value = Assignment(); // // replace assignment expression with (expr - value) // Expr minusEquals = new Expr.Binary(expr, new Token(Token.TokenType.Minus, "-", null, equals.line), value); // if (expr is Expr.Variable v) // { // Token name = v.name; // return new Expr.Assign(name, minusEquals); // } // else if (expr is Expr.Get) // { // Expr.Get get = (Expr.Get)expr; // return new Expr.Set(get.Objekt, get.Name, minusEquals); // } // Error(equals, "Invalid assignment target."); //} return(expr); }