// Expect retcode = 0 // Parser:token 流 -> Ast(ADT 节点)。C 版只作控制流参考,节点分配顺序需与 stage1 // 一致,以保证 dump 的 #ref 可对比。pos 在 token 向量上移动,自动跳过 trivia。 type .{ .Vec } = @import("utils/vec.spl"); type .{ .ByteView } = @import("utils/byte_view.spl"); type .{ .Token, .TokenKind, .Radix, .Keyword, .Punct } = @import("token.spl"); type .{ .Lexer } = @import("lexer.spl"); type BigInt = @import("utils/mp.spl"); type Ast = @import("ast.spl"); type .{ .Body, .ref_vec } = @import("ast.spl"); type .{ .Span, .span_make, .span_none } = @import("diag.spl"); type Diag = @import("diag.spl"); type .{ .Sources, .set_new, .set_add, .set_drop } = @import("utils/source.spl"); type .{ .ByteBuffer } = @import("utils/byte_buffer.spl"); type Parser = @self; ast: *Ast, toks: Vec, // Token(借用) diag: *Diag, // 全程诊断;可为 null(自测) pos: usize, failed: bool, stop_agg: i32, error_count: usize, file: usize, // 当前源文件在 Sources 中的下标 comptime_depth: usize, // >0 表示处于 comptime 上下文(类型可作为表达式) fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser { ret Parser { .ast = ast, .toks = toks, .diag = diag, .pos = 0, .failed = false, .stop_agg = 0, .error_count = 0, .file = 0, .comptime_depth = 0, }; } // ---- token 访问 ---- fn token_ptr(self: *Parser, index: usize): *Token { if index >= self.toks.len { ret null; } ret (self.toks.data as *Token) + index; } fn is_trivia(kind: TokenKind): bool { match kind { .Endline => { ret true; }, .LineComment => { ret true; }, _ => { ret false; }, } ret false; } // 跳过 trivia;返回当前 token(含 EOF)。越界返回 null。 fn peek(self: *Parser): *Token { var n = self.toks.len; var base = self.toks.data as *Token; var i = self.pos; while i < n { if base == null { break; } var t = base + i; if !is_trivia(t.kind) { break; } i = i + 1; } self.pos = i; if i >= n || base == null { ret null; } ret base + i; } // 跳过 trivia,返回当前 token 之后的下一个非 trivia token(不移动 pos)。 fn peek_next(self: *Parser): *Token { var n = self.toks.len; var base = self.toks.data as *Token; var i = self.pos + 1; while i < n { if base == null { break; } var t = base + i; if !is_trivia(t.kind) { break; } i = i + 1; } if i >= n || base == null { ret null; } ret base + i; } fn advance(self: *Parser): *Token { var t = peek(self); if t == null { ret null; } var eof = false; match t.kind { .Eof => { eof = true; }, _ => {}, } if !eof { self.pos = self.pos + 1; } ret t; } fn kind_is_punct(kind: TokenKind, punct: Punct): bool { match kind { .Punct(p) => { ret @enum2int(p) == @enum2int(punct); }, _ => { ret false; }, } ret false; } fn kind_is_keyword(kind: TokenKind, kw: Keyword): bool { match kind { .Keyword(k) => { ret @enum2int(k) == @enum2int(kw); }, _ => { ret false; }, } ret false; } fn at_punct(self: *Parser, punct: Punct): bool { var t = peek(self); if t == null { ret false; } ret kind_is_punct(t.kind, punct); } fn at_keyword(self: *Parser, kw: Keyword): bool { var t = peek(self); if t == null { ret false; } ret kind_is_keyword(t.kind, kw); } fn at_eof(self: *Parser): bool { var t = peek(self); if t == null { ret true; } match t.kind { .Eof => { ret true; }, _ => { ret false; }, } ret false; } // ---- 错误 ---- fn span_of(self: *Parser, tok: *Token): Span { if tok == null { ret span_none(); } ret span_make(self.file, tok.offset, tok.offset + tok.text.len); } fn parse_error(self: *Parser, tok: *Token, what: []u8) { self.error_count = self.error_count + 1; if self.diag == null { ret; } Diag.err(self.diag, span_of(self, tok), .ParseExpected, what); } // 转义解码(镜像 stage1 spl_decode_escape):去掉引号,处理 \n \t \r \0 \\ \' \" \xHH。 fn hex_digit(c: u8): u8 { if c >= '0' && c <= '9' { ret c - '0'; } if c >= 'a' && c <= 'f' { ret (c - 'a') + 10; } if c >= 'A' && c <= 'F' { ret (c - 'A') + 10; } ret 0; } fn decode_str(t: ByteView): ByteBuffer { @assert(t.len >= 2 && t.ptr != null, "string literal token must include quotes"); var out = ByteBuffer.new(); if t.len < 2 { ret out; } var i: usize = 1; var end = t.len - 1; while i < end { var c = t.ptr[i]; if c == '\\' && i + 1 < end { var e = t.ptr[i + 1]; if e == 'x' && i + 3 < end { var hi = hex_digit(t.ptr[i + 2]); var lo = hex_digit(t.ptr[i + 3]); match ByteBuffer.append_byte(&out, (hi * 16 + lo) as u8) { .Ok => {}, .Err(error) => {}, } i = i + 4; } else { var d: u8 = e; if e == 'n' { d = '\n'; } else if e == 't' { d = '\t'; } else if e == 'r' { d = '\r'; } else if e == '0' { d = 0; } match ByteBuffer.append_byte(&out, d) { .Ok => {}, .Err(error) => {} } i = i + 2; } } else { match ByteBuffer.append_byte(&out, c) { .Ok => {}, .Err(error) => {} } i = i + 1; } } ret out; } fn expect_punct(self: *Parser, punct: Punct, what: []u8): bool { var t = peek(self); if t == null || !kind_is_punct(t.kind, punct) { parse_error(self, t, what); ret false; } advance(self); ret true; } // ---- 节点 ---- fn new_node(self: *Parser, body: Body, tok: *Token): usize { ret Ast.alloc(self.ast, body, span_of(self, tok)); } fn radix_value(r: Radix): u8 { match r { .Bin => { ret 2; }, .Oct => { ret 8; }, .Hex => { ret 16; }, .Dec => { ret 10; }, } ret 10; } // 整型 token 文本(去下划线、去 0x/0b/0o 前缀)→ 任意宽度 BigInt。失败返回 0 并报错。 fn big_int_of(self: *Parser, tok: *Token, radix: Radix): BigInt { var cleaned = ByteBuffer.new(); defer { ByteBuffer.drop(&cleaned); } var start: usize = 0; if @enum2int(radix) != @enum2int(Radix.Dec) && tok.text.len >= 2 { start = 2; } var i = start; while i < tok.text.len { var b = tok.text.ptr[i]; if b != '_' { match ByteBuffer.append_byte(&cleaned, b) { .Ok => {}, .Err(error) => { ret BigInt.zero(); }, } } i = i + 1; } var out = BigInt.zero(); match BigInt.parse(&out, ByteBuffer.as_slice(cleaned), radix_value(radix)) { .Ok => {}, .Err(error) => { parse_error(self, tok, "invalid integer literal"); BigInt.drop(&out); ret BigInt.zero(); }, } ret out; } // 数组长度等场景取 u64(溢出/失败取 0)。 fn int_token_u64(self: *Parser, tok: *Token, radix: Radix): u64 { var b = big_int_of(self, tok, radix); var v: u64 = 0; match BigInt.to_u64(&v, b) { .Ok => {}, .Err(error) => { v = 0; }, } BigInt.drop(&b); ret v; } // ---- 判定(stage1 can_start_*)---- fn can_start_expr(kind: TokenKind): bool { match kind { .Int(_) => { ret true; }, .Float => { ret true; }, .Char(_) => { ret true; }, .Str => { ret true; }, .Keyword(k) => { match k { .True => { ret true; }, .False => { ret true; }, .Null => { ret true; }, .Undefined => { ret true; }, .If => { ret true; }, .While => { ret true; }, .Loop => { ret true; }, .For => { ret true; }, .Match => { ret true; }, .Comptime => { ret true; }, .Try => { ret true; }, _ => { ret false; }, } }, .Ident => { ret true; }, .Punct(p) => { match p { .At => { ret true; }, .Sharp => { ret true; }, .Dollar => { ret true; }, .LParen => { ret true; }, .LBracket => { ret true; }, .LBrace => { ret true; }, .Dot => { ret true; }, .Sub => { ret true; }, .Not => { ret true; }, .BitNot => { ret true; }, .And => { ret true; }, .Mul => { ret true; }, _ => { ret false; }, } }, _ => { ret false; }, } ret false; } fn can_start_statement(kind: TokenKind): bool { match kind { .Keyword(k) => { match k { .If => { ret true; }, .While => { ret true; }, .Loop => { ret true; }, .For => { ret true; }, .Match => { ret true; }, .Ret => { ret true; }, .Break => { ret true; }, .Continue => { ret true; }, .Defer => { ret true; }, .Errdefer => { ret true; }, .Var => { ret true; }, .Const => { ret true; }, .Type => { ret true; }, .Comptime => { ret true; }, _ => { ret false; }, } }, _ => { ret false; }, } ret false; } // ---- 模式(M3.2 子集:ident / _ / .variant / tuple / .{ } )---- fn parse_pattern_aggregate(self: *Parser, start: *Token): usize { if !expect_punct(self, .LBrace, "'{' after '.'") { ret 0; } // stage1 先分配 PATTERN_AGGREGATE,再解析字段。 var ref = new_node(self, .PatternAggregate(.{ .items = ref_vec() }), start); var fields = ref_vec(); while !self.failed { var t = peek(self); if t == null { break; } var is_eof = false; match t.kind { .Eof => { is_eof = true; }, _ => {} } if is_eof || kind_is_punct(t.kind, .RBrace) { break; } if !kind_is_punct(t.kind, .Dot) { break; } advance(self); var name = peek(self); if name == null { break; } match name.kind { .Ident => {}, _ => { break; } } advance(self); // stage1 先分配 PATTERN_FIELD,再解析可选子模式。 var fre = new_node(self, .PatternField(.{ .name = name.text, .pattern = 0, }), name); if at_punct(self, .Assign) { advance(self); var sub = parse_pattern_atom(self); var fnode = Ast.node(self.ast, fre); match fnode.body { .PatternField(pf) => { pf.pattern = sub; fnode.body = .PatternField(pf); }, _ => {}, } } match Vec.push(&fields, &fre as *u8) { .Ok => {}, .Err(e) => { ret 0; } } var nt = peek(self); if nt != null && (kind_is_punct(nt.kind, .Comma) || kind_is_punct(nt.kind, .Semicolon)) { advance(self); continue; } break; } expect_punct(self, .RBrace, "'}' in aggregate pattern"); var n = Ast.node(self.ast, ref); match n.body { .PatternAggregate(_) => { n.body = .PatternAggregate(.{ .items = fields }); }, _ => {}, } ret ref; } fn parse_pattern_atom(self: *Parser): usize { var t = peek(self); if t == null { ret 0; } if kind_is_keyword(t.kind, .Underscore) { advance(self); ret new_node(self, .PatternUnderscore, t); } match t.kind { .Int(radix) => { advance(self); var bi = big_int_of(self, t, radix); ret new_node(self, .ExprIntLit(.{ .value = bi }), t); }, .Float => { advance(self); ret new_node(self, .ExprFloatLit(.{ .text = t.text }), t); }, .Char(c) => { advance(self); ret new_node(self, .ExprCharLit(.{ .value = c }), t); }, .Str => { advance(self); ret new_node(self, .ExprStringLit(.{ .data = Ast.push_string(self.ast, decode_str(t.text)) }), t); }, .Keyword(k) => { match k { .True => { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = true }), t); }, .False => { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = false }), t); }, .Null => { advance(self); ret new_node(self, .ExprNullLit, t); }, _ => {}, } }, .Ident => { advance(self); ret new_node(self, .PatternIdent(.{ .name = t.text }), t); }, .Punct(p) => { match p { .Dot => { advance(self); var nx = peek(self); if nx != null && kind_is_punct(nx.kind, .LBrace) { ret parse_pattern_aggregate(self, t); } if nx == null { ret 0; } match nx.kind { .Ident => {}, _ => { ret 0; } } advance(self); // stage1 先分配 PATTERN_DOT,再解析子模式。 var ref = new_node(self, .PatternDot(.{ .name = nx.text, .sub_pattern = 0, }), t); if at_punct(self, .LParen) { advance(self); if !at_punct(self, .RParen) { var sub = parse_pattern(self); var pn = Ast.node(self.ast, ref); match pn.body { .PatternDot(&pd) => { pd.sub_pattern = sub; }, _ => {}, } } expect_punct(self, .RParen, "')' after variant payload"); } ret ref; }, .LParen => { advance(self); // stage1 先分配 PATTERN_TUPLE,再解析内部模式。 var ref = new_node(self, .PatternTuple(.{ .items = ref_vec() }), t); if at_punct(self, .RParen) { advance(self); ret ref; } var inner = parse_pattern(self); expect_punct(self, .RParen, "')' in tuple pattern"); var items = ref_vec(); if inner != 0 { var expanded = false; var inode = Ast.node(self.ast, inner); match inode.body { .PatternList(v) => { items = v.items; expanded = true; }, _ => {}, } if !expanded { match Vec.push(&items, &inner as *u8) { .Ok => {}, .Err(e) => { ret 0; }, } } } var tn = Ast.node(self.ast, ref); match tn.body { .PatternTuple(_) => { tn.body = .PatternTuple(.{ .items = items }); }, _ => {}, } ret ref; }, .And => { // `&name`:解包左值引用绑定。仅允许作用于绑定名。 advance(self); var nx = peek(self); if nx == null { parse_error(self, nx, "expected binding name after '&'"); ret 0; } match nx.kind { .Ident => {}, _ => { parse_error(self, nx, "expected binding name after '&'"); ret 0; }, } var inner = parse_pattern_atom(self); ret new_node(self, .PatternRef(.{ .pat = inner }), t); }, _ => {}, } }, _ => {}, } parse_error(self, t, "unexpected token in pattern"); ret 0; } fn parse_pattern(self: *Parser): usize { var atoms = ref_vec(); while !self.failed { var atom = parse_pattern_atom(self); if atom == 0 { break; } match Vec.push(&atoms, &atom as *u8) { .Ok => {}, .Err(e) => { ret 0; }, } if at_punct(self, .Comma) { advance(self); continue; } break; } if atoms.len == 0 { ret 0; } if atoms.len == 1 { var only: usize = 0; match Vec.get(&atoms, 0) { .Ok(p) => { only = (p as *usize).*; }, .Err(e) => { ret 0; }, } Vec.drop(&atoms); ret only; } ret new_node(self, .PatternList(.{ .items = atoms }), peek(self)); } // ---- 声明 ---- fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool, bang_only: bool) { while !self.failed { var t = peek(self); if t == null { ret; } var is_at = false; if kind_is_punct(t.kind, .At) { is_at = true; } else if kind_is_punct(t.kind, .Sharp) { is_at = false; } else { ret; } if !allow_at && is_at { ret; } if bang_only { // 仅消费 `#!`/`@!` 形式的当前层 tag;遇到普通 `#name` 即停。 var save = self.pos; advance(self); var nt = peek(self); var has_bang = nt != null && kind_is_punct(nt.kind, .Not); self.pos = save; if !has_bang { ret; } } advance(self); t = peek(self); var ext_here = is_external; if t != null && kind_is_punct(t.kind, .Not) { advance(self); ext_here = true; } t = peek(self); if t == null { ret; } match t.kind { .Ident => {}, _ => { parse_error(self, t, "expected tag name"); ret; } } advance(self); // stage1 先分配 tag 节点,再解析参数。 var body: Body = .TagItem(.{ .is_at = is_at, .is_external = ext_here, .name = t.text, .args = ref_vec(), }); if ext_here { body = .ExternalTagItem(.{ .is_at = is_at, .is_external = ext_here, .name = t.text, .args = ref_vec(), }); } var ref = new_node(self, body, t); if at_punct(self, .LParen) { advance(self); var args = parse_expr_list(self); expect_punct(self, .RParen, "')' in tag args"); var n = Ast.node(self.ast, ref); match n.body { .TagItem(x) => { x.args = args; n.body = .TagItem(x); }, .ExternalTagItem(x) => { x.args = args; n.body = .ExternalTagItem(x); }, _ => {}, } } match Vec.push(out, &ref as *u8) { .Ok => {}, .Err(e) => { ret; } } } } fn parse_param_decl_impl(self: *Parser): usize { var attrs = ref_vec(); var is_comptime = false; var c0 = peek(self); if c0 != null && kind_is_keyword(c0.kind, .Comptime) { advance(self); is_comptime = true; } parse_tag_list(self, &attrs, false, true, false); var name = peek(self); if name == null { ret 0; } var ok = false; match name.kind { .Ident => { ok = true; }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Underscore) { ok = true; } }, _ => {}, } if !ok { ret 0; } advance(self); if !expect_punct(self, .Colon, "':' in parameter declaration") { ret 0; } parse_tag_list(self, &attrs, false, false, false); var ty = parse_type_expr(self); ret new_node(self, .ParamDecl(.{ .attr_list = attrs, .is_comptime = is_comptime, .name = name.text, .type_expr = ty }), name); } fn parse_param_list(self: *Parser): Vec { var out = ref_vec(); while !self.failed { var t = peek(self); if t == null { break; } if kind_is_punct(t.kind, .RParen) { break; } var has_at = kind_is_punct(t.kind, .At) || kind_is_punct(t.kind, .Sharp); if has_at { var pd = parse_param_decl_impl(self); if pd != 0 { match Vec.push(&out, &pd as *u8) { .Ok => {}, .Err(e) => { ret out; } } } if at_punct(self, .Comma) { advance(self); continue; } break; } match t.kind { .Ident => { var pd = parse_param_decl_impl(self); if pd != 0 { match Vec.push(&out, &pd as *u8) { .Ok => {}, .Err(e) => { ret out; } } } if at_punct(self, .Comma) { advance(self); continue; } break; }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Comptime) { var pd2 = parse_param_decl_impl(self); if pd2 != 0 { match Vec.push(&out, &pd2 as *u8) { .Ok => {}, .Err(e) => { ret out; } } } if at_punct(self, .Comma) { advance(self); continue; } break; } parse_error(self, t, "unexpected token in parameter list"); break; }, .Punct(p) => { if @enum2int(p) == @enum2int(Punct.Ellipsis) { advance(self); var pd = new_node(self, .ParamDecl(.{ .attr_list = ref_vec(), .is_comptime = false, .name = ByteView.empty(), .type_expr = 0, }), t); match Vec.push(&out, &pd as *u8) { .Ok => {}, .Err(e) => { ret out; } } if at_punct(self, .Comma) { advance(self); } break; } parse_error(self, t, "unexpected token in parameter list"); break; }, _ => { parse_error(self, t, "unexpected token in parameter list"); break; }, } } ret out; } // 该 token 是否可能开始一个类型表达式(用于省略冒号的返回类型)。 fn token_starts_type(t: *Token): bool { if t == null { ret false; } match t.kind { .Ident => { ret true; }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Type) { ret true; } if @enum2int(k) == @enum2int(Keyword.Bool) { ret true; } if @enum2int(k) == @enum2int(Keyword.Any) { ret true; } if @enum2int(k) == @enum2int(Keyword.Fn) { ret true; } if @enum2int(k) == @enum2int(Keyword.Struct) { ret true; } if @enum2int(k) == @enum2int(Keyword.Union) { ret true; } if @enum2int(k) == @enum2int(Keyword.Enum) { ret true; } if @enum2int(k) == @enum2int(Keyword.Shape) { ret true; } }, .Punct(p) => { if @enum2int(p) == @enum2int(Punct.Mul) { ret true; } if @enum2int(p) == @enum2int(Punct.LBracket) { ret true; } if @enum2int(p) == @enum2int(Punct.At) { ret true; } if @enum2int(p) == @enum2int(Punct.Sharp) { ret true; } }, _ => {}, } ret false; } fn parse_fn_decl(self: *Parser, tags: Vec, is_comptime: bool): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Fn) { ret 0; } advance(self); var name = peek(self); if name == null { ret 0; } match name.kind { .Ident => {}, _ => { ret 0; } } advance(self); if !expect_punct(self, .LParen, "'(' in function declaration") { ret 0; } var params = parse_param_list(self); if !expect_punct(self, .RParen, "')' in function declaration") { ret 0; } var ret_type: usize = 0; var t = peek(self); if t != null && kind_is_punct(t.kind, .Colon) { advance(self); ret_type = parse_type_expr(self); t = peek(self); } else if token_starts_type(t) { // 省略冒号:`fn f() T { ... }` ret_type = parse_type_expr(self); t = peek(self); } var body: usize = 0; if is_comptime { self.comptime_depth = self.comptime_depth + 1; } if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); } else if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } else { if is_comptime { self.comptime_depth = self.comptime_depth - 1; } parse_error(self, t, "function declaration must end with ';' or block"); ret 0; } if is_comptime { self.comptime_depth = self.comptime_depth - 1; } ret new_node(self, .FnDecl(.{ .tag_list = tags, .is_comptime = is_comptime, .name = name.text, .params = params, .ret_type = ret_type, .body = body, }), kw); } fn parse_type_decl(self: *Parser, tags: Vec): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Type) { ret 0; } advance(self); var pattern = parse_pattern(self); if pattern == 0 { ret 0; } if !expect_punct(self, .Assign, "'=' in type declaration") { ret 0; } // stage1 先分配 TYPE_DECL 再解析 type_expr(影响 #ref 顺序)。 var ref = new_node(self, .TypeDecl(.{ .attr_list = tags, .pattern = pattern, .type_expr = 0, }), kw); var te = parse_type_expr(self); var node = Ast.node(self.ast, ref); match node.body { .TypeDecl(td) => { td.type_expr = te; node.body = .TypeDecl(td); }, _ => {}, } var t = peek(self); if t != null && (kind_is_punct(t.kind, .Comma) || kind_is_punct(t.kind, .Semicolon)) { advance(self); } ret ref; } fn parse_var_const_decl(self: *Parser, tags: Vec, is_const: bool): usize { var kw = peek(self); if kw == null { ret 0; } if is_const && !kind_is_keyword(kw.kind, .Const) { ret 0; } if !is_const && !kind_is_keyword(kw.kind, .Var) { ret 0; } advance(self); var pattern = parse_pattern(self); if pattern == 0 { ret 0; } var te: usize = 0; var init: usize = 0; var t = peek(self); if t != null && kind_is_punct(t.kind, .Colon) { advance(self); te = parse_type_expr(self); t = peek(self); if t != null && kind_is_punct(t.kind, .Assign) { advance(self); init = parse_expr(self); } } else if t != null && kind_is_punct(t.kind, .Assign) { advance(self); init = parse_expr(self); } var body: Body = .VarDecl(.{ .attr_list = tags, .pattern = pattern, .type_expr = te, .init_expr = init, }); if is_const { body = .ConstDecl(.{ .attr_list = tags, .pattern = pattern, .type_expr = te, .init_expr = init, }); } var ref = new_node(self, body, kw); t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } ret ref; } fn parse_member_decl(self: *Parser, tags: Vec): usize { var name = peek(self); if name == null { ret 0; } match name.kind { .Ident => {}, _ => { ret 0; } } advance(self); var te: usize = 0; var init: usize = 0; var t = peek(self); if t != null && kind_is_punct(t.kind, .Colon) { advance(self); parse_tag_list(self, &tags, false, false, false); te = parse_type_expr(self); t = peek(self); if t != null && kind_is_punct(t.kind, .Assign) { advance(self); init = parse_expr(self); } } else if t != null && kind_is_punct(t.kind, .Assign) { advance(self); init = parse_expr(self); } var ref = new_node(self, .MemberDecl(.{ .attr_list = tags, .name = name.text, .type_expr = te, .init_expr = init, }), name); t = peek(self); if t != null && (kind_is_punct(t.kind, .Comma) || kind_is_punct(t.kind, .Semicolon)) { advance(self); } ret ref; } fn parse_comptime_decl(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; } advance(self); var body: usize = 0; var t = peek(self); self.comptime_depth = self.comptime_depth + 1; if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); } else { body = parse_expr(self); } self.comptime_depth = self.comptime_depth - 1; ret new_node(self, .ComptimeDecl(.{ .body = body }), kw); } fn parse_container_declaration(self: *Parser): usize { var tags = ref_vec(); parse_tag_list(self, &tags, false, false, false); var t = peek(self); if t == null { ret 0; } match t.kind { .Eof => { ret 0; }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Fn) { ret parse_fn_decl(self, tags, false); } if @enum2int(k) == @enum2int(Keyword.Type) { ret parse_type_decl(self, tags); } if @enum2int(k) == @enum2int(Keyword.Var) { ret parse_var_const_decl(self, tags, false); } if @enum2int(k) == @enum2int(Keyword.Const) { ret parse_var_const_decl(self, tags, true); } if @enum2int(k) == @enum2int(Keyword.Comptime) { // `comptime fn ...` -> 标记为编译期函数;否则 `comptime { }` 声明。 var nxt = peek_next(self); if nxt != null && kind_is_keyword(nxt.kind, .Fn) { advance(self); ret parse_fn_decl(self, tags, true); } ret parse_comptime_decl(self); } }, .Ident => { ret parse_member_decl(self, tags); }, .Punct(p) => { if @enum2int(p) == @enum2int(Punct.LBrace) { var block = parse_block(self); ret new_node(self, .TopLevelBlock(.{ .tag_list = tags, .block = block, }), t); } }, _ => {}, } parse_error(self, t, "unrecognized declaration"); advance(self); ret 0; } // ---- 类型 ---- fn parse_type_comptime_call(self: *Parser): usize { var start = peek(self); if start == null { ret 0; } var ok = kind_is_punct(start.kind, .At) || kind_is_punct(start.kind, .Sharp); if !ok { ret 0; } advance(self); var name = peek(self); if name == null { ret 0; } match name.kind { .Ident => {}, _ => { parse_error(self, name, "expected comptime name"); ret 0; } } advance(self); var args = ref_vec(); if at_punct(self, .LParen) { advance(self); args = parse_expr_list(self); expect_punct(self, .RParen, "')' in comptime type"); } ret new_node(self, .TypeComptimeCall(.{ .name = name.text, .args = args }), start); } fn parse_type_base(self: *Parser): usize { var bt = peek(self); if bt == null { ret 0; } if kind_is_keyword(bt.kind, .Fn) { advance(self); if !expect_punct(self, .LParen, "'(' in function type") { ret 0; } // stage1 先分配 TYPE_FN,再解析参数与返回类型。 var ref = new_node(self, .TypeFn(.{ .is_comptime = false, .params = ref_vec(), .ret_type = 0, }), bt); var params = ref_vec(); while !self.failed && !at_punct(self, .RParen) { var pt = parse_type_expr(self); match Vec.push(¶ms, &pt as *u8) { .Ok => {}, .Err(e) => { ret 0; } } if at_punct(self, .Comma) { advance(self); continue; } break; } expect_punct(self, .RParen, "')' in function type"); var ret_ty: usize = 0; if at_punct(self, .Colon) { advance(self); ret_ty = parse_type_expr(self); } var fnode = Ast.node(self.ast, ref); match fnode.body { .TypeFn(f) => { f.params = params; f.ret_type = ret_ty; fnode.body = .TypeFn(f); }, _ => {}, } ret ref; } match bt.kind { .Keyword(k) => { var kind_name: []u8 = ""; if @enum2int(k) == @enum2int(Keyword.Struct) { kind_name = "struct"; } else if @enum2int(k) == @enum2int(Keyword.Union) { kind_name = "union"; } else if @enum2int(k) == @enum2int(Keyword.Enum) { kind_name = "enum"; } else if @enum2int(k) == @enum2int(Keyword.Shape) { kind_name = "shape"; } if kind_name.len != 0 { advance(self); if !expect_punct(self, .LBrace, "'{' in aggregate type") { ret 0; } var members = ref_vec(); var ref = new_node(self, .TypeAggregate(.{ .kind_name = ByteView.from_slice(kind_name), .members = members, }), bt); while !self.failed { var t = peek(self); if t == null { break; } match t.kind { .Eof => { break; }, _ => {} } if kind_is_punct(t.kind, .RBrace) { advance(self); break; } var member = parse_container_declaration(self); if member != 0 { var node = Ast.node(self.ast, ref); match node.body { .TypeAggregate(a) => { match Vec.push(&a.members, &member as *u8) { .Ok => {}, .Err(e) => { ret 0; }, } node.body = .TypeAggregate(a); }, _ => {}, } } t = peek(self); if t != null && (kind_is_punct(t.kind, .Comma) || kind_is_punct(t.kind, .Semicolon)) { advance(self); } } ret ref; } }, _ => {}, } match bt.kind { .Ident => { advance(self); // 类型调用 `Name(T, ...)`(comptime 类型构造,如 `vec(i32)`)。 if at_punct(self, .LParen) { advance(self); var ref = new_node(self, .TypePath(.{ .name = bt.text, .generic_args = ref_vec(), }), bt); var args = ref_vec(); while !self.failed && !at_punct(self, .RParen) { var a = parse_type_expr(self); match Vec.push(&args, &a as *u8) { .Ok => {}, .Err(e) => { ret 0; } } if at_punct(self, .Comma) { advance(self); continue; } break; } expect_punct(self, .RParen, "')' in type call"); var tn = Ast.node(self.ast, ref); match tn.body { .TypePath(tp) => { tp.generic_args = args; tn.body = .TypePath(tp); }, _ => {}, } ret ref; } ret new_node(self, .TypePath(.{ .name = bt.text, .generic_args = ref_vec(), }), bt); }, .Keyword(k) => { // stage1 is_builtin_type_kw:bool / any 作为类型路径;stage3 增加 `type`。 if @enum2int(k) == @enum2int(Keyword.Bool) || @enum2int(k) == @enum2int(Keyword.Any) || @enum2int(k) == @enum2int(Keyword.Type) { advance(self); ret new_node(self, .TypePath(.{ .name = bt.text, .generic_args = ref_vec(), }), bt); } }, .Punct(p) => { if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) { ret parse_type_comptime_call(self); } }, _ => {}, } parse_error(self, bt, "unrecognized type"); ret 0; } fn parse_type_expr(self: *Parser): usize { var t = peek(self); if t == null { ret 0; } if kind_is_punct(t.kind, .Mul) { advance(self); // stage1 先分配 POINTER,再解析 pointee。 var ref = new_node(self, .TypePointer(.{ .pointee = 0 }), t); var pointee = parse_type_expr(self); var pn = Ast.node(self.ast, ref); match pn.body { .TypePointer(p) => { p.pointee = pointee; pn.body = .TypePointer(p); }, _ => {}, } ret ref; } if kind_is_punct(t.kind, .LBracket) { advance(self); var has_len = false; var is_underscore = false; var len: usize = 0; t = peek(self); if t != null { match t.kind { .Int(radix) => { len = int_token_u64(self, t, radix) as usize; has_len = true; advance(self); }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Underscore) { has_len = true; is_underscore = true; advance(self); } }, _ => {}, } } expect_punct(self, .RBracket, "']' in array type"); var elem = parse_type_expr(self); if has_len { ret new_node(self, .TypeArray(.{ .has_len = true, .len = len as isize, .is_underscore_len = is_underscore, .elem_type = elem, }), t); } ret new_node(self, .TypeSlice(.{ .elem_type = elem }), t); } if kind_is_punct(t.kind, .LParen) { advance(self); var types = ref_vec(); if at_punct(self, .RParen) { advance(self); ret new_node(self, .TypeParen(.{ .types = types }), t); } var first = parse_type_expr(self); match Vec.push(&types, &first as *u8) { .Ok => {}, .Err(e) => { ret 0; } } while at_punct(self, .Comma) { advance(self); if at_punct(self, .RParen) { break; } var next = parse_type_expr(self); match Vec.push(&types, &next as *u8) { .Ok => {}, .Err(e) => { ret 0; } } } expect_punct(self, .RParen, "')' in parenthesized type"); if types.len == 1 { Vec.drop(&types); ret first; } ret new_node(self, .TypeParen(.{ .types = types }), t); } var base = parse_type_base(self); if self.failed || base == 0 { ret base; } while at_punct(self, .Dot) { var dot = peek(self); advance(self); var seg = peek(self); if seg == null { ret base; } match seg.kind { .Ident => {}, _ => { parse_error(self, seg, "type: expected identifier after '.'"); ret base; } } advance(self); base = new_node(self, .ExprField(.{ .obj = base, .name = seg.text }), dot); } ret base; } // ---- 表达式 ---- type BinInfo = struct { op: Punct, prec: i32, right: bool } type BinResult = enum { No, Yes: BinInfo } // 只判断优先级/结合性;算子本体就是 Punct 自身。 fn binop_of(p: Punct): BinResult { var prec: i32 = 0; var right = false; match p { .Assign, .AddAssign, .SubAssign, .MulAssign, .DivAssign, .ModAssign, .AndAssign, .OrAssign, .XorAssign, .LShAssign, .RShAssign => { prec = 1; right = true; }, .OrOr => { prec = 2; }, .AndAnd => { prec = 3; }, .Or => { prec = 4; }, .Xor => { prec = 5; }, .And => { prec = 6; }, .Eq, .Neq => { prec = 7; }, .Le, .Ge, .Lt, .Gt => { prec = 8; }, .LeftArrow, .RightArrow, .ArrowBoth, .FatArrowBoth, .RightFatArrow => { prec = 9; }, .Range => { prec = 10; }, .LSh, .RSh => { prec = 11; }, .Add, .Sub => { prec = 12; }, .Mul, .Div, .Mod => { prec = 13; }, _ => { ret .No; }, } ret .Yes(BinInfo { .op = p, .prec = prec, .right = right }); } fn mk_bin(self: *Parser, loc: *Token, op: Punct, left: usize, right: usize): usize { ret new_node(self, .ExprBinary(.{ .op = op, .left = left, .right = right }), loc); } fn mk_unary(self: *Parser, loc: *Token, op: Punct, operand: usize): usize { ret new_node(self, .ExprUnary(.{ .op = op, .operand = operand }), loc); } fn parse_expr_prec(self: *Parser, min_prec: i32): usize { var left = parse_cast(self); if self.failed { ret left; } while !self.failed { var t = peek(self); if t == null { break; } var info: BinInfo = undefined; var found = false; match t.kind { .Punct(p) => { match binop_of(p) { .Yes(bi) => { info = bi; found = true; }, .No => {}, } }, _ => {}, } if !found { break; } if info.prec < min_prec { break; } advance(self); if @enum2int(info.op) == @enum2int(Punct.Range) { var right: usize = 0; var nx = peek(self); if nx != null && can_start_expr(nx.kind) { right = parse_expr_prec(self, info.prec + 1); } left = mk_bin(self, t, info.op, left, right); continue; } var next_min = info.prec + 1; if info.right { next_min = info.prec; } var right = parse_expr_prec(self, next_min); left = mk_bin(self, t, info.op, left, right); } ret left; } fn parse_expr(self: *Parser): usize { var e = parse_expr_prec(self, 1); // `expr catch `:catch 用解包语法绑定错误。 while !self.failed { var t = peek(self); if t == null { break; } if !kind_is_keyword(t.kind, .Catch) { break; } advance(self); var pat = parse_pattern(self); if pat == 0 { ret e; } var handler = parse_block(self); e = new_node(self, .ExprCatch(.{ .value = e, .pattern = pat, .handler = handler }), t); } ret e; } fn parse_cast(self: *Parser): usize { var expr = parse_prefix(self); while !self.failed { var t = peek(self); if t == null { break; } if !kind_is_keyword(t.kind, .As) { break; } advance(self); var ty = parse_type_expr(self); expr = new_node(self, .ExprCast(.{ .expr = expr, .type_expr = ty }), t); } ret expr; } fn parse_prefix(self: *Parser): usize { var t = peek(self); if t == null { ret 0; } if kind_is_punct(t.kind, .Dollar) { var label = parse_label(self); var kw = peek(self); var ref: usize = 0; if kw != null && kind_is_keyword(kw.kind, .While) { ref = parse_while_expr(self); } else if kw != null && kind_is_keyword(kw.kind, .Loop) { ref = parse_loop_expr(self); } else if kw != null && kind_is_keyword(kw.kind, .For) { ref = parse_for_expr(self); } else { parse_error(self, kw, "expected while/loop/for after label"); ret 0; } if ref == 0 { ret 0; } var n = Ast.node(self.ast, ref); match n.body { .ExprWhile(&w) => { w.label = label; }, .ExprLoop(&l) => { l.label = label; }, .ExprFor(&f) => { f.label = label; }, _ => {}, } ret ref; } // 一元算子复用 Punct:- ! ~ &;解引用 .* 见 parse_postfix(用 .Mul)。 var uop: Punct = .Sub; var is_unary = false; match t.kind { .Punct(p) => { var ok = false; match p { .Sub => { ok = true; }, .Not => { ok = true; }, .BitNot => { ok = true; }, .And => { ok = true; }, _ => {}, } if ok { uop = p; is_unary = true; } }, _ => {}, } if !is_unary { ret parse_postfix(self); } advance(self); var operand = parse_prefix(self); ret mk_unary(self, t, uop, operand); } fn parse_postfix(self: *Parser): usize { var expr = parse_primary(self); while !self.failed { var t = peek(self); if t == null { break; } if kind_is_punct(t.kind, .LParen) { advance(self); // stage1 先分配 CALL,再解析实参(影响 #ref 顺序)。 var ref = new_node(self, .ExprCall(.{ .callee = expr, .args = ref_vec() }), t); var args = parse_expr_list(self); var cn = Ast.node(self.ast, ref); match cn.body { .ExprCall(c) => { c.args = args; cn.body = .ExprCall(c); }, _ => {}, } if !expect_punct(self, .RParen, "')' in call") { ret expr; } expr = ref; } else if kind_is_punct(t.kind, .Dot) { advance(self); var next = peek(self); if next == null { break; } var is_ident = false; match next.kind { .Ident => { is_ident = true; }, _ => {} } if is_ident { advance(self); expr = new_node(self, .ExprField(.{ .obj = expr, .name = next.text }), t); } else if kind_is_punct(next.kind, .Mul) { advance(self); expr = mk_unary(self, t, .Mul, expr); // 一元上下文:Mul 表示 .* 解引用 } else { parse_error(self, next, "expected identifier or '*' after '.'"); break; } } else if kind_is_punct(t.kind, .LBracket) { advance(self); var begin = parse_expr_prec(self, 11); if at_punct(self, .Range) { advance(self); var end: usize = 0; if !at_punct(self, .RBracket) { end = parse_expr(self); } if !expect_punct(self, .RBracket, "']' in slice") { ret expr; } expr = new_node(self, .ExprSlice(.{ .obj = expr, .begin = begin, .end = end, }), t); } else { if !expect_punct(self, .RBracket, "']' in index") { ret expr; } expr = new_node(self, .ExprIndex(.{ .obj = expr, .index = begin }), t); } } else { break; } } ret expr; } fn parse_variant_construct(self: *Parser, dot: *Token): usize { var name = advance(self); var field = new_node(self, .StructInitItem(.{ .name = name.text, .expr = 0 }), name); if at_punct(self, .LParen) { advance(self); var args = ref_vec(); if !at_punct(self, .RParen) { args = parse_expr_list(self); } expect_punct(self, .RParen, "')' after variant payload"); var expr: usize = 0; if args.len == 1 { match Vec.get(&args, 0) { .Ok(p) => { expr = (p as *usize).*; }, .Err(e) => {}, } Vec.drop(&args); } else { expr = new_node(self, .ExprTuple(.{ .items = args }), name); } var fnode = Ast.node(self.ast, field); match fnode.body { .StructInitItem(s) => { s.expr = expr; fnode.body = .StructInitItem(s); }, _ => {}, } } var fields = ref_vec(); match Vec.push(&fields, &field as *u8) { .Ok => {}, .Err(e) => { ret 0; } } ret new_node(self, .ExprAggregateLit(.{ .name = ByteView.empty(), .fields = fields, }), dot); } fn parse_primary(self: *Parser): usize { var t = peek(self); if t == null { ret 0; } match t.kind { .Int(radix) => { advance(self); var bi = big_int_of(self, t, radix); ret new_node(self, .ExprIntLit(.{ .value = bi }), t); }, .Float => { advance(self); ret new_node(self, .ExprFloatLit(.{ .text = t.text }), t); }, .Char(c) => { advance(self); ret new_node(self, .ExprCharLit(.{ .value = c }), t); }, .Str => { advance(self); ret new_node(self, .ExprStringLit(.{ .data = Ast.push_string(self.ast, decode_str(t.text)) }), t); }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.True) { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = true }), t); } if @enum2int(k) == @enum2int(Keyword.False) { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = false }), t); } if @enum2int(k) == @enum2int(Keyword.Null) { advance(self); ret new_node(self, .ExprNullLit, t); } if @enum2int(k) == @enum2int(Keyword.Undefined) { advance(self); ret new_node(self, .ExprUndefinedLit, t); } if @enum2int(k) == @enum2int(Keyword.If) { ret parse_if_expr(self); } if @enum2int(k) == @enum2int(Keyword.While) { ret parse_while_expr(self); } if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); } if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); } if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); } // `try `:错误上抛前缀。 if @enum2int(k) == @enum2int(Keyword.Try) { advance(self); var tv = parse_expr(self); ret new_node(self, .ExprTry(.{ .value = tv }), t); } // `comptime `:编译期表达式(可调用普通函数、返回类型等)。 if @enum2int(k) == @enum2int(Keyword.Comptime) { advance(self); var cbody = parse_expr(self); ret new_node(self, .ExprComptime(.{ .body = cbody }), t); } // 类型即表达式:仅在 comptime 上下文内允许 `struct { ... }` 等聚合类型。 if self.comptime_depth > 0 { if @enum2int(k) == @enum2int(Keyword.Struct) || @enum2int(k) == @enum2int(Keyword.Union) || @enum2int(k) == @enum2int(Keyword.Enum) || @enum2int(k) == @enum2int(Keyword.Shape) { ret parse_type_base(self); } } parse_error(self, t, "unrecognized expression"); ret 0; }, .Ident => { advance(self); if at_punct(self, .LBrace) && self.stop_agg == 0 { // stage1 先分配 AGGREGATE_LIT,再解析字段。 var ref = new_node(self, .ExprAggregateLit(.{ .name = t.text, .fields = ref_vec(), }), t); var fields = parse_aggregate_fields(self); var an = Ast.node(self.ast, ref); match an.body { .ExprAggregateLit(a) => { a.fields = fields; an.body = .ExprAggregateLit(a); }, _ => {}, } ret ref; } ret new_node(self, .ExprIdent(.{ .name = t.text }), t); }, .Punct(p) => { if @enum2int(p) == @enum2int(Punct.Dot) { advance(self); var nx = peek(self); if nx == null { parse_error(self, t, "unexpected '.'"); ret 0; } if kind_is_punct(nx.kind, .LBrace) && self.stop_agg == 0 { var ref = new_node(self, .ExprAggregateLit(.{ .name = ByteView.empty(), .fields = ref_vec(), }), t); var fields = parse_aggregate_fields(self); var an = Ast.node(self.ast, ref); match an.body { .ExprAggregateLit(a) => { a.fields = fields; an.body = .ExprAggregateLit(a); }, _ => {}, } ret ref; } if kind_is_punct(nx.kind, .LBracket) { advance(self); var elems = parse_expr_list(self); if !expect_punct(self, .RBracket, "']' in array literal") { ret 0; } ret new_node(self, .ExprArrayLit(.{ .has_len = false, .len = 0, .is_underscore_len = false, .elem_type = 0, .elems = elems, }), t); } var is_ident = false; match nx.kind { .Ident => { is_ident = true; }, _ => {} } if is_ident { ret parse_variant_construct(self, t); } parse_error(self, nx, "expected '{', '[' or identifier after '.'"); ret 0; } if @enum2int(p) == @enum2int(Punct.LBracket) { ret parse_array_literal(self); } if @enum2int(p) == @enum2int(Punct.LBrace) { ret parse_block(self); } if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) { ret parse_comptime_call(self); } if @enum2int(p) == @enum2int(Punct.LParen) { advance(self); var elems = ref_vec(); if at_punct(self, .RParen) { advance(self); ret new_node(self, .ExprTuple(.{ .items = elems }), t); } var first = parse_expr(self); if at_punct(self, .Comma) { match Vec.push(&elems, &first as *u8) { .Ok => {}, .Err(e) => { ret 0; } } while at_punct(self, .Comma) { advance(self); if at_punct(self, .RParen) { break; } var nx = parse_expr(self); match Vec.push(&elems, &nx as *u8) { .Ok => {}, .Err(e) => { ret 0; } } } expect_punct(self, .RParen, "')' in tuple literal"); ret new_node(self, .ExprTuple(.{ .items = elems }), t); } expect_punct(self, .RParen, "')' in paren expression"); Vec.drop(&elems); ret first; } }, _ => {}, } parse_error(self, t, "unrecognized expression"); ret 0; } fn parse_aggregate_fields(self: *Parser): Vec { var fields = ref_vec(); if !expect_punct(self, .LBrace, "'{' in aggregate literal") { ret fields; } while !self.failed { var t = peek(self); if t == null { break; } var stop = false; match t.kind { .Eof => { stop = true; }, _ => {} } if kind_is_punct(t.kind, .RBrace) { stop = true; } if stop { break; } if !kind_is_punct(t.kind, .Dot) { break; } advance(self); var name = peek(self); if name == null { break; } match name.kind { .Ident => {}, _ => { break; } } advance(self); var item = new_node(self, .StructInitItem(.{ .name = name.text, .expr = 0 }), name); if at_punct(self, .Assign) { advance(self); var e = parse_expr(self); var n = Ast.node(self.ast, item); match n.body { .StructInitItem(s) => { s.expr = e; n.body = .StructInitItem(s); }, _ => {}, } } match Vec.push(&fields, &item as *u8) { .Ok => {}, .Err(e) => { ret fields; } } var nt = peek(self); if nt != null && (kind_is_punct(nt.kind, .Comma) || kind_is_punct(nt.kind, .Semicolon)) { advance(self); continue; } break; } expect_punct(self, .RBrace, "'}' in aggregate literal"); ret fields; } fn parse_array_literal(self: *Parser): usize { var start = peek(self); if start == null { ret 0; } if !kind_is_punct(start.kind, .LBracket) { ret 0; } advance(self); var has_len = false; var len: usize = 0; var is_underscore = false; var t = peek(self); if t != null { match t.kind { .Int(radix) => { len = int_token_u64(self, t, radix) as usize; has_len = true; advance(self); }, .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Underscore) { has_len = true; is_underscore = true; advance(self); } }, _ => {}, } } if !expect_punct(self, .RBracket, "']' in array literal") { ret 0; } var elem_type = parse_type_expr(self); if !expect_punct(self, .LBracket, "'[' in array literal") { ret 0; } var elems = parse_expr_list(self); if !expect_punct(self, .RBracket, "']' in array literal") { ret 0; } ret new_node(self, .ExprArrayLit(.{ .has_len = has_len, .len = len, .is_underscore_len = is_underscore, .elem_type = elem_type, .elems = elems, }), start); } fn parse_comptime_call(self: *Parser): usize { var start = peek(self); if start == null { ret 0; } var ok = kind_is_punct(start.kind, .At) || kind_is_punct(start.kind, .Sharp); if !ok { ret 0; } advance(self); var name = peek(self); if name == null { ret 0; } match name.kind { .Ident => {}, _ => { parse_error(self, name, "expected comptime name"); ret 0; } } advance(self); var args = ref_vec(); if at_punct(self, .LParen) { advance(self); args = parse_expr_list(self); if !expect_punct(self, .RParen, "')' in comptime call") { ret 0; } } // `@name(args){ ... }`:构造该编译期类型的值(聚合初始化)。 if at_punct(self, .LBrace) && self.stop_agg == 0 { var fields = parse_aggregate_fields(self); ret new_node(self, .ExprComptimeInit(.{ .name = name.text, .args = args, .fields = fields, }), start); } ret new_node(self, .ExprComptimeCall(.{ .name = name.text, .args = args }), start); } fn parse_label(self: *Parser): usize { var t = peek(self); if t == null { ret 0; } if !kind_is_punct(t.kind, .Dollar) { ret 0; } advance(self); var name = peek(self); if name == null { ret 0; } match name.kind { .Ident => {}, _ => { parse_error(self, name, "expected identifier after '$'"); ret 0; } } advance(self); ret new_node(self, .Label(.{ .name = name.text }), t); } fn parse_expr_list(self: *Parser): Vec { var vec = ref_vec(); if self.failed { ret vec; } var t = peek(self); if t == null || !can_start_expr(t.kind) { ret vec; } var first = parse_expr(self); match Vec.push(&vec, &first as *u8) { .Ok => {}, .Err(e) => { ret vec; } } while !self.failed { var nt = peek(self); if nt == null || !kind_is_punct(nt.kind, .Comma) { break; } advance(self); var nx = peek(self); if nx == null || !can_start_expr(nx.kind) { break; } var e = parse_expr(self); match Vec.push(&vec, &e as *u8) { .Ok => {}, .Err(er) => { ret vec; } } } ret vec; } // ---- if / while / loop / for / match ---- fn parse_if_expr(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .If) { ret 0; } advance(self); var is_ifvar = false; var var_decl: usize = 0; var t = peek(self); if t != null && kind_is_keyword(t.kind, .Var) { advance(self); is_ifvar = true; var pattern = parse_pattern(self); if pattern == 0 { ret 0; } var te: usize = 0; var init: usize = 0; t = peek(self); if t != null && kind_is_punct(t.kind, .Colon) { advance(self); te = parse_type_expr(self); t = peek(self); if t != null && kind_is_punct(t.kind, .Assign) { advance(self); self.stop_agg = self.stop_agg + 1; init = parse_expr(self); self.stop_agg = self.stop_agg - 1; } else { parse_error(self, t, "expected '=' in if var"); ret 0; } } else if t != null && kind_is_punct(t.kind, .Assign) { advance(self); self.stop_agg = self.stop_agg + 1; init = parse_expr(self); self.stop_agg = self.stop_agg - 1; } else { parse_error(self, t, "expected type annotation or '=' in if var"); ret 0; } var_decl = new_node(self, .VarDecl(.{ .attr_list = ref_vec(), .pattern = pattern, .type_expr = te, .init_expr = init, }), kw); } else { self.stop_agg = self.stop_agg + 1; var_decl = parse_expr(self); self.stop_agg = self.stop_agg - 1; if var_decl == 0 { ret 0; } } var then_block = parse_block(self); if then_block == 0 { ret 0; } var else_branch: usize = 0; t = peek(self); if t != null && kind_is_keyword(t.kind, .Else) { advance(self); t = peek(self); if t != null && kind_is_keyword(t.kind, .If) { else_branch = parse_if_expr(self); } else { else_branch = parse_block(self); } } if is_ifvar { ret new_node(self, .ExprIfvar(.{ .var_decl = var_decl, .then_block = then_block, .else_branch = else_branch, }), kw); } ret new_node(self, .ExprIf(.{ .cond = var_decl, .then_block = then_block, .else_branch = else_branch, }), kw); } fn parse_while_expr(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .While) { ret 0; } advance(self); self.stop_agg = self.stop_agg + 1; var cond = parse_expr(self); self.stop_agg = self.stop_agg - 1; if cond == 0 { ret 0; } var body = parse_block(self); if body == 0 { ret 0; } var else_branch: usize = 0; if at_keyword(self, .Else) { advance(self); if at_punct(self, .LBrace) { else_branch = parse_block(self); } else { else_branch = parse_expr(self); } } ret new_node(self, .ExprWhile(.{ .label = 0, .cond = cond, .body = body, .else_branch = else_branch, }), kw); } fn parse_loop_expr(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Loop) { ret 0; } advance(self); var body = parse_block(self); ret new_node(self, .ExprLoop(.{ .label = 0, .body = body }), kw); } fn parse_for_expr(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .For) { ret 0; } advance(self); var pattern = parse_pattern(self); if pattern == 0 { ret 0; } if !expect_punct(self, .Colon, "':' after for pattern") { ret 0; } var iter_exprs = ref_vec(); self.stop_agg = self.stop_agg + 1; var e0 = parse_expr(self); match Vec.push(&iter_exprs, &e0 as *u8) { .Ok => {}, .Err(er) => { ret 0; } } while at_punct(self, .Comma) { advance(self); var e = parse_expr(self); match Vec.push(&iter_exprs, &e as *u8) { .Ok => {}, .Err(er) => { ret 0; } } } self.stop_agg = self.stop_agg - 1; var body = parse_block(self); if body == 0 { ret 0; } var else_branch: usize = 0; if at_keyword(self, .Else) { advance(self); if at_punct(self, .LBrace) { else_branch = parse_block(self); } else { else_branch = parse_expr(self); } } ret new_node(self, .ExprFor(.{ .label = 0, .iter_exprs = iter_exprs, .pattern = pattern, .body = body, .else_branch = else_branch, }), kw); } fn parse_match_expr(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Match) { ret 0; } advance(self); self.stop_agg = self.stop_agg + 1; var matched = parse_expr(self); self.stop_agg = self.stop_agg - 1; if !expect_punct(self, .LBrace, "'{' in match") { ret 0; } var patterns = ref_vec(); var bodies = ref_vec(); while !self.failed { var t = peek(self); if t == null { break; } var stop = false; match t.kind { .Eof => { stop = true; }, _ => {} } if kind_is_punct(t.kind, .RBrace) { stop = true; } if stop { break; } var pat = parse_pattern(self); if pat == 0 { break; } if !expect_punct(self, .RightFatArrow, "'=>' in match arm") { break; } var body = parse_block(self); match Vec.push(&patterns, &pat as *u8) { .Ok => {}, .Err(e) => { ret 0; } } match Vec.push(&bodies, &body as *u8) { .Ok => {}, .Err(e) => { ret 0; } } if at_punct(self, .Comma) { advance(self); } } if !expect_punct(self, .RBrace, "'}' in match") { ret 0; } // stage1 先分配 MATCH_EXPR,再逐个分配 MATCH_ARM。 var ref = new_node(self, .ExprMatch(.{ .matched_expr = matched, .arms = ref_vec(), }), kw); var arms = ref_vec(); var i: usize = 0; while i < patterns.len { var pv: usize = 0; var bv: usize = 0; match Vec.get(&patterns, i) { .Ok(p) => { pv = (p as *usize).*; }, .Err(e) => {} } match Vec.get(&bodies, i) { .Ok(p) => { bv = (p as *usize).*; }, .Err(e) => {} } var arm = new_node(self, .MatchArm(.{ .pattern = pv, .body = bv }), null); match Vec.push(&arms, &arm as *u8) { .Ok => {}, .Err(e) => { ret 0; } } i = i + 1; } Vec.drop(&patterns); Vec.drop(&bodies); var mn = Ast.node(self.ast, ref); match mn.body { .ExprMatch(m) => { m.arms = arms; mn.body = .ExprMatch(m); }, _ => {}, } ret ref; } // ---- 语句 ---- fn parse_ret_stmt(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Ret) { ret 0; } advance(self); var expr: usize = 0; var t = peek(self); var can = false; if t != null { can = can_start_expr(t.kind); // comptime 上下文:允许返回类型表达式(如 `ret struct { ... };`)。 if !can && self.comptime_depth > 0 { if kind_is_keyword(t.kind, .Struct) || kind_is_keyword(t.kind, .Union) || kind_is_keyword(t.kind, .Enum) || kind_is_keyword(t.kind, .Shape) { can = true; } } } if can { expr = parse_expr(self); } t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } ret new_node(self, .StatementRet(.{ .expr = expr }), kw); } fn parse_break_stmt(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Break) { ret 0; } advance(self); var label = parse_label(self); var expr: usize = 0; var t = peek(self); if t != null && can_start_expr(t.kind) { expr = parse_expr(self); } t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } ret new_node(self, .StatementBreak(.{ .label = label, .expr = expr }), kw); } fn parse_continue_stmt(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Continue) { ret 0; } advance(self); var label = parse_label(self); var t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } ret new_node(self, .StatementContinue(.{ .label = label }), kw); } fn parse_defer_stmt(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Defer) { ret 0; } advance(self); var body: usize = 0; var t = peek(self); if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); } else if t != null && can_start_statement(t.kind) { body = parse_statement(self); } else { body = parse_expr(self); t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } } ret new_node(self, .StatementDefer(.{ .body = body }), kw); } fn parse_errdefer_stmt(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Errdefer) { ret 0; } advance(self); var body: usize = 0; var t = peek(self); if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); } else if t != null && can_start_statement(t.kind) { body = parse_statement(self); } else { body = parse_expr(self); t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); } } ret new_node(self, .StatementErrdefer(.{ .body = body }), kw); } fn parse_comptime_stmt(self: *Parser): usize { var kw = peek(self); if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; } advance(self); var body: usize = 0; var t = peek(self); if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); } else { body = parse_expr(self); } ret new_node(self, .StatementComptime(.{ .body = body }), kw); } fn parse_statement(self: *Parser): usize { var t = peek(self); if t == null { ret 0; } match t.kind { .Keyword(k) => { if @enum2int(k) == @enum2int(Keyword.Ret) { ret parse_ret_stmt(self); } if @enum2int(k) == @enum2int(Keyword.Break) { ret parse_break_stmt(self); } if @enum2int(k) == @enum2int(Keyword.Continue) { ret parse_continue_stmt(self); } if @enum2int(k) == @enum2int(Keyword.Defer) { ret parse_defer_stmt(self); } if @enum2int(k) == @enum2int(Keyword.Errdefer) { ret parse_errdefer_stmt(self); } if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_stmt(self); } if @enum2int(k) == @enum2int(Keyword.Var) || @enum2int(k) == @enum2int(Keyword.Const) || @enum2int(k) == @enum2int(Keyword.Type) { var tags = ref_vec(); parse_tag_list(self, &tags, false, false, false); var decl: usize = 0; if @enum2int(k) == @enum2int(Keyword.Var) { decl = parse_var_const_decl(self, tags, false); } else if @enum2int(k) == @enum2int(Keyword.Const) { decl = parse_var_const_decl(self, tags, true); } else { decl = parse_type_decl(self, tags); } if decl == 0 { ret 0; } ret new_node(self, .StatementDecl(.{ .decl = decl }), t); } }, _ => {}, } if can_start_expr(t.kind) { var expr = parse_expr(self); if expr == 0 { ret 0; } ret new_node(self, .StatementExpr(.{ .expr = expr }), t); } parse_error(self, t, "unrecognized statement"); ret 0; } fn parse_block(self: *Parser): usize { var start = peek(self); if start == null { ret 0; } if !kind_is_punct(start.kind, .LBrace) { parse_error(self, start, "expected '{'"); ret 0; } advance(self); var ref = new_node(self, .ExprBlock(.{ .stmts = ref_vec(), .tail_expr = 0 }), start); while !self.failed { var t = peek(self); if t == null { break; } var is_eof = false; match t.kind { .Eof => { is_eof = true; }, _ => {} } if is_eof { parse_error(self, t, "block missing '}'"); ret ref; } if kind_is_punct(t.kind, .RBrace) { advance(self); break; } var before = self.pos; if can_start_statement(t.kind) { var stmt = parse_statement(self); if stmt != 0 { block_push_stmt(self, ref, stmt); } else { if self.pos == before { advance(self); } skip_stmt_sync(self); } } else if can_start_expr(t.kind) { var expr = parse_expr(self); if expr == 0 { if self.pos == before { advance(self); } skip_stmt_sync(self); } else { t = peek(self); if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); block_push_stmt(self, ref, expr); } else if t != null && kind_is_punct(t.kind, .RBrace) { block_set_tail(self, ref, expr); } else { var block_like = false; var n = Ast.node(self.ast, expr); match n.body { .ExprBlock(_) => { block_like = true; }, .ExprIf(_) => { block_like = true; }, .ExprIfvar(_) => { block_like = true; }, .ExprWhile(_) => { block_like = true; }, .ExprLoop(_) => { block_like = true; }, .ExprFor(_) => { block_like = true; }, .ExprMatch(_) => { block_like = true; }, _ => {}, } if block_like { block_push_stmt(self, ref, expr); } else { parse_error(self, t, "expected ';' or '}' after expression"); if self.pos == before { advance(self); } skip_stmt_sync(self); } } } } else { parse_error(self, t, "unrecognized token in block"); if self.pos == before { advance(self); } skip_stmt_sync(self); } } ret ref; } // 块内错误恢复:同步到 ';'(消费)或 '}' / EOF(不消费)。 fn skip_stmt_sync(self: *Parser) { while !self.failed { var t = peek(self); if t == null { ret; } if kind_is_punct(t.kind, .RBrace) { ret; } var is_eof = false; match t.kind { .Eof => { is_eof = true; }, _ => {} } if is_eof { ret; } if kind_is_punct(t.kind, .Semicolon) { advance(self); ret; } advance(self); } } fn block_push_stmt(self: *Parser, ref: usize, stmt: usize) { var n = Ast.node(self.ast, ref); match n.body { .ExprBlock(b) => { match Vec.push(&b.stmts, &stmt as *u8) { .Ok => {}, .Err(e) => {} } n.body = .ExprBlock(b); }, _ => {}, } } fn block_set_tail(self: *Parser, ref: usize, expr: usize) { var n = Ast.node(self.ast, ref); match n.body { .ExprBlock(b) => { if b.tail_expr != 0 { parse_error(self, peek(self), "only one tail expression allowed"); } b.tail_expr = expr; n.body = .ExprBlock(b); }, _ => {}, } } // ---- 入口 ---- fn skip_to_sync(self: *Parser) { while !self.failed { var t = peek(self); if t == null { ret; } match t.kind { .Eof => { ret; }, _ => {} } if kind_is_punct(t.kind, .Semicolon) || kind_is_punct(t.kind, .RBrace) { advance(self); ret; } advance(self); } } // 解析整个文件;Root/ContainerMembers 最后分配(与 stage1 一致)。 fn parse(self: *Parser) { var external_tags = ref_vec(); var declarations = ref_vec(); while !self.failed { var t = peek(self); if t == null { break; } match t.kind { .Eof => { break; }, _ => {} } var is_at = kind_is_punct(t.kind, .At) || kind_is_punct(t.kind, .Sharp); if is_at { var save = self.pos; self.pos = self.pos + 1; var t2 = peek(self); self.pos = save; if t2 != null && kind_is_punct(t2.kind, .Not) { parse_tag_list(self, &external_tags, true, true, true); continue; } } var decl = parse_container_declaration(self); if decl != 0 { match Vec.push(&declarations, &decl as *u8) { .Ok => {}, .Err(e) => { ret; } } } else if !self.failed { var at = peek(self); if at != null { var is_eof = false; match at.kind { .Eof => { is_eof = true; }, _ => {} } if is_eof { break; } skip_to_sync(self); } else { break; } } } var cm = new_node(self, .ContainerMembers(.{ .external_tags = external_tags, .declarations = declarations, }), null); var root = new_node(self, .Root(.{ .members = cm }), null); self.ast.root = root; } // ---- 测试 ---- fn lex_tokens(src: []u8): Vec { var out = Vec.blank(@sizeof(Token), @alignof(Token)); match Lexer.lex(ByteView.from_slice(src), null, 0) { .Ok(v) => { out = v; }, .Err(error) => { @assert(false, "lex failed"); }, } ret out; } fn test_peek_advance() { var toks = lex_tokens("fn main() {\n ret 0;\n}\n"); defer { Vec.drop(&toks); } var ast: Ast = undefined; match Ast.new(null) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new"); }, } defer { Ast.drop(&ast); } var p = new(&ast, toks, null); @assert(at_keyword(&p, .Fn), "peek fn"); var t = advance(&p); @assert(kind_is_keyword(t.kind, .Fn), "advanced fn"); var id = peek(&p); match id.kind { .Ident => {}, _ => { @assert(false, "expected ident"); }, } advance(&p); @assert(expect_punct(&p, .LParen, "expected ("), "expect lparen"); @assert(expect_punct(&p, .RParen, "expected )"), "expect rparen"); @assert(!expect_punct(&p, .Semicolon, "expected ;"), "wrong punct errors"); @assert(p.error_count == 1, "one error recorded"); } fn test_new_node_span() { var toks = lex_tokens("ret"); defer { Vec.drop(&toks); } var ast: Ast = undefined; match Ast.new(null) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new"); }, } defer { Ast.drop(&ast); } var p = new(&ast, toks, null); var t = peek(&p); var r = new_node(&p, .ExprNullLit, t); var n = Ast.node(&ast, r); @assert(n.span.file == 0 && n.span.start == 0 && n.span.end == 3, "node span"); } fn expect_text(buf: *ByteBuffer, expected: []u8) { var actual = ByteBuffer.as_slice(buf.*); @assert(actual.len == expected.len, "dump length"); var i: usize = 0; while i < expected.len { @assert(actual.ptr[i] == expected.ptr[i], "dump byte"); i = i + 1; } } // 与 stage1 `splc0 --dump ast` 输出逐字节一致(含 #ref 与 [line:col])。 fn test_parse_decls() { var src: []u8 = "type Point = struct { x: i32, y: i32 };\nfn f(a: i32): i32;\n"; var toks = lex_tokens(src); defer { Vec.drop(&toks); } var sources = set_new(); defer { set_drop(&sources); } match set_add(&sources, ByteView.from_slice("test.spl"), ByteView.from_slice(src)) { .Ok(index) => {}, .Err(error) => { @assert(false, "set_add"); }, } var ast: Ast = undefined; match Ast.new(&sources) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new"); }, } defer { Ast.drop(&ast); } var p = new(&ast, toks, null); parse(&p); @assert(!p.failed, "parse ok"); @assert(p.error_count == 0, "no errors"); var buf = ByteBuffer.new(); defer { ByteBuffer.drop(&buf); } @assert(Ast.dump(&ast, &buf), "dump"); expect_text(&buf, "AST:\n`-root #13\n `-container_members #12\n |-type_decl #2 [1:1]\n | |-pattern_ident #1 [1:6]\n | `-type_aggregate #3 [1:14]\n | |-member_decl #5 [1:23]\n | | |-type_path #4 [1:26]\n | `-member_decl #7 [1:31]\n | |-type_path #6 [1:34]\n `-fn_decl #11 [2:1]\n `-param_decl #9 [2:6]\n `-type_path #8 [2:9]\n |-type_path #10 [2:15]\n"); } // 表达式/语句/块:与 001_main.spl 的 stage1 dump 逐字节一致。 fn test_parse_main() { var src: []u8 = "// Expect retcode = 0\n\nfn main(): i32 {\n ret 0;\n}\n"; var toks = lex_tokens(src); defer { Vec.drop(&toks); } var sources = set_new(); defer { set_drop(&sources); } match set_add(&sources, ByteView.from_slice("test.spl"), ByteView.from_slice(src)) { .Ok(index) => {}, .Err(error) => { @assert(false, "set_add"); }, } var ast: Ast = undefined; match Ast.new(&sources) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new"); }, } defer { Ast.drop(&ast); } var p = new(&ast, toks, null); parse(&p); @assert(!p.failed, "parse ok"); @assert(p.error_count == 0, "no errors"); var buf = ByteBuffer.new(); defer { ByteBuffer.drop(&buf); } @assert(Ast.dump(&ast, &buf), "dump"); expect_text(&buf, "AST:\n`-root #7\n `-container_members #6\n `-fn_decl #5 [3:1]\n |-type_path #1 [3:12]\n `-block #2 [3:16]\n `-statement_ret #4 [4:5]\n `-int_lit #3 [4:9]\n"); } // 任意宽度整数:2^64 也能正确解析(stage1 u64 之外);含分隔符与多进制。 fn check_int(src: []u8, expected: []u8) { var toks = lex_tokens(src); defer { Vec.drop(&toks); } var ast: Ast = undefined; match Ast.new(null) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new"); }, } defer { Ast.drop(&ast); } var p = new(&ast, toks, null); var t = peek(&p); @assert(t != null, "token"); var radix: Radix = .Dec; match t.kind { .Int(r) => { radix = r; }, _ => { @assert(false, "expected int token"); }, } var b = big_int_of(&p, t, radix); defer { BigInt.drop(&b); } var buf = ByteBuffer.new(); defer { ByteBuffer.drop(&buf); } match BigInt.format(&buf, b, 10, false) { .Ok => {}, .Err(error) => { @assert(false, "format"); }, } expect_text(&buf, expected); } fn test_big_int_literal() { check_int("18446744073709551616", "18446744073709551616"); check_int("1_000_000", "1000000"); check_int("0xfff", "4095"); check_int("0b10110", "22"); check_int("0o_17", "15"); } fn expect_contains(buf: *ByteBuffer, needle: []u8) { var actual = ByteBuffer.as_slice(buf.*); if needle.len > actual.len { @assert(false, "contains length"); } var i: usize = 0; while i + needle.len <= actual.len { var j: usize = 0; var ok = true; while j < needle.len { if actual.ptr[i + j] != needle.ptr[j] { ok = false; break; } j = j + 1; } if ok { ret; } i = i + 1; } @assert(false, "expected substring not found"); } // stage3 语法:`comptime fn ... (t: type) type { ... }` 与省略冒号的返回类型。 fn test_parse_comptime_fn() { var src: []u8 = "comptime fn vec(t: type) type { ret t; }\n"; var toks = lex_tokens(src); defer { Vec.drop(&toks); } var sources = set_new(); defer { set_drop(&sources); } match set_add(&sources, ByteView.from_slice("test.spl"), ByteView.from_slice(src)) { .Ok(index) => {}, .Err(error) => { @assert(false, "set_add"); }, } var ast: Ast = undefined; match Ast.new(&sources) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new"); }, } defer { Ast.drop(&ast); } var p = new(&ast, toks, null); parse(&p); @assert(!p.failed, "comptime fn parse ok"); @assert(p.error_count == 0, "comptime fn no errors"); var buf = ByteBuffer.new(); defer { ByteBuffer.drop(&buf); } @assert(Ast.dump(&ast, &buf), "dump"); expect_contains(&buf, "fn_decl"); expect_contains(&buf, "type_path"); } fn main() { test_peek_advance(); test_new_node_span(); test_parse_decls(); test_parse_main(); test_big_int_literal(); test_parse_comptime_fn(); }