Пример #1
0
    // BB (andrews): Start line should be provided from the if token

    public AstIfStmt(AstExpr condition, AstStmt body, AstStmt else_ = null)
        : base(STMTK.If, condition.m_startLine)
    {
        m_condition = condition;
        m_body      = body;
        m_else      = else_;
    }
Пример #2
0
 public AstForStmt(int startLine, AstExpr pre, AstExpr condition, AstExpr post, AstStmt body)
     : base(STMTK.For, startLine)
 {
     m_preExpr   = pre;
     m_condition = condition;
     m_post      = post;
     m_body      = body;
 }
Пример #3
0
    // TODO: Consider not using parentheses around the conditional clause? The ebook says that a delimiter after the
    //  conditional is required, but it could be ), then, or {
    //  I think Jai doesn't require parentheses or a delimiter though? I wonder how it handles some of these ambiguous
    //  cases or if they are just kind of ignored since most of them are pretty nonsense:
    //  https://softwareengineering.stackexchange.com/questions/335504/why-do-languages-require-parenthesis-around-expressions-when-used-with-if-and

    protected AstStmt ParseIfStmt()
    {
        Token ifToken = Previous();

        Debug.Assert(ifToken.m_tokenk == TOKENK.If);

        Token openParen;
        Token closeParen;

        AstExpr condition;
        AstStmt body;

        // TODO: A lot of these errors after getting null expr/stmt are probably reported as errors at the spots
        //  where they are parsed and returned as null. It might be redundant to also report them here. Do an audit
        //  of what kinds of malformed if's lead to these errors and see if the additional message actually provides
        //  any useful information. It *might*, since the other errors aren't reported in the context of an "if"
        //  statement but these ones are. Do the audit to find out!!

        if ((openParen = TryMatch(TOKENK.OpenParen)) == null)
        {
            return(ErrorStmt(ifToken.m_line, "Expected '(' after \"if\""));
        }

        condition = ParseExpr();
        if (condition == null)
        {
            return(EmptyErrorStmt());
        }

        if ((closeParen = TryMatch(TOKENK.CloseParen)) == null)
        {
            return(ErrorStmt(condition.m_startLine, "Expected ')' after \"if\" conditional"));
        }

        body = ParseStmt();
        if (body == null)
        {
            return(EmptyErrorStmt());
        }

        Token   elseToken;
        AstStmt elseStmt = null;

        if ((elseToken = TryMatch(TOKENK.Else)) != null)
        {
            // TODO: Right now we assign a dangling else to the nearest "if". I would prefer if
            //  we could *detect* the ambiguous dangling else case and simply make it an error.
            //  This would require some special bookkeeping, but I don't see why it wouldn't be possible.

            elseStmt = ParseStmt();
            if (elseStmt == null)
            {
                return(ErrorStmt(elseToken.m_line, "Expected statement after \"else\""));
            }
        }

        return(new AstIfStmt(condition, body, elseStmt));
    }
Пример #4
0
    protected void ExecuteStmt(AstStmt stmt)
    {
        if (HadErrorOrReturn())
        {
            return;
        }

        switch (stmt.m_stmtk)
        {
        case STMTK.Expr:
            ExecuteExprStmt((AstExprStmt)stmt); break;

        case STMTK.Print:
            ExecutePrintStmt((AstPrintStmt)stmt); break;

        case STMTK.VarDecl:
            ExecuteVarDeclStmt((AstVarDeclStmt)stmt); break;

        case STMTK.FunDecl:
            ExecuteFunDeclStmt((AstFunDeclStmt)stmt); break;

        case STMTK.ClassDecl:
            ExecuteClassDeclStmt((AstClassDeclStmt)stmt); break;

        case STMTK.Block:
            ExecuteBlockStmt((AstBlockStmt)stmt); break;

        case STMTK.If:
            ExecuteIfStmt((AstIfStmt)stmt); break;

        case STMTK.While:
            ExecuteWhileStmt((AstWhileStmt)stmt); break;

        case STMTK.For:
            ExecuteForStmt((AstForStmt)stmt); break;

        case STMTK.Break:
            ExecuteBreakStmt((AstBreakStmt)stmt); break;

        case STMTK.Continue:
            ExecuteContinueStmt((AstContinueStmt)stmt); break;

        case STMTK.Return:
            ExecuteReturnStmt((AstReturnStmt)stmt); break;

        default:
        {
            m_runtimeError = true;
            Lox.InternalError(stmt.m_startLine, "Unexpected statement kind " + stmt.m_stmtk);
        }
        break;
        }
    }
