Created
September 15, 2025 08:11
-
-
Save resetius/45efe2b970542b428cc5e5326eb6c7f7 to your computer and use it in GitHub Desktop.
parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using TAstTask = TExpectedTask<TExprPtr, TParserError, TLoc>; | |
| // factor ::= Number | Ident | '(' Expr ')' | 'fun' ... | |
| TAstTask factor(TTokenStream& s) { | |
| auto tok = co_await s.Next(); | |
| if (tok.Type == TToken::Number) | |
| co_return std::make_shared<TNumberExpr>(tok.Loc, tok.Value.i64); | |
| if (tok.Type == TToken::Identifier) | |
| co_return std::make_shared<TIdentExpr>(tok.Loc, tok.Name); | |
| if (tok.Type == TToken::Keyword && tok.Value == (int)TToken::Fun) | |
| co_return co_await fun_expr_or_decl_after_fun(s); | |
| if (tok.Type == TToken::Op && tok.Value == '(') { | |
| auto e = co_await expr(s); | |
| auto r = co_await s.Next(); | |
| if (!(r.Type == TToken::Op && r.Value == ')')) co_return TParserError(s.GetLoc(), "expected ')'"); | |
| co_return e; | |
| } | |
| co_return TParserError(s.GetLoc(), "expected primary"); | |
| } | |
| // call_expr ::= factor ( '(' arg_list_opt ')' )* | |
| TAstTask call_expr(TTokenStream& s) { | |
| auto base = co_await factor(s); | |
| while (auto t = s.Next()) { | |
| if (t->Type == TToken::Op && t->Value == '(') { | |
| auto args = co_await parse_arg_list_opt(s); | |
| base = std::make_shared<TCallExpr>(t->Loc, std::move(base), std::move(args)); | |
| } else { s.Unget(*t); break; } | |
| } | |
| co_return base; | |
| } | |
| // unary ::= call_expr ( ('+'|'-') unary ) | |
| TAstTask unary_expr(TTokenStream& s) { | |
| auto tok = co_await s.Next(); | |
| if (tok.Type == TToken::Op && (tok.Value == '+' || tok.Value == '-')) { | |
| auto inner = co_await unary_expr(s); | |
| co_return std::make_shared<TUnaryExpr>(tok.Loc, TOp(tok.Value.u64), std::move(inner)); | |
| } | |
| s.Unget(tok); | |
| co_return co_await call_expr(s); | |
| } | |
| template<class Prev, class... Ops> | |
| TAstTask binary_chain(TTokenStream& s, Prev prev, Ops... ops) { | |
| auto ret = co_await prev(s); | |
| while (auto tok = s.Next()) { | |
| if ((tok->Type == TToken::Op && ((tok->Value == TOp(ops)) || ...))) { | |
| auto rhs = co_await prev(s); | |
| ret = std::make_shared<TBinaryExpr>(tok->Loc, TOp(tok->Value.u64), std::move(ret), std::move(rhs)); | |
| } else { s.Unget(*tok); break; } | |
| } | |
| co_return ret; | |
| } | |
| TAstTask mul_expr(TTokenStream& s) { co_return co_await binary_chain(s, unary_expr, '*', '/'); } | |
| TAstTask add_expr(TTokenStream& s) { co_return co_await binary_chain(s, mul_expr, '+', '-'); } | |
| TAstTask rel_expr(TTokenStream& s) { co_return co_await binary_chain(s, add_expr, '<', '>'); } | |
| TAstTask eq_expr(TTokenStream& s) { co_return co_await binary_chain(s, rel_expr, TOp('=', '=')); } | |
| TAstTask and_expr(TTokenStream& s) { co_return co_await binary_chain(s, eq_expr, TOp('&', '&')); } | |
| TAstTask or_expr(TTokenStream& s) { co_return co_await binary_chain(s, and_expr, TOp('|', '|')); } | |
| // assign_expr ::= ('val'|'var') IDENT '=' assign_expr | IDENT '=' assign_expr | or_expr | |
| TAstTask assign_expr(TTokenStream& s) { | |
| auto tok = co_await s.Next(); | |
| if (tok.Type == TToken::Keyword && (tok.Value == (int)TToken::Val || tok.Value == (int)TToken::Var)) { | |
| const bool isVar = tok.Value == (int)TToken::Var; | |
| auto id = co_await s.Next(); | |
| if (id.Type != TToken::Identifier) co_return TParserError(s.GetLoc(), "expected identifier after val/var"); | |
| auto eq = co_await s.Next(); | |
| if (!(eq.Type == TToken::Op && eq.Value == '=')) { | |
| if (isVar) co_return std::make_shared<TVarStmt>(id.Loc, id.Name); // var x; | |
| co_return TParserError(s.GetLoc(), "expected '=' in val declaration"); | |
| } | |
| auto init = co_await assign_expr(s); | |
| co_return std::make_shared<TDeclExpr>(eq.Loc, isVar, id.Name, std::move(init)); | |
| } | |
| if (tok.Type == TToken::Identifier) { | |
| if (auto eq = s.Next(); eq && eq->Type == TToken::Op && eq->Value == '=') { | |
| auto rhs = co_await assign_expr(s); | |
| co_return std::make_shared<TAssignExpr>(eq->Loc, tok.Name, std::move(rhs)); | |
| } | |
| s.Unget(*eq); | |
| } else { s.Unget(tok); } | |
| co_return co_await or_expr(s); | |
| } | |
| // cond_expr ::= 'if' '(' assign_expr ')' expr ('else' expr)? | |
| TAstTask cond_expr(TTokenStream& s) { | |
| auto tok = co_await s.Next(); | |
| if (tok.Type == TToken::Keyword && tok.Value == (int)TToken::If) { | |
| if (auto t = s.Next(); !(t.Type == TToken::Op && t.Value == '(')) co_return TParserError(s.GetLoc(), "expected '(' after if"); | |
| auto cond = co_await assign_expr(s); | |
| if (auto t = co_await s.Next(); !(t.Type == TToken::Op && t.Value == ')')) co_return TParserError(s.GetLoc(), "expected ')'"); | |
| auto thenE = co_await expr(s); | |
| auto next = co_await s.Next(); | |
| if (next.Type == TToken::Keyword && next.Value == (int)TToken::Else) { | |
| auto elseE = co_await expr(s); | |
| co_return std::make_shared<TIfExpr>(next.Loc, std::move(cond), std::move(thenE), std::move(elseE)); | |
| } | |
| s.Unget(next); | |
| co_return std::make_shared<TIfStmtExpr>(tok.Loc, std::move(cond), std::move(thenE)); | |
| } | |
| s.Unget(tok); | |
| co_return co_await or_expr(s); | |
| } | |
| std::expected<TExprPtr, TParserError> Parse(TTokenStream& s) { | |
| return stmt_list(s).result(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment