Ejemplo n.º 1
0
        // 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);
        }
Ejemplo n.º 2
0
        // 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);
        }
Ejemplo n.º 3
0
 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);
 }
Ejemplo n.º 4
0
 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);
 }
Ejemplo n.º 5
0
 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;
 }
Ejemplo n.º 6
0
 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);
 }
Ejemplo n.º 7
0
 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);
 }
Ejemplo n.º 8
0
        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);
                }
            }
        }
Ejemplo n.º 9
0
        // 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;
            }
        }
Ejemplo n.º 10
0
 // reports an error about a token
 private ParseError Error(Token token, string message)
 {
     Basil.Error(token, message);
     return(new ParseError());
 }
Ejemplo n.º 11
0
        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);
        }