From b5a7a811b33717fb8100ad3978d59381ed8a319d Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Fri, 18 Sep 2026 22:32:57 +0800 Subject: [PATCH] =?UTF-8?q?stage2=20=E8=BF=81=E7=A7=BBast=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=99=A8=20stage1=20=E9=98=B2=E6=AD=A2=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=AD=BB=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nob.c | 9 +- stage1/spl_parser.c | 1 + stage2/ast.spl | 642 +++++++++++++ stage2/lexer.spl | 104 +- stage2/parser.spl | 2192 +++++++++++++++++++++++++++++++++++++++++++ stage2/splc1.spl | 92 +- stage2/token.spl | 31 +- 7 files changed, 3000 insertions(+), 71 deletions(-) create mode 100644 stage2/ast.spl create mode 100644 stage2/parser.spl diff --git a/nob.c b/nob.c index 1462834..9352bab 100644 --- a/nob.c +++ b/nob.c @@ -401,8 +401,8 @@ static bool test_once_diff(const char *fname, void *user_data) { } Nob_Cmd s_cmd = {0}; - nob_cmd_append(&s_cmd, exe_path("spl_cli"), BUILD_FOLDER "splc1.sir", "--dump", - (char *)mode, (char *)fname); + nob_cmd_append(&s_cmd, exe_path("spl_cli"), BUILD_FOLDER "splc1.sir", "--dump", (char *)mode, + (char *)fname); Nob_Cmd_Result s_res = {0}; if (!run_capture(&s_cmd, &s_res)) { nob_cmd_result_free(c_res); @@ -414,8 +414,8 @@ static bool test_once_diff(const char *fname, void *user_data) { Nob_String_View s_out = nob_sb_to_sv(s_res.out); bool same = nob_sv_eq(c_out, s_out); if (!same) { - nob_log(NOB_ERROR, "diff mismatch: %s (c=%zu bytes, spl=%zu bytes)", fname, - c_out.count, s_out.count); + nob_log(NOB_ERROR, "diff mismatch: %s (c=%zu bytes, spl=%zu bytes)", fname, c_out.count, + s_out.count); // 打印首个不相同行(按行比较)。 Nob_String_View ca = c_out; Nob_String_View sa = s_out; @@ -618,6 +618,7 @@ int main(int argc, char **argv) { return run_test(stage, &files, tfn, (void *)mode_name) ? 0 : 1; } + if (strcmp(command_name, "clean") == 0) { if (nob_file_exists(BUILD_FOLDER)) { if (!delete_directory_recursively(BUILD_FOLDER)) diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 94a7bd8..d9222a7 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -849,6 +849,7 @@ static spl_ast_node_ref_t parse_container_declaration(spl_parser_t *p) { } default: PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unrecognized declaration"); + advance(p); /* 消费该 token,避免调用方在错误恢复时原地死循环 */ vec_free(tag_list); return 0; } diff --git a/stage2/ast.spl b/stage2/ast.spl new file mode 100644 index 0000000..59bca4e --- /dev/null +++ b/stage2/ast.spl @@ -0,0 +1,642 @@ +// Expect retcode = 0 + +// AST:用 ADT(enum 带载荷)表达节点,节点引用是 arena 序号 usize。 +// stage1 的 C 结构只是「手写 union 模拟 ADT」;这里直接用 SPL 的 enum 载荷, +// 不照抄 union 布局,只要求最终 dump / 语义与 stage1 一致。 +// 索引 0 为 NONE 哨兵(与 stage1 一致)。 + +type .{ .Error } = @import("utils/error.spl"); +type .{ .Vec } = @import("utils/vec.spl"); +type .{ .ByteView } = @import("utils/byte_view.spl"); +type .{ .ByteBuffer } = @import("utils/byte_buffer.spl"); +type .{ .append_usize } = @import("utils/fmt.spl"); +type .{ .Punct } = @import("token.spl"); +type BigInt = @import("utils/mp.spl"); +type .{ .Span, .span_none, .span_make, .span_is_none } = @import("./diag.spl"); +type .{ .Sources, .Source, .set_at } = @import("utils/source.spl"); + +// 算子不另立 enum:AST 直接存词法 Punct。一元/二元由节点 kind 区分 +// (一元:Sub=负 / Not=! / BitNot=~ / And=& / Mul=.*;二元即对应 Punct)。 + +// ---- 节点体(ADT;variant 名 = stage1 的 kind 名;载荷用内联匿名 struct)---- + +type Body = enum { + None, + Root: struct { members: usize }, + ContainerMembers: struct { external_tags: Vec, declarations: Vec }, + ExternalTagItem: struct { is_at: bool, is_external: bool, name: ByteView, args: Vec }, + TagItem: struct { is_at: bool, is_external: bool, name: ByteView, args: Vec }, + ComptimeDecl: struct { body: usize }, + TopLevelBlock: struct { tag_list: Vec, block: usize }, + FnDecl: struct { + tag_list: Vec, + is_comptime: bool, + name: ByteView, + params: Vec, + ret_type: usize, + body: usize, + }, + VarDecl: struct { attr_list: Vec, pattern: usize, type_expr: usize, init_expr: usize }, + ConstDecl: struct { attr_list: Vec, pattern: usize, type_expr: usize, init_expr: usize }, + TypeDecl: struct { attr_list: Vec, pattern: usize, type_expr: usize }, + MemberDecl: struct { + attr_list: Vec, + name: ByteView, + type_expr: usize, + init_expr: usize, + }, + ParamDecl: struct { attr_list: Vec, name: ByteView, type_expr: usize }, + StatementRet: struct { expr: usize }, + StatementBreak: struct { label: usize, expr: usize }, + StatementContinue: struct { label: usize }, + StatementDefer: struct { body: usize }, + StatementComptime: struct { body: usize }, + StatementDecl: struct { decl: usize }, + StatementExpr: struct { expr: usize }, + AggregateMember: struct { + attr_list: Vec, + name: ByteView, + type_expr: usize, + init_expr: usize, + }, + StructInitItem: struct { name: ByteView, expr: usize }, + MatchArm: struct { pattern: usize, body: usize }, + Label: struct { name: ByteView }, + ExprIntLit: struct { value: BigInt }, // 任意宽度(mp),解析后由 Ast 拥有 + ExprFloatLit: struct { text: ByteView }, + ExprCharLit: struct { value: u8 }, + ExprStringLit: struct { data: ByteView }, + ExprBoolLit: struct { value: bool }, + ExprNullLit, + ExprUndefinedLit, + ExprIdent: struct { name: ByteView }, + ExprUnary: struct { op: Punct, operand: usize }, + ExprBinary: struct { op: Punct, left: usize, right: usize }, + ExprCall: struct { callee: usize, args: Vec }, + ExprField: struct { obj: usize, name: ByteView }, + ExprIndex: struct { obj: usize, index: usize }, + ExprSlice: struct { obj: usize, begin: usize, end: usize }, + ExprCast: struct { expr: usize, type_expr: usize }, + ExprTuple: struct { items: Vec }, + ExprArrayLit: struct { + has_len: bool, + len: usize, + is_underscore_len: bool, + elem_type: usize, + elems: Vec, + }, + ExprAggregateLit: struct { name: ByteView, fields: Vec }, + ExprComptimeCall: struct { name: ByteView, args: Vec }, + ExprBlock: struct { stmts: Vec, tail_expr: usize }, + ExprIf: struct { cond: usize, then_block: usize, else_branch: usize }, + ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize }, + ExprWhile: struct { label: usize, cond: usize, body: usize, else_branch: usize }, + ExprLoop: struct { label: usize, body: usize }, + ExprFor: struct { + label: usize, + iter_exprs: Vec, + pattern: usize, + body: usize, + else_branch: usize, + }, + ExprMatch: struct { matched_expr: usize, arms: Vec }, + ExprFnType: struct { is_comptime: bool, params: Vec, ret_type: usize }, + TypePath: struct { name: ByteView, generic_args: Vec }, + TypePointer: struct { pointee: usize }, + TypeArray: struct { + has_len: bool, + len: isize, + is_underscore_len: bool, + elem_type: usize, + }, + TypeSlice: struct { elem_type: usize }, + TypeFn: struct { is_comptime: bool, params: Vec, ret_type: usize }, + TypeParen: struct { types: Vec }, + TypeAggregate: struct { kind_name: ByteView, members: Vec }, + TypeComptimeCall: struct { name: ByteView, args: Vec }, + PatternIdent: struct { name: ByteView }, + PatternUnderscore, + PatternDot: struct { name: ByteView, sub_pattern: usize }, + PatternTuple: struct { items: Vec }, + PatternList: struct { items: Vec }, + PatternAggregate: struct { items: Vec }, + PatternField: struct { name: ByteView, pattern: usize }, +} + +fn kind_name(body: Body): []u8 { + match body { + .None => { ret "none"; }, + .Root(_) => { ret "root"; }, + .ContainerMembers(_) => { ret "container_members"; }, + .ExternalTagItem(_) => { ret "external_tag_item"; }, + .TagItem(_) => { ret "tag_item"; }, + .ComptimeDecl(_) => { ret "comptime_decl"; }, + .TopLevelBlock(_) => { ret "top_level_block"; }, + .FnDecl(_) => { ret "fn_decl"; }, + .VarDecl(_) => { ret "var_decl"; }, + .ConstDecl(_) => { ret "const_decl"; }, + .TypeDecl(_) => { ret "type_decl"; }, + .MemberDecl(_) => { ret "member_decl"; }, + .ParamDecl(_) => { ret "param_decl"; }, + .StatementRet(_) => { ret "statement_ret"; }, + .StatementBreak(_) => { ret "statement_break"; }, + .StatementContinue(_) => { ret "statement_continue"; }, + .StatementDefer(_) => { ret "statement_defer"; }, + .StatementComptime(_) => { ret "statement_comptime"; }, + .StatementDecl(_) => { ret "statement_decl"; }, + .StatementExpr(_) => { ret "statement_expr"; }, + .AggregateMember(_) => { ret "aggregate_member"; }, + .StructInitItem(_) => { ret "struct_init_item"; }, + .MatchArm(_) => { ret "match_arm"; }, + .Label(_) => { ret "label"; }, + .ExprIntLit(_) => { ret "int_lit"; }, + .ExprFloatLit(_) => { ret "float_lit"; }, + .ExprCharLit(_) => { ret "char_lit"; }, + .ExprStringLit(_) => { ret "string_lit"; }, + .ExprBoolLit(_) => { ret "bool_lit"; }, + .ExprNullLit => { ret "null_lit"; }, + .ExprUndefinedLit => { ret "undefined_lit"; }, + .ExprIdent(_) => { ret "ident"; }, + .ExprUnary(_) => { ret "unary"; }, + .ExprBinary(_) => { ret "binary"; }, + .ExprCall(_) => { ret "call"; }, + .ExprField(_) => { ret "field"; }, + .ExprIndex(_) => { ret "index"; }, + .ExprSlice(_) => { ret "slice"; }, + .ExprCast(_) => { ret "cast"; }, + .ExprTuple(_) => { ret "tuple"; }, + .ExprArrayLit(_) => { ret "array_literal"; }, + .ExprAggregateLit(_) => { ret "aggregate_literal"; }, + .ExprComptimeCall(_) => { ret "comptime_call"; }, + .ExprBlock(_) => { ret "block"; }, + .ExprIf(_) => { ret "if_expr"; }, + .ExprIfvar(_) => { ret "ifvar_expr"; }, + .ExprWhile(_) => { ret "while_expr"; }, + .ExprLoop(_) => { ret "loop_expr"; }, + .ExprFor(_) => { ret "for_expr"; }, + .ExprMatch(_) => { ret "match_expr"; }, + .ExprFnType(_) => { ret "fn_type"; }, + .TypePath(_) => { ret "type_path"; }, + .TypePointer(_) => { ret "type_pointer"; }, + .TypeArray(_) => { ret "type_array"; }, + .TypeSlice(_) => { ret "type_slice"; }, + .TypeFn(_) => { ret "type_fn"; }, + .TypeParen(_) => { ret "type_paren"; }, + .TypeAggregate(_) => { ret "type_aggregate"; }, + .TypeComptimeCall(_) => { ret "type_comptime_call"; }, + .PatternIdent(_) => { ret "pattern_ident"; }, + .PatternUnderscore => { ret "pattern_underscore"; }, + .PatternDot(_) => { ret "pattern_dot"; }, + .PatternTuple(_) => { ret "pattern_tuple"; }, + .PatternList(_) => { ret "pattern_list"; }, + .PatternAggregate(_) => { ret "pattern_aggregate"; }, + .PatternField(_) => { ret "pattern_field"; }, + } + ret "?"; +} + +type Node = struct { + span: Span, + resolved_def_id: usize, + resolved_lang_tid: usize, + expected_lang_tid: usize, + attrs: Vec, + body: Body, +} + +type Ast = @self; + +nodes: Vec, // Node +root: usize, +sources: *Sources, // 借用;可为 null(dump 时不解析行列) + +type AstResult = enum { + Ok: Ast, + Err: Error, +} + +fn new_node(body: Body, span: Span): Node { + ret Node { + .span = span, + .resolved_def_id = 0, + .resolved_lang_tid = 0, + .expected_lang_tid = 0, + .attrs = Vec.blank(@sizeof(usize), @alignof(usize)), + .body = body, + }; +} + +fn new(sources: *Sources): AstResult { + var ast = Ast { + .nodes = Vec.blank(@sizeof(Node), @alignof(Node)), + .root = 0, + .sources = sources, + }; + var sentinel = new_node(.None, span_none()); + match Vec.push(&ast.nodes, &sentinel as *u8) { + .Ok => {}, + .Err(error) => { + Vec.drop(&ast.nodes); + ret .Err(error); + }, + } + ret .Ok(ast); +} + +fn drop(self: *Ast) { + var i: usize = 0; + while i < self.nodes.len { + var n = node(self, i); + if n != null { + Vec.drop(&n.attrs); + match n.body { + .ExprIntLit(x) => { BigInt.drop(&x.value); }, + _ => {}, + } + } + i = i + 1; + } + Vec.drop(&self.nodes); + self.root = 0; +} + +fn node(self: *Ast, ref: usize): *Node { + match Vec.get(&self.nodes, ref) { + .Ok(ptr) => { ret ptr as *Node; }, + .Err(error) => { ret null; }, + } + ret null; +} + +// 追加节点,返回其引用(失败返回 0)。 +fn alloc(self: *Ast, body: Body, span: Span): usize { + var n = new_node(body, span); + match Vec.push(&self.nodes, &n as *u8) { + .Ok => {}, + .Err(error) => { + Vec.drop(&n.attrs); + ret 0; + }, + } + ret self.nodes.len - 1; +} + +fn count(self: *Ast): usize { + ret self.nodes.len; +} + +fn ref_vec(): Vec { + ret Vec.blank(@sizeof(usize), @alignof(usize)); +} + +// ---- dump(镜像 stage1 spl_ast_dump)---- +// +// AST: +// `-/|-kind #ref [line:col] +// indent:栈上每层 last 为真输出 " ",否则 "| "。 + +fn push(buf: *ByteBuffer, bytes: []u8): bool { + match ByteBuffer.append(buf, bytes) { + .Ok => { ret true; }, + .Err(error) => { ret false; }, + } + ret false; +} + +fn push_byte(buf: *ByteBuffer, byte: u8): bool { + match ByteBuffer.append_byte(buf, byte) { + .Ok => { ret true; }, + .Err(error) => { ret false; }, + } + ret false; +} + +fn push_usize(buf: *ByteBuffer, value: usize): bool { + match append_usize(buf, value, 10, false) { + .Ok => { ret true; }, + .Err(error) => { ret false; }, + } + ret false; +} + +fn ref_at(list: Vec, index: usize): usize { + match Vec.get(&list, index) { + .Ok(ptr) => { ret (ptr as *usize).*; }, + .Err(error) => { ret 0; }, + } + ret 0; +} + +fn dump_opt(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool): bool { + if ref == 0 { ret true; } + ret dump_node(ast, buf, stack, ref, last); +} + +fn dump_list(ast: *Ast, buf: *ByteBuffer, stack: *Vec, list: Vec): bool { + var i: usize = 0; + while i < list.len { + var last = (i + 1 == list.len); + if !dump_node(ast, buf, stack, ref_at(list, i), last) { ret false; } + i = i + 1; + } + ret true; +} + +fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool): bool { + var n = node(ast, ref); + if n == null { ret false; } + + var i: usize = 0; + while i < stack.len { + var flag: u8 = 0; + match Vec.get(stack, i) { + .Ok(ptr) => { flag = ptr.*; }, + .Err(error) => { ret false; }, + } + if !push(buf, if flag != 0 { " " } else { "| " }) { ret false; } + i = i + 1; + } + if !push(buf, if last { "`-" } else { "|-" }) { ret false; } + if !push(buf, kind_name(n.body)) { ret false; } + if !push(buf, " #") { ret false; } + if !push_usize(buf, ref) { ret false; } + if !span_is_none(n.span) && ast.sources != null { + var src = set_at(ast.sources, n.span.file); + if src != null { + var pos = Source.line_col(src.*, n.span.start); + if !push(buf, " [") { ret false; } + if !push_usize(buf, pos.line) { ret false; } + if !push_byte(buf, ':') { ret false; } + if !push_usize(buf, pos.col) { ret false; } + if !push_byte(buf, ']') { ret false; } + } + } + if !push_byte(buf, '\n') { ret false; } + + var l: u8 = 0; + if last { l = 1; } + match Vec.push(stack, &l as *u8) { + .Ok => {}, + .Err(error) => { ret false; }, + } + + var ok = true; + match n.body { + .None => {}, + .Root(r) => { ok = dump_opt(ast, buf, stack, r.members, true); }, + .ContainerMembers(c) => { + ok = dump_list(ast, buf, stack, c.external_tags) && + dump_list(ast, buf, stack, c.declarations); + }, + .ExternalTagItem(t) => { ok = dump_list(ast, buf, stack, t.args); }, + .TagItem(t) => { ok = dump_list(ast, buf, stack, t.args); }, + .ComptimeDecl(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, + .TopLevelBlock(t) => { + ok = dump_list(ast, buf, stack, t.tag_list) && + dump_opt(ast, buf, stack, t.block, true); + }, + .FnDecl(f) => { + ok = dump_list(ast, buf, stack, f.tag_list) && + dump_list(ast, buf, stack, f.params) && + dump_opt(ast, buf, stack, f.ret_type, false) && + dump_opt(ast, buf, stack, f.body, true); + }, + .VarDecl(d) => { + ok = dump_list(ast, buf, stack, d.attr_list) && + dump_opt(ast, buf, stack, d.pattern, false) && + dump_opt(ast, buf, stack, d.type_expr, false) && + dump_opt(ast, buf, stack, d.init_expr, true); + }, + .ConstDecl(d) => { + ok = dump_list(ast, buf, stack, d.attr_list) && + dump_opt(ast, buf, stack, d.pattern, false) && + dump_opt(ast, buf, stack, d.type_expr, false) && + dump_opt(ast, buf, stack, d.init_expr, true); + }, + .TypeDecl(d) => { + ok = dump_list(ast, buf, stack, d.attr_list) && + dump_opt(ast, buf, stack, d.pattern, false) && + dump_opt(ast, buf, stack, d.type_expr, true); + }, + .MemberDecl(d) => { + ok = dump_list(ast, buf, stack, d.attr_list) && + dump_opt(ast, buf, stack, d.type_expr, false) && + dump_opt(ast, buf, stack, d.init_expr, true); + }, + .ParamDecl(p) => { + ok = dump_list(ast, buf, stack, p.attr_list) && + dump_opt(ast, buf, stack, p.type_expr, true); + }, + .StatementRet(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); }, + .StatementBreak(b) => { + ok = dump_opt(ast, buf, stack, b.label, false) && + dump_opt(ast, buf, stack, b.expr, true); + }, + .StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); }, + .StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, + .StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, + .StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); }, + .StatementExpr(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); }, + .AggregateMember(d) => { + ok = dump_list(ast, buf, stack, d.attr_list) && + dump_opt(ast, buf, stack, d.type_expr, false) && + dump_opt(ast, buf, stack, d.init_expr, true); + }, + .StructInitItem(s) => { ok = dump_opt(ast, buf, stack, s.expr, true); }, + .MatchArm(m) => { + ok = dump_opt(ast, buf, stack, m.pattern, false) && + dump_opt(ast, buf, stack, m.body, true); + }, + .Label(x) => {}, + .ExprIntLit(x) => {}, + .ExprFloatLit(x) => {}, + .ExprCharLit(x) => {}, + .ExprStringLit(x) => {}, + .ExprBoolLit(x) => {}, + .ExprNullLit => {}, + .ExprUndefinedLit => {}, + .ExprIdent(x) => {}, + .ExprUnary(u) => { ok = dump_opt(ast, buf, stack, u.operand, true); }, + .ExprBinary(b) => { + ok = dump_opt(ast, buf, stack, b.left, false) && + dump_opt(ast, buf, stack, b.right, true); + }, + .ExprCall(c) => { + ok = dump_opt(ast, buf, stack, c.callee, false) && + dump_list(ast, buf, stack, c.args); + }, + .ExprField(f) => { ok = dump_opt(ast, buf, stack, f.obj, true); }, + .ExprIndex(x) => { + ok = dump_opt(ast, buf, stack, x.obj, false) && + dump_opt(ast, buf, stack, x.index, true); + }, + .ExprSlice(s) => { + ok = dump_opt(ast, buf, stack, s.obj, false) && + dump_opt(ast, buf, stack, s.begin, false) && + dump_opt(ast, buf, stack, s.end, true); + }, + .ExprCast(c) => { + ok = dump_opt(ast, buf, stack, c.expr, false) && + dump_opt(ast, buf, stack, c.type_expr, true); + }, + .ExprTuple(v) => { ok = dump_list(ast, buf, stack, v.items); }, + .ExprArrayLit(a) => { + ok = dump_opt(ast, buf, stack, a.elem_type, false) && + dump_list(ast, buf, stack, a.elems); + }, + .ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); }, + .ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); }, + .ExprBlock(b) => { + ok = dump_list(ast, buf, stack, b.stmts) && + dump_opt(ast, buf, stack, b.tail_expr, true); + }, + .ExprIf(x) => { + ok = dump_opt(ast, buf, stack, x.cond, false) && + dump_opt(ast, buf, stack, x.then_block, false) && + dump_opt(ast, buf, stack, x.else_branch, true); + }, + .ExprIfvar(x) => { + ok = dump_opt(ast, buf, stack, x.var_decl, false) && + dump_opt(ast, buf, stack, x.then_block, false) && + dump_opt(ast, buf, stack, x.else_branch, true); + }, + .ExprWhile(w) => { + ok = dump_opt(ast, buf, stack, w.label, false) && + dump_opt(ast, buf, stack, w.cond, false) && + dump_opt(ast, buf, stack, w.body, false) && + dump_opt(ast, buf, stack, w.else_branch, true); + }, + .ExprLoop(x) => { + ok = dump_opt(ast, buf, stack, x.label, false) && + dump_opt(ast, buf, stack, x.body, true); + }, + .ExprFor(f) => { + ok = dump_opt(ast, buf, stack, f.label, false) && + dump_list(ast, buf, stack, f.iter_exprs) && + dump_opt(ast, buf, stack, f.pattern, false) && + dump_opt(ast, buf, stack, f.body, false) && + dump_opt(ast, buf, stack, f.else_branch, true); + }, + .ExprMatch(m) => { + ok = dump_opt(ast, buf, stack, m.matched_expr, false) && + dump_list(ast, buf, stack, m.arms); + }, + .ExprFnType(f) => { + ok = dump_list(ast, buf, stack, f.params) && + dump_opt(ast, buf, stack, f.ret_type, true); + }, + .TypePath(p) => {}, + .TypePointer(p) => { ok = dump_opt(ast, buf, stack, p.pointee, true); }, + .TypeArray(a) => { ok = dump_opt(ast, buf, stack, a.elem_type, true); }, + .TypeSlice(s) => { ok = dump_opt(ast, buf, stack, s.elem_type, true); }, + .TypeFn(f) => { + ok = dump_list(ast, buf, stack, f.params) && + dump_opt(ast, buf, stack, f.ret_type, true); + }, + .TypeParen(p) => { ok = dump_list(ast, buf, stack, p.types); }, + .TypeAggregate(a) => { ok = dump_list(ast, buf, stack, a.members); }, + .TypeComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); }, + .PatternIdent(x) => {}, + .PatternUnderscore => {}, + .PatternDot(p) => { ok = dump_opt(ast, buf, stack, p.sub_pattern, true); }, + .PatternTuple(v) => { ok = dump_list(ast, buf, stack, v.items); }, + .PatternList(v) => { ok = dump_list(ast, buf, stack, v.items); }, + .PatternAggregate(v) => { ok = dump_list(ast, buf, stack, v.items); }, + .PatternField(p) => { ok = dump_opt(ast, buf, stack, p.pattern, true); }, + } + + stack.len = stack.len - 1; + ret ok; +} + +// 把整棵树 dump 到 out(追加,不 clear)。 +fn dump(ast: *Ast, out: *ByteBuffer): bool { + if !push(out, "AST:\n") { ret false; } + var stack = Vec.blank(1, 1); + defer { Vec.drop(&stack); } + ret dump_node(ast, out, &stack, ast.root, true); +} + +// ---- 测试 ---- + +fn test_kind_names() { + @assert(ByteView.eq(ByteView.from_slice(kind_name(.None)), ByteView.from_slice("none")), + "none name"); + @assert(ByteView.eq(ByteView.from_slice(kind_name(.ExprBinary(.{ + .op = .Add, + .left = 0, + .right = 0, + }))), + ByteView.from_slice("binary")), + "binary name"); +} + +fn test_alloc_and_payload() { + var ast: Ast = undefined; + match new(null) { + .Ok(v) => { ast = v; }, + .Err(error) => { @assert(false, "ast new failed"); }, + } + defer { drop(&ast); } + + @assert(count(&ast) == 1, "sentinel only"); + + var r = alloc(&ast, .ExprIdent(.{ .name = ByteView.from_slice("main") }), span_make(0, 5, 9)); + @assert(r == 1, "first alloc is 1"); + + var n = node(&ast, r); + n.resolved_def_id = 99; + @assert(n.span.file == 0 && n.span.start == 5 && n.span.end == 9, "span stored"); + @assert(n.resolved_def_id == 99, "header field"); + + match n.body { + .ExprIdent(i) => { + @assert(ByteView.eq(i.name, ByteView.from_slice("main")), "payload name"); + }, + _ => { @assert(false, "expected ident"); }, + } +} + +fn expect_bytes(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; + } +} + +fn test_dump() { + var ast: Ast = undefined; + match new(null) { + .Ok(v) => { ast = v; }, + .Err(error) => { @assert(false, "ast new failed"); }, + } + defer { drop(&ast); } + + var id_ref = alloc(&ast, .ExprIdent(.{ .name = ByteView.from_slice("main") }), span_none()); // #1 + var decls = ref_vec(); + match Vec.push(&decls, &id_ref as *u8) { + .Ok => {}, + .Err(error) => { @assert(false, "push decl"); }, + } + var cm_ref = alloc(&ast, .ContainerMembers(.{ + .external_tags = ref_vec(), + .declarations = decls, + }), span_none()); // #2 + var root_ref = alloc(&ast, .Root(.{ .members = cm_ref }), span_none()); // #3 + ast.root = root_ref; + + var buf = ByteBuffer.new(); + defer { ByteBuffer.drop(&buf); } + @assert(dump(&ast, &buf), "dump ok"); + // sources=null → 不解析行列;无 span 时也不打印。 + expect_bytes(&buf, "AST:\n`-root #3\n `-container_members #2\n `-ident #1\n"); +} + +fn main() { + test_kind_names(); + test_alloc_and_payload(); + test_dump(); +} diff --git a/stage2/lexer.spl b/stage2/lexer.spl index 87f4274..0fdf7e3 100644 --- a/stage2/lexer.spl +++ b/stage2/lexer.spl @@ -9,7 +9,7 @@ type .{ .Vec, .VecResult } = @import("utils/vec.spl"); type .{ .ByteView } = @import("utils/byte_view.spl"); type .{ .is_digit, .is_alpha, .is_alnum, .is_hex_digit, .digit_value } = @import("utils/ascii.spl"); type .{ - .Token, .TokenKind, .Radix, .Keyword, .Punct, .int_value, .keyword_of + .Token, .TokenKind, .Radix, .Keyword, .Punct, .keyword_of } = @import("token.spl"); type .{ .Span, .span_make } = @import("./diag.spl"); type Diag = @import("./diag.spl"); @@ -45,23 +45,6 @@ fn hex_val(c: u8): u8 { ret 0; } -// 跳过下划线,按 radix 折叠;非法字符忽略(前缀 x/b/o 自然被忽略)。 -fn decode_digits(text: ByteView, radix: u8): u64 { - var acc: u64 = 0; - var i: usize = 0; - while i < text.len { - var b = text.ptr[i]; - if b != '_' { - match digit_value(b, radix) { - .Ok(v) => { acc = acc * (radix as u64) + (v as u64); }, - .Err(error) => {}, - } - } - i = i + 1; - } - ret acc; -} - // 文本形如 'a' / '\n' / '\x41';返回解码后的字节。 fn decode_char(text: ByteView): u8 { var start: usize = 1; @@ -119,7 +102,7 @@ fn sub(self: *Lexer, start: usize, end: usize): ByteView { // 产出 token 并把 pos/col 推进到 end。Vec.push 失败视为致命(同 vec.make_vec 风格)。 fn emit_to(self: *Lexer, out: *Vec, kind: TokenKind, start: usize, end: usize, line: usize, col: usize) { - var t = Token.make(kind, sub(self, start, end), line, col); + var t = Token.make(kind, sub(self, start, end), line, col, start); match Vec.push(out, &t as *u8) { .Ok => {}, .Err(error) => { @assert(false, "lexer: Vec.push failed"); }, @@ -151,8 +134,7 @@ fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) { while p < self.src.len && (is_hex_digit(self.src.ptr[p]) || self.src.ptr[p] == '_') { p = p + 1; } - emit_to(self, out, .Int(int_value(decode_digits(sub(self, start, p), 16), .Hex)), start, - p, line, col); + emit_to(self, out, .Int(.Hex), start, p, line, col); ret; } if nc == 'b' || nc == 'B' { @@ -161,8 +143,7 @@ fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) { self.src.ptr[p] == '_') { p = p + 1; } - emit_to(self, out, .Int(int_value(decode_digits(sub(self, start, p), 2), .Bin)), start, - p, line, col); + emit_to(self, out, .Int(.Bin), start, p, line, col); ret; } if nc == 'o' || nc == 'O' { @@ -172,8 +153,7 @@ fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) { self.src.ptr[p] == '_') { p = p + 1; } - emit_to(self, out, .Int(int_value(decode_digits(sub(self, start, p), 8), .Oct)), start, - p, line, col); + emit_to(self, out, .Int(.Oct), start, p, line, col); ret; } } @@ -181,12 +161,25 @@ fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) { while p < self.src.len && (is_digit(self.src.ptr[p]) || self.src.ptr[p] == '_') { p = p + 1; } if p + 1 < self.src.len && self.src.ptr[p] == '.' && is_digit(self.src.ptr[p + 1]) { p = p + 1; - while p < self.src.len && is_digit(self.src.ptr[p]) { p = p + 1; } + // 小数部分允许下划线分隔(Rust 风格)。 + while p < self.src.len && (is_digit(self.src.ptr[p]) || self.src.ptr[p] == '_') { + p = p + 1; + } + // 可选指数:e/E [+-] 数字(数字允许下划线);仅当确有数字时纳入。 + if p < self.src.len && (self.src.ptr[p] == 'e' || self.src.ptr[p] == 'E') { + var q = p + 1; + if q < self.src.len && (self.src.ptr[q] == '+' || self.src.ptr[q] == '-') { q = q + 1; } + if q < self.src.len && is_digit(self.src.ptr[q]) { + p = q + 1; + while p < self.src.len && (is_digit(self.src.ptr[p]) || self.src.ptr[p] == '_') { + p = p + 1; + } + } + } emit_to(self, out, .Float, start, p, line, col); ret; } - emit_to(self, out, .Int(int_value(decode_digits(sub(self, start, p), 10), .Dec)), start, p, line, - col); + emit_to(self, out, .Int(.Dec), start, p, line, col); } fn scan_char(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) { @@ -481,9 +474,9 @@ fn lex_ok(src: []u8): Vec { fn at(v: *Vec, index: usize): Token { match Vec.get(v, index) { .Ok(ptr) => { ret (ptr as *Token).*; }, - .Err(error) => { ret Token.make(.Unknown(0), ByteView.empty(), 0, 0); }, + .Err(error) => { ret Token.make(.Unknown(0), ByteView.empty(), 0, 0, 0); }, } - ret Token.make(.Unknown(0), ByteView.empty(), 0, 0); + ret Token.make(.Unknown(0), ByteView.empty(), 0, 0, 0); } fn expect_punct(tok: Token, expected: Punct) { @@ -507,11 +500,10 @@ fn expect_ident(tok: Token) { } } -fn expect_int(tok: Token, value: u64, radix: Radix) { +fn expect_int(tok: Token, radix: Radix) { match tok.kind { - .Int(iv) => { - @assert(iv.value == value, "int value"); - @assert(@enum2int(iv.radix) == @enum2int(radix), "int radix"); + .Int(r) => { + @assert(@enum2int(r) == @enum2int(radix), "int radix"); }, _ => { @assert(false, "expected int"); }, } @@ -563,7 +555,7 @@ fn test_basic() { expect_endline(at(&v, 5)); expect_keyword(at(&v, 6), .Ret); expect_pos(at(&v, 6), 2, 5); - expect_int(at(&v, 7), 0, .Dec); + expect_int(at(&v, 7), .Dec); expect_punct(at(&v, 8), .Semicolon); expect_endline(at(&v, 9)); expect_punct(at(&v, 10), .RBrace); @@ -652,10 +644,10 @@ fn test_comments_and_literals() { } expect_text(s, "\"hi\\n\""); - expect_int(at(&v, 7), 255, .Hex); - expect_int(at(&v, 8), 10, .Bin); - expect_int(at(&v, 9), 15, .Oct); - expect_int(at(&v, 10), 1000, .Dec); + expect_int(at(&v, 7), .Hex); + expect_int(at(&v, 8), .Bin); + expect_int(at(&v, 9), .Oct); + expect_int(at(&v, 10), .Dec); var fl = at(&v, 11); match fl.kind { @@ -666,6 +658,39 @@ fn test_comments_and_literals() { } // 非法输入走 diag:仍产出 token 以便继续收集错误,但 diag 必须记录。 +// Rust 风格数字字面量:分隔符下划线 + 多进制 + 小数/指数字面量。 +fn test_number_forms() { + var v = lex_ok("1_000_000 0xFF_FF 0x_ff 0b_1010 0o_7 1_000.5 1.5_0 1_000.500 1.5e-3 1_0.5_0E2"); + defer { Vec.drop(&v); } + @assert(v.len == 11, "number token count"); + + expect_int(at(&v, 0), .Dec); + expect_text(at(&v, 0), "1_000_000"); + expect_int(at(&v, 1), .Hex); + expect_text(at(&v, 1), "0xFF_FF"); + expect_int(at(&v, 2), .Hex); + expect_text(at(&v, 2), "0x_ff"); + expect_int(at(&v, 3), .Bin); + expect_text(at(&v, 3), "0b_1010"); + expect_int(at(&v, 4), .Oct); + expect_text(at(&v, 4), "0o_7"); + + var i: usize = 5; + while i < 10 { + match at(&v, i).kind { + .Float => {}, + _ => { @assert(false, "expected float"); }, + } + i = i + 1; + } + expect_text(at(&v, 5), "1_000.5"); + expect_text(at(&v, 6), "1.5_0"); + expect_text(at(&v, 7), "1_000.500"); + expect_text(at(&v, 8), "1.5e-3"); + expect_text(at(&v, 9), "1_0.5_0E2"); + expect_eof(at(&v, 10)); +} + fn test_diag_errors() { var diag = Diag.new(null); defer { Diag.drop(&diag); } @@ -689,5 +714,6 @@ fn main() { test_basic(); test_operators(); test_comments_and_literals(); + test_number_forms(); test_diag_errors(); } diff --git a/stage2/parser.spl b/stage2/parser.spl new file mode 100644 index 0000000..90d0282 --- /dev/null +++ b/stage2/parser.spl @@ -0,0 +1,2192 @@ +// 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 中的下标 + +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, + }; +} + +// ---- token 访问 ---- + +fn token_ptr(self: *Parser, index: usize): *Token { + match Vec.get(&self.toks, index) { + .Ok(ptr) => { ret ptr as *Token; }, + .Err(error) => { ret null; }, + } + ret null; +} + +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 i = self.pos; + while i < n { + var t = token_ptr(self, i); + if t == null { break; } + if !is_trivia(t.kind) { break; } + i = i + 1; + } + self.pos = i; + if i >= n { ret null; } + ret token_ptr(self, 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); +} + +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; }, + _ => { 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; }, + .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 = 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; + pn.body = .PatternDot(pd); + }, + _ => {}, + } + } + 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; + }, + _ => {}, + } + }, + _ => {}, + } + 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) { + 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; } + advance(self); + if is_external { + t = peek(self); + if t == null || !kind_is_punct(t.kind, .Not) { + parse_error(self, t, "expected '!' in external tag"); + ret; + } + advance(self); + } + 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 = is_external, + .name = t.text, + .args = ref_vec(), + }); + if is_external { + body = .ExternalTagItem(.{ + .is_at = is_at, + .is_external = is_external, + .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(); + parse_tag_list(self, &attrs, false, true); + 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); + var ty = parse_type_expr(self); + ret new_node(self, + .ParamDecl(.{ .attr_list = attrs, .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; + }, + .Punct(p) => { + if @enum2int(p) == @enum2int(Punct.Ellipsis) { + advance(self); + var pd = new_node(self, .ParamDecl(.{ + .attr_list = ref_vec(), + .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; +} + +fn parse_fn_decl(self: *Parser, tags: Vec): 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); + } + var body: usize = 0; + 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 { + parse_error(self, t, "function declaration must end with ';' or block"); + ret 0; + } + ret new_node(self, .FnDecl(.{ + .tag_list = tags, + .is_comptime = false, + .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); + 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); + if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); } + else { body = parse_expr(self); } + 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); + 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); } + 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) { 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); + ret new_node(self, .TypePath(.{ + .name = bt.text, + .generic_args = ref_vec(), + }), bt); + }, + .Keyword(k) => { + // stage1 is_builtin_type_kw:bool / any 作为类型路径。 + if @enum2int(k) == @enum2int(Keyword.Bool) || + @enum2int(k) == @enum2int(Keyword.Any) { + 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 { + ret parse_expr_prec(self, 1); +} + +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; n.body = .ExprWhile(w); }, + .ExprLoop(l) => { l.label = label; n.body = .ExprLoop(l); }, + .ExprFor(f) => { f.label = label; n.body = .ExprFor(f); }, + _ => {}, + } + 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 = 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); } + 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; } + } + 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); + 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, .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_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.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); + 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); + 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 main() { + test_peek_advance(); + test_new_node_span(); + test_parse_decls(); + test_parse_main(); + test_big_int_literal(); +} diff --git a/stage2/splc1.spl b/stage2/splc1.spl index caa2f47..4bbf31a 100644 --- a/stage2/splc1.spl +++ b/stage2/splc1.spl @@ -9,6 +9,8 @@ type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl"); type .{ .Vec } = @import("./utils/vec.spl"); type .{ .Token, .kind_name } = @import("./token.spl"); type .{ .Lexer } = @import("./lexer.spl"); +type Ast = @import("./ast.spl"); +type Parser = @import("./parser.spl"); type .{ .append_usize } = @import("./utils/fmt.spl"); type .{ .read_all, .print, .eprint } = @import("./utils/io.spl"); type .{ .set_new, .set_add, .set_drop } = @import("./utils/source.spl"); @@ -18,9 +20,9 @@ type Logger = @import("./utils/log.spl"); fn token_at(tokens: *Vec, index: usize): Token { match Vec.get(tokens, index) { .Ok(ptr) => { ret (ptr as *Token).*; }, - .Err(error) => { ret Token.make(.Unknown(0), ByteView.empty(), 0, 0); }, + .Err(error) => { ret Token.make(.Unknown(0), ByteView.empty(), 0, 0, 0); }, } - ret Token.make(.Unknown(0), ByteView.empty(), 0, 0); + ret Token.make(.Unknown(0), ByteView.empty(), 0, 0, 0); } // 追加成功返回 true;失败(仅 OOM)返回 false。 @@ -136,6 +138,77 @@ fn dump_lex(log: *Logger, path: ByteView): i32 { ret 0; } +fn dump_ast(log: *Logger, path: ByteView): i32 { + var content = ByteBuffer.new(); + defer { ByteBuffer.drop(&content); } + match read_all(path, &content) { + .Ok => {}, + .Err(error) => { + Logger.error(log, "cannot read input file"); + ret 1; + }, + } + var text = ByteView.from_slice(ByteBuffer.as_slice(content)); + + var sources = set_new(); + defer { set_drop(&sources); } + match set_add(&sources, path, text) { + .Ok(index) => {}, + .Err(error) => { + Logger.error(log, "cannot register source"); + ret 1; + }, + } + var diag = Diag.new(&sources); + defer { Diag.drop(&diag); } + + var tokens = Vec.blank(@sizeof(Token), @alignof(Token)); + match Lexer.lex(text, &diag, 0) { + .Ok(v) => { tokens = v; }, + .Err(error) => { + Logger.error(log, "lex failed"); + ret 1; + }, + } + defer { Vec.drop(&tokens); } + + var ast: Ast = undefined; + match Ast.new(&sources) { + .Ok(v) => { ast = v; }, + .Err(error) => { + Logger.error(log, "ast init failed"); + ret 1; + }, + } + defer { Ast.drop(&ast); } + + var p = Parser.new(&ast, tokens, &diag); + Parser.parse(&p); + + if Diag.has_error(diag) || p.failed { + var rendered = ByteBuffer.new(); + defer { ByteBuffer.drop(&rendered); } + match Diag.render_human(&diag, &rendered) { + .Ok => {}, + .Err(error) => {}, + } + eprint(ByteBuffer.as_slice(rendered)); + ret 1; + } + + var out = ByteBuffer.new(); + defer { ByteBuffer.drop(&out); } + if !Ast.dump(&ast, &out) { + Logger.fatal(log, "out of memory while dumping ast"); + ret 1; + } + match print(ByteBuffer.as_slice(out)) { + .Ok => {}, + .Err(error) => { ret 1; }, + } + ret 0; +} + fn main(argc: i32, argv: **u8): i32 { // 无参数:自测运行(`nob test stage2`)视为成功。 if argc <= 1 { ret 0; } @@ -143,7 +216,7 @@ fn main(argc: i32, argv: **u8): i32 { var log = Logger.new(ByteView.from_slice("splc1")); if argc < 4 { - Logger.fatal(&log, "usage: splc1 --dump [out]"); + Logger.fatal(&log, "usage: splc1 --dump [out]"); ret 1; } @@ -152,12 +225,15 @@ fn main(argc: i32, argv: **u8): i32 { var path = ByteView.from_cstr(argv[3]); if !ByteView.eq(arg1, ByteView.from_slice("--dump")) { - Logger.fatal(&log, "usage: splc1 --dump [out]"); + Logger.fatal(&log, "usage: splc1 --dump [out]"); ret 1; } - if !ByteView.eq(mode, ByteView.from_slice("lex")) { - Logger.fatal(&log, "dump mode not implemented"); - ret 1; + if ByteView.eq(mode, ByteView.from_slice("lex")) { + ret dump_lex(&log, path); } - ret dump_lex(&log, path); + if ByteView.eq(mode, ByteView.from_slice("ast")) { + ret dump_ast(&log, path); + } + Logger.fatal(&log, "dump mode not implemented"); + ret 1; } diff --git a/stage2/token.spl b/stage2/token.spl index fcd231f..ed227d4 100644 --- a/stage2/token.spl +++ b/stage2/token.spl @@ -32,16 +32,11 @@ type Radix = enum { Hex, } -type IntValue = struct { - value: u64, - radix: Radix, -} - type TokenKind = enum { Keyword: Keyword, Punct: Punct, Ident, - Int: IntValue, + Int: Radix, // 仅记进制;数值由 parser 按任意宽度(mp)解析 Float, Char: u8, Str, @@ -57,13 +52,10 @@ kind: TokenKind, text: ByteView, line: usize, col: usize, +offset: usize, // 源文件字节偏移(供 AST Span 使用) -fn make(kind: TokenKind, text: ByteView, line: usize, col: usize): Token { - ret Token { .kind = kind, .text = text, .line = line, .col = col }; -} - -fn int_value(value: u64, radix: Radix): IntValue { - ret IntValue { .value = value, .radix = radix }; +fn make(kind: TokenKind, text: ByteView, line: usize, col: usize, offset: usize): Token { + ret Token { .kind = kind, .text = text, .line = line, .col = col, .offset = offset }; } fn is_trivia(kind: TokenKind): bool { @@ -254,24 +246,23 @@ fn test_keywords() { } fn test_adt_payloads() { - var t = make(.Int(int_value(255, .Hex)), view("0xFF"), 3, 5); + var t = make(.Int(.Hex), view("0xFF"), 3, 5, 0); @assert(t.line == 3 && t.col == 5, "position"); match t.kind { - .Int(iv) => { - @assert(iv.value == 255, "int value"); + .Int(radix) => { var hex: Radix = .Hex; - @assert(@enum2int(iv.radix) == @enum2int(hex), "int radix"); + @assert(@enum2int(radix) == @enum2int(hex), "int radix"); }, _ => { @assert(false, "expected int"); }, } - var c = make(.Char('a'), view("'a'"), 1, 1); + var c = make(.Char('a'), view("'a'"), 1, 1, 0); match c.kind { .Char(value) => { @assert(value == 'a', "char value"); }, _ => { @assert(false, "expected char"); }, } - var p = make(.Punct(.ArrowBoth), view("<->"), 1, 1); + var p = make(.Punct(.ArrowBoth), view("<->"), 1, 1, 0); match p.kind { .Punct(punct) => { var both: Punct = .ArrowBoth; @@ -280,7 +271,7 @@ fn test_adt_payloads() { _ => { @assert(false, "expected punct"); }, } - var k = make(.Keyword(.Ret), view("ret"), 1, 1); + var k = make(.Keyword(.Ret), view("ret"), 1, 1, 0); match k.kind { .Keyword(kw) => { var ret_kw: Keyword = .Ret; @@ -390,7 +381,7 @@ fn test_kind_names() { expect_kind_name(.Punct(.Dollar), "TOK_DOLLAR"); expect_kind_name(.Ident, "TOK_IDENT"); - expect_kind_name(.Int(int_value(0, .Dec)), "TOK_INT_LITERAL"); + expect_kind_name(.Int(.Dec), "TOK_INT_LITERAL"); expect_kind_name(.Float, "TOK_FLOAT_LITERAL"); expect_kind_name(.Char('a'), "TOK_CHAR_LITERAL"); expect_kind_name(.Str, "TOK_STRING_LITERAL");