Пример #5
0
    protected void ResolveStmt(AstStmt stmt)
    {
        // todo when error?
        switch (stmt.m_stmtk)
        {
        case STMTK.Expr:
            ResolveExprStmt((AstExprStmt)stmt); break;

        case STMTK.Print:
            ResolvePrintStmt((AstPrintStmt)stmt); break;

        case STMTK.VarDecl:
            ResolveVarDeclStmt((AstVarDeclStmt)stmt); break;

        case STMTK.FunDecl:
            ResolveFunDeclStmt((AstFunDeclStmt)stmt); break;

        case STMTK.ClassDecl:
            ResolveClassDeclStmt((AstClassDeclStmt)stmt); break;

        case STMTK.Block:
            ResolveBlockStmt((AstBlockStmt)stmt); break;

        case STMTK.If:
            ResolveIfStmt((AstIfStmt)stmt); break;

        case STMTK.While:
            ResolveWhileStmt((AstWhileStmt)stmt); break;

        case STMTK.For:
            ResolveForStmt((AstForStmt)stmt); break;

        case STMTK.Return:
            ResolveReturnStmt((AstReturnStmt)stmt); break;

        // NOP

        case STMTK.Break:
        case STMTK.Continue:
            break;

        default:
        {
            m_error = true;
            Lox.InternalError(stmt.m_startLine, "Unexpected statement kind in resolver " + stmt.m_stmtk);
        }
        break;
        }
    }
Пример #6
0
    protected AstStmt ParseWhileStmt()
    {
        PushLoop();     // Ugh... really wish there was some sort a nicer way to defer PopLoop
        try
        {
            Token whileToken = Previous();
            Debug.Assert(whileToken.m_tokenk == TOKENK.While);

            Token openParen;
            Token closeParen;

            if ((openParen = TryMatch(TOKENK.OpenParen)) == null)
            {
                return(ErrorStmt(whileToken.m_line, "Expected '(' after \"while\""));
            }

            AstExpr condition = ParseExpr();
            if (condition == null)
            {
                return(EmptyErrorStmt());
            }

            if ((closeParen = TryMatch(TOKENK.CloseParen)) == null)
            {
                return(ErrorStmt(condition.m_startLine, "Expected ')' after \"while\" conditional"));
            }

            AstStmt stmt = ParseStmt();
            if (stmt == null)
            {
                return(EmptyErrorStmt());
            }

            return(new AstWhileStmt(condition, stmt));
        }
        finally
        {
            PopLoop();
        }
    }
Пример #7
0
    public List <AstStmt> Parse()
    {
        m_currentIndex = 0;

        List <AstStmt> statements = new List <AstStmt>();

        while (!IsAtEnd())
        {
            AstStmt stmt = ParseDeclStmt();

            if (stmt != null)
            {
                statements.Add(stmt);
            }
            else
            {
                // TODO: error recovery

                return(statements);
            }
        }

        return(statements);
    }
