Skip to content

Instantly share code, notes, and snippets.

@resetius
Created September 15, 2025 08:11
Show Gist options
  • Select an option

  • Save resetius/45efe2b970542b428cc5e5326eb6c7f7 to your computer and use it in GitHub Desktop.

Select an option

Save resetius/45efe2b970542b428cc5e5326eb6c7f7 to your computer and use it in GitHub Desktop.
parser
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