Esempio n. 1
0
        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();
        }
Esempio n. 2
0
 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;
     }
 }
Esempio n. 3
0
 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());
 }
Esempio n. 4
0
 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());
 }
Esempio n. 5
0
 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());
 }
Esempio n. 6
0
        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());
        }
Esempio n. 7
0
 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());
 }
Esempio n. 8
0
        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());
        }
Esempio n. 9
0
        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);
            }
        }
Esempio n. 10
0
        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);
        }
Esempio n. 11
0
        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());
        }
Esempio n. 12
0
        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();
            }
        }
Esempio n. 13
0
        /// <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);
        }
Esempio n. 14
0
 private Exception Error(Token token, string message)
 {
     CSLox.Error(token, message);
     return(new ParserError(message));
 }
Esempio n. 15
0
        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;
            }
        }