// 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_; }
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; }
// 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)); }
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; } }
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; } }
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(); } }
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); }
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(); } }
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(); } }
// 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; }