Пример #8
0
    protected AstStmt ParseForStmt()
    {
        PushLoop();
        try
        {
            Token forToken = Previous();
            Debug.Assert(forToken.m_tokenk == TOKENK.For);

            Token openParen;
            Token closeParen;

            AstVarDeclStmt preDecl   = null;
            AstExpr        preExpr   = null;
            AstExpr        condition = null;
            AstExpr        post      = null;

            Token semicolon;

            if ((openParen = TryMatch(TOKENK.OpenParen)) == null)
            {
                return(ErrorStmt(forToken.m_line, "Expected '(' after \"for\""));
            }

            if ((semicolon = TryMatch(TOKENK.Semicolon)) == null)
            {
                Token varToken;
                if ((varToken = TryMatch(TOKENK.Var)) != null)
                {
                    AstStmt stmt = ParseVarDeclStmt();
                    if (stmt == null)
                    {
                        return(EmptyErrorStmt());
                    }

                    Debug.Assert(stmt.m_stmtk == STMTK.VarDecl);
                    preDecl = (AstVarDeclStmt)stmt;
                }
                else
                {
                    AstStmt stmt = ParseExprStmt();
                    if (stmt == null)
                    {
                        return(EmptyErrorStmt());
                    }

                    Debug.Assert(stmt.m_stmtk == STMTK.Expr);
                    preExpr = ((AstExprStmt)stmt).m_expr;
                }
            }

            if ((semicolon = TryMatch(TOKENK.Semicolon)) == null)
            {
                AstStmt stmt = ParseExprStmt();
                if (stmt == null)
                {
                    return(EmptyErrorStmt());
                }

                Debug.Assert(stmt.m_stmtk == STMTK.Expr);
                condition = ((AstExprStmt)stmt).m_expr;
            }

            if ((closeParen = TryMatch(TOKENK.CloseParen)) == null)
            {
                post = ParseExpr();
                if (post == null)
                {
                    return(EmptyErrorStmt());
                }

                if ((closeParen = TryMatch(TOKENK.CloseParen)) == null)
                {
                    return(ErrorStmt(forToken.m_line, "Expected ')' at end of \"for\""));
                }
            }

            AstStmt body = ParseStmt();
            if (body == null)
            {
                return(EmptyErrorStmt());
            }

            if (preDecl != null)
            {
                return(new AstForStmt(forToken.m_line, preDecl, condition, post, body));
            }
            else
            {
                return(new AstForStmt(forToken.m_line, preExpr, condition, post, body));
            }
        }
        finally
        {
            PopLoop();
        }
    }
Пример #9
0
    protected AstStmt ParseFunDeclStmt()
    {
        PushFun();
        try
        {
            // BB (andrews) Many of the error lines are only "close enough"

            Token fun = Previous();
            Debug.Assert(fun.m_tokenk == TOKENK.Fun);

            Token ident;
            if ((ident = TryMatch(TOKENK.Identifier)) == null)
            {
                return(ErrorStmt(fun.m_line, "Expected identifier after \"fun\""));
            }

            Token token;
            if ((token = TryMatch(TOKENK.OpenParen)) == null)
            {
                return(ErrorStmt(fun.m_line, "Expected '(' after function name"));
            }

            List <string> params_      = new List <string>();
            bool          isFirstParam = true;

            while ((token = TryMatch(TOKENK.CloseParen)) == null)
            {
                if (!isFirstParam)
                {
                    if ((token = TryMatch(TOKENK.Comma)) == null)
                    {
                        return(ErrorStmt(fun.m_line, "Expected ',' in parameter list"));
                    }

                    if (params_.Count == AstFunCallExpr.s_maxArgCount)
                    {
                        // NOTE (andrews) This isn't a parse error so we don't return ErrorExpr like we do for other parse errors.
                        //  This lets us report it while continuing the parse in case we find other errors.

                        Lox.Error(fun.m_line, "Cannot exceed " + AstFunCallExpr.s_maxArgCount + " parameters");
                    }
                }

                if ((token = TryMatch(TOKENK.Identifier)) == null)
                {
                    return(ErrorStmt(ident.m_line, "Expected parameter identifier"));
                }

                params_.Add(token.m_lexeme);
                isFirstParam = false;
            }

            if ((token = TryMatch(TOKENK.OpenBrace)) == null)
            {
                return(ErrorStmt(fun.m_line, "Expected '{'"));
            }

            AstStmt block = ParseBlockStmt();
            if (block == null)
            {
                return(EmptyErrorStmt());
            }

            Debug.Assert(block.m_stmtk == STMTK.Block);

            return(new AstFunDeclStmt(ident, params_, ((AstBlockStmt)block).m_stmts));
        }
        finally
        {
            PopFun();
        }
    }
Пример #10
0
    // BB (andrews): Start line should be provided from the while token

    public AstWhileStmt(AstExpr condition, AstStmt body)
        : base(STMTK.While, condition.m_startLine)
    {
        m_condition = condition;
        m_body      = body;
    }