Exemple #1
0
        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.");
            }
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        public object VisitBreakStmt(Break stmt)
        {
            if (currentLoop == LoopType.NONE)
            {
                CsLox.Error(stmt.Keyword, "Break statement must be inside a loop.");
            }

            return(null);
        }
Exemple #4
0
        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);
        }
Exemple #5
0
 public void Interpret(List <Stmt> statements)
 {
     try
     {
         foreach (Stmt statement in statements)
         {
             Execute(statement);
         }
     }
     catch (RuntimeException re)
     {
         CsLox.RuntimeError(re);
     }
 }
Exemple #6
0
        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);
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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;
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        private ParseException Error(Token token, string message)
        {
            CsLox.Error(token, message);

            return(new ParseException());
        }
Exemple #12
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.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;
            }
        }