Files
spl/stage2/parser.spl
T

2405 lines
83 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Expect retcode = 0
// Parser:token 流 -> Ast(ADT 节点)。C 版只作控制流参考,节点分配顺序需与 stage1
// 一致,以保证 dump 的 #ref 可对比。pos 在 token 向量上移动,自动跳过 trivia。
type .{ .Vec } = @import("utils/vec.spl");
type .{ .ByteView } = @import("utils/byte_view.spl");
type .{ .Token, .TokenKind, .Radix, .Keyword, .Punct } = @import("token.spl");
type .{ .Lexer } = @import("lexer.spl");
type BigInt = @import("utils/mp.spl");
type Ast = @import("ast.spl");
type .{ .Body, .ref_vec } = @import("ast.spl");
type .{ .Span, .span_make, .span_none } = @import("diag.spl");
type Diag = @import("diag.spl");
type .{ .Sources, .set_new, .set_add, .set_drop } = @import("utils/source.spl");
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
type Parser = @self;
ast: *Ast,
toks: Vec, // Token(借用)
diag: *Diag, // 全程诊断;可为 null(自测)
pos: usize,
failed: bool,
stop_agg: i32,
error_count: usize,
file: usize, // 当前源文件在 Sources 中的下标
comptime_depth: usize, // >0 表示处于 comptime 上下文(类型可作为表达式)
fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
ret Parser {
.ast = ast,
.toks = toks,
.diag = diag,
.pos = 0,
.failed = false,
.stop_agg = 0,
.error_count = 0,
.file = 0,
.comptime_depth = 0,
};
}
// ---- token 访问 ----
fn token_ptr(self: *Parser, index: usize): *Token {
if index >= self.toks.len { ret null; }
ret (self.toks.data as *Token) + index;
}
fn is_trivia(kind: TokenKind): bool {
match kind {
.Endline => { ret true; },
.LineComment => { ret true; },
_ => { ret false; },
}
ret false;
}
// 跳过 trivia;返回当前 token(含 EOF)。越界返回 null。
fn peek(self: *Parser): *Token {
var n = self.toks.len;
var base = self.toks.data as *Token;
var i = self.pos;
while i < n {
if base == null { break; }
var t = base + i;
if !is_trivia(t.kind) { break; }
i = i + 1;
}
self.pos = i;
if i >= n || base == null { ret null; }
ret base + i;
}
// 跳过 trivia,返回当前 token 之后的下一个非 trivia token(不移动 pos)。
fn peek_next(self: *Parser): *Token {
var n = self.toks.len;
var base = self.toks.data as *Token;
var i = self.pos + 1;
while i < n {
if base == null { break; }
var t = base + i;
if !is_trivia(t.kind) { break; }
i = i + 1;
}
if i >= n || base == null { ret null; }
ret base + i;
}
fn advance(self: *Parser): *Token {
var t = peek(self);
if t == null { ret null; }
var eof = false;
match t.kind {
.Eof => { eof = true; },
_ => {},
}
if !eof { self.pos = self.pos + 1; }
ret t;
}
fn kind_is_punct(kind: TokenKind, punct: Punct): bool {
match kind {
.Punct(p) => { ret @enum2int(p) == @enum2int(punct); },
_ => { ret false; },
}
ret false;
}
fn kind_is_keyword(kind: TokenKind, kw: Keyword): bool {
match kind {
.Keyword(k) => { ret @enum2int(k) == @enum2int(kw); },
_ => { ret false; },
}
ret false;
}
fn at_punct(self: *Parser, punct: Punct): bool {
var t = peek(self);
if t == null { ret false; }
ret kind_is_punct(t.kind, punct);
}
fn at_keyword(self: *Parser, kw: Keyword): bool {
var t = peek(self);
if t == null { ret false; }
ret kind_is_keyword(t.kind, kw);
}
fn at_eof(self: *Parser): bool {
var t = peek(self);
if t == null { ret true; }
match t.kind {
.Eof => { ret true; },
_ => { ret false; },
}
ret false;
}
// ---- 错误 ----
fn span_of(self: *Parser, tok: *Token): Span {
if tok == null { ret span_none(); }
ret span_make(self.file, tok.offset, tok.offset + tok.text.len);
}
fn parse_error(self: *Parser, tok: *Token, what: []u8) {
self.error_count = self.error_count + 1;
if self.diag == null { ret; }
Diag.err(self.diag, span_of(self, tok), .ParseExpected, what);
}
// 转义解码(镜像 stage1 spl_decode_escape):去掉引号,处理 \n \t \r \0 \\ \' \" \xHH。
fn hex_digit(c: u8): u8 {
if c >= '0' && c <= '9' { ret c - '0'; }
if c >= 'a' && c <= 'f' { ret (c - 'a') + 10; }
if c >= 'A' && c <= 'F' { ret (c - 'A') + 10; }
ret 0;
}
fn decode_str(t: ByteView): ByteBuffer {
@assert(t.len >= 2 && t.ptr != null, "string literal token must include quotes");
var out = ByteBuffer.new();
if t.len < 2 { ret out; }
var i: usize = 1;
var end = t.len - 1;
while i < end {
var c = t.ptr[i];
if c == '\\' && i + 1 < end {
var e = t.ptr[i + 1];
if e == 'x' && i + 3 < end {
var hi = hex_digit(t.ptr[i + 2]);
var lo = hex_digit(t.ptr[i + 3]);
match ByteBuffer.append_byte(&out, (hi * 16 + lo) as u8) {
.Ok => {}, .Err(error) => {},
}
i = i + 4;
} else {
var d: u8 = e;
if e == 'n' { d = '\n'; }
else if e == 't' { d = '\t'; }
else if e == 'r' { d = '\r'; }
else if e == '0' { d = 0; }
match ByteBuffer.append_byte(&out, d) { .Ok => {}, .Err(error) => {} }
i = i + 2;
}
} else {
match ByteBuffer.append_byte(&out, c) { .Ok => {}, .Err(error) => {} }
i = i + 1;
}
}
ret out;
}
fn expect_punct(self: *Parser, punct: Punct, what: []u8): bool {
var t = peek(self);
if t == null || !kind_is_punct(t.kind, punct) {
parse_error(self, t, what);
ret false;
}
advance(self);
ret true;
}
// ---- 节点 ----
fn new_node(self: *Parser, body: Body, tok: *Token): usize {
ret Ast.alloc(self.ast, body, span_of(self, tok));
}
fn radix_value(r: Radix): u8 {
match r {
.Bin => { ret 2; },
.Oct => { ret 8; },
.Hex => { ret 16; },
.Dec => { ret 10; },
}
ret 10;
}
// 整型 token 文本(去下划线、去 0x/0b/0o 前缀)→ 任意宽度 BigInt。失败返回 0 并报错。
fn big_int_of(self: *Parser, tok: *Token, radix: Radix): BigInt {
var cleaned = ByteBuffer.new();
defer { ByteBuffer.drop(&cleaned); }
var start: usize = 0;
if @enum2int(radix) != @enum2int(Radix.Dec) && tok.text.len >= 2 {
start = 2;
}
var i = start;
while i < tok.text.len {
var b = tok.text.ptr[i];
if b != '_' {
match ByteBuffer.append_byte(&cleaned, b) {
.Ok => {},
.Err(error) => { ret BigInt.zero(); },
}
}
i = i + 1;
}
var out = BigInt.zero();
match BigInt.parse(&out, ByteBuffer.as_slice(cleaned), radix_value(radix)) {
.Ok => {},
.Err(error) => {
parse_error(self, tok, "invalid integer literal");
BigInt.drop(&out);
ret BigInt.zero();
},
}
ret out;
}
// 数组长度等场景取 u64(溢出/失败取 0)。
fn int_token_u64(self: *Parser, tok: *Token, radix: Radix): u64 {
var b = big_int_of(self, tok, radix);
var v: u64 = 0;
match BigInt.to_u64(&v, b) {
.Ok => {},
.Err(error) => { v = 0; },
}
BigInt.drop(&b);
ret v;
}
// ---- 判定(stage1 can_start_*)----
fn can_start_expr(kind: TokenKind): bool {
match kind {
.Int(_) => { ret true; },
.Float => { ret true; },
.Char(_) => { ret true; },
.Str => { ret true; },
.Keyword(k) => {
match k {
.True => { ret true; },
.False => { ret true; },
.Null => { ret true; },
.Undefined => { ret true; },
.If => { ret true; },
.While => { ret true; },
.Loop => { ret true; },
.For => { ret true; },
.Match => { ret true; },
.Comptime => { ret true; },
_ => { 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 = Ast.push_string(self.ast, decode_str(t.text)) }), t);
},
.Keyword(k) => {
match k {
.True => {
advance(self);
ret new_node(self, .ExprBoolLit(.{ .value = true }), t);
},
.False => {
advance(self);
ret new_node(self, .ExprBoolLit(.{ .value = false }), t);
},
.Null => {
advance(self);
ret new_node(self, .ExprNullLit, t);
},
_ => {},
}
},
.Ident => {
advance(self);
ret new_node(self, .PatternIdent(.{ .name = t.text }), t);
},
.Punct(p) => {
match p {
.Dot => {
advance(self);
var nx = peek(self);
if nx != null && kind_is_punct(nx.kind, .LBrace) {
ret parse_pattern_aggregate(self, t);
}
if nx == null { ret 0; }
match nx.kind { .Ident => {}, _ => { ret 0; } }
advance(self);
// stage1 先分配 PATTERN_DOT,再解析子模式。
var ref = new_node(self, .PatternDot(.{
.name = nx.text,
.sub_pattern = 0,
}), t);
if at_punct(self, .LParen) {
advance(self);
if !at_punct(self, .RParen) {
var sub = parse_pattern(self);
var pn = Ast.node(self.ast, ref);
match pn.body {
.PatternDot(pd) => {
pd.sub_pattern = sub;
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;
}
// 该 token 是否可能开始一个类型表达式(用于省略冒号的返回类型)。
fn token_starts_type(t: *Token): bool {
if t == null { ret false; }
match t.kind {
.Ident => { ret true; },
.Keyword(k) => {
if @enum2int(k) == @enum2int(Keyword.Type) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Bool) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Any) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Fn) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Struct) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Union) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Enum) { ret true; }
if @enum2int(k) == @enum2int(Keyword.Shape) { ret true; }
},
.Punct(p) => {
if @enum2int(p) == @enum2int(Punct.Mul) { ret true; }
if @enum2int(p) == @enum2int(Punct.LBracket) { ret true; }
if @enum2int(p) == @enum2int(Punct.At) { ret true; }
if @enum2int(p) == @enum2int(Punct.Sharp) { ret true; }
},
_ => {},
}
ret false;
}
fn parse_fn_decl(self: *Parser, tags: Vec, is_comptime: bool): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Fn) { ret 0; }
advance(self);
var name = peek(self);
if name == null { ret 0; }
match name.kind { .Ident => {}, _ => { ret 0; } }
advance(self);
if !expect_punct(self, .LParen, "'(' in function declaration") { ret 0; }
var params = parse_param_list(self);
if !expect_punct(self, .RParen, "')' in function declaration") { ret 0; }
var ret_type: usize = 0;
var t = peek(self);
if t != null && kind_is_punct(t.kind, .Colon) {
advance(self);
ret_type = parse_type_expr(self);
t = peek(self);
} else if token_starts_type(t) {
// 省略冒号:`fn f() T { ... }`
ret_type = parse_type_expr(self);
t = peek(self);
}
var body: usize = 0;
if is_comptime { self.comptime_depth = self.comptime_depth + 1; }
if t != null && kind_is_punct(t.kind, .LBrace) {
body = parse_block(self);
} else if t != null && kind_is_punct(t.kind, .Semicolon) {
advance(self);
} else {
if is_comptime { self.comptime_depth = self.comptime_depth - 1; }
parse_error(self, t, "function declaration must end with ';' or block");
ret 0;
}
if is_comptime { self.comptime_depth = self.comptime_depth - 1; }
ret new_node(self, .FnDecl(.{
.tag_list = tags,
.is_comptime = is_comptime,
.name = name.text,
.params = params,
.ret_type = ret_type,
.body = body,
}), kw);
}
fn parse_type_decl(self: *Parser, tags: Vec): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Type) { ret 0; }
advance(self);
var pattern = parse_pattern(self);
if pattern == 0 { ret 0; }
if !expect_punct(self, .Assign, "'=' in type declaration") { ret 0; }
// stage1 先分配 TYPE_DECL 再解析 type_expr(影响 #ref 顺序)。
var ref = new_node(self, .TypeDecl(.{
.attr_list = tags,
.pattern = pattern,
.type_expr = 0,
}), kw);
var te = parse_type_expr(self);
var node = Ast.node(self.ast, ref);
match node.body {
.TypeDecl(td) => {
td.type_expr = te;
node.body = .TypeDecl(td);
},
_ => {},
}
var t = peek(self);
if t != null && (kind_is_punct(t.kind, .Comma) || kind_is_punct(t.kind, .Semicolon)) {
advance(self);
}
ret ref;
}
fn parse_var_const_decl(self: *Parser, tags: Vec, is_const: bool): usize {
var kw = peek(self);
if kw == null { ret 0; }
if is_const && !kind_is_keyword(kw.kind, .Const) { ret 0; }
if !is_const && !kind_is_keyword(kw.kind, .Var) { ret 0; }
advance(self);
var pattern = parse_pattern(self);
if pattern == 0 { ret 0; }
var te: usize = 0;
var init: usize = 0;
var t = peek(self);
if t != null && kind_is_punct(t.kind, .Colon) {
advance(self);
te = parse_type_expr(self);
t = peek(self);
if t != null && kind_is_punct(t.kind, .Assign) {
advance(self);
init = parse_expr(self);
}
} else if t != null && kind_is_punct(t.kind, .Assign) {
advance(self);
init = parse_expr(self);
}
var body: Body = .VarDecl(.{
.attr_list = tags,
.pattern = pattern,
.type_expr = te,
.init_expr = init,
});
if is_const { body = .ConstDecl(.{
.attr_list = tags,
.pattern = pattern,
.type_expr = te,
.init_expr = init,
}); }
var ref = new_node(self, body, kw);
t = peek(self);
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
ret ref;
}
fn parse_member_decl(self: *Parser, tags: Vec): usize {
var name = peek(self);
if name == null { ret 0; }
match name.kind { .Ident => {}, _ => { ret 0; } }
advance(self);
var te: usize = 0;
var init: usize = 0;
var t = peek(self);
if t != null && kind_is_punct(t.kind, .Colon) {
advance(self);
parse_tag_list(self, &tags, false, false);
te = parse_type_expr(self);
t = peek(self);
if t != null && kind_is_punct(t.kind, .Assign) {
advance(self);
init = parse_expr(self);
}
} else if t != null && kind_is_punct(t.kind, .Assign) {
advance(self);
init = parse_expr(self);
}
var ref = new_node(self, .MemberDecl(.{
.attr_list = tags,
.name = name.text,
.type_expr = te,
.init_expr = init,
}), name);
t = peek(self);
if t != null && (kind_is_punct(t.kind, .Comma) || kind_is_punct(t.kind, .Semicolon)) {
advance(self);
}
ret ref;
}
fn parse_comptime_decl(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
advance(self);
var body: usize = 0;
var t = peek(self);
self.comptime_depth = self.comptime_depth + 1;
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
else { body = parse_expr(self); }
self.comptime_depth = self.comptime_depth - 1;
ret new_node(self, .ComptimeDecl(.{ .body = body }), kw);
}
fn parse_container_declaration(self: *Parser): usize {
var tags = ref_vec();
parse_tag_list(self, &tags, false, false);
var t = peek(self);
if t == null { ret 0; }
match t.kind {
.Eof => { ret 0; },
.Keyword(k) => {
if @enum2int(k) == @enum2int(Keyword.Fn) { ret parse_fn_decl(self, tags, false); }
if @enum2int(k) == @enum2int(Keyword.Type) { ret parse_type_decl(self, tags); }
if @enum2int(k) == @enum2int(Keyword.Var) {
ret parse_var_const_decl(self, tags, false);
}
if @enum2int(k) == @enum2int(Keyword.Const) {
ret parse_var_const_decl(self, tags, true);
}
if @enum2int(k) == @enum2int(Keyword.Comptime) {
// `comptime fn ...` -> 标记为编译期函数;否则 `comptime { }` 声明。
var nxt = peek_next(self);
if nxt != null && kind_is_keyword(nxt.kind, .Fn) {
advance(self);
ret parse_fn_decl(self, tags, true);
}
ret parse_comptime_decl(self);
}
},
.Ident => { ret parse_member_decl(self, tags); },
.Punct(p) => {
if @enum2int(p) == @enum2int(Punct.LBrace) {
var block = parse_block(self);
ret new_node(self, .TopLevelBlock(.{
.tag_list = tags,
.block = block,
}), t);
}
},
_ => {},
}
parse_error(self, t, "unrecognized declaration");
advance(self);
ret 0;
}
// ---- 类型 ----
fn parse_type_comptime_call(self: *Parser): usize {
var start = peek(self);
if start == null { ret 0; }
var ok = kind_is_punct(start.kind, .At) || kind_is_punct(start.kind, .Sharp);
if !ok { ret 0; }
advance(self);
var name = peek(self);
if name == null { ret 0; }
match name.kind { .Ident => {}, _ => { parse_error(self, name, "expected comptime name"); ret 0; } }
advance(self);
var args = ref_vec();
if at_punct(self, .LParen) {
advance(self);
args = parse_expr_list(self);
expect_punct(self, .RParen, "')' in comptime type");
}
ret new_node(self,
.TypeComptimeCall(.{ .name = name.text, .args = args }), start);
}
fn parse_type_base(self: *Parser): usize {
var bt = peek(self);
if bt == null { ret 0; }
if kind_is_keyword(bt.kind, .Fn) {
advance(self);
if !expect_punct(self, .LParen, "'(' in function type") { ret 0; }
// stage1 先分配 TYPE_FN,再解析参数与返回类型。
var ref = new_node(self, .TypeFn(.{
.is_comptime = false,
.params = ref_vec(),
.ret_type = 0,
}), bt);
var params = ref_vec();
while !self.failed && !at_punct(self, .RParen) {
var pt = parse_type_expr(self);
match Vec.push(&params, &pt as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
if at_punct(self, .Comma) { advance(self); continue; }
break;
}
expect_punct(self, .RParen, "')' in function type");
var ret_ty: usize = 0;
if at_punct(self, .Colon) {
advance(self);
ret_ty = parse_type_expr(self);
}
var fnode = Ast.node(self.ast, ref);
match fnode.body {
.TypeFn(f) => {
f.params = params;
f.ret_type = ret_ty;
fnode.body = .TypeFn(f);
},
_ => {},
}
ret ref;
}
match bt.kind {
.Keyword(k) => {
var kind_name: []u8 = "";
if @enum2int(k) == @enum2int(Keyword.Struct) { kind_name = "struct"; }
else if @enum2int(k) == @enum2int(Keyword.Union) { kind_name = "union"; }
else if @enum2int(k) == @enum2int(Keyword.Enum) { kind_name = "enum"; }
else if @enum2int(k) == @enum2int(Keyword.Shape) { kind_name = "shape"; }
if kind_name.len != 0 {
advance(self);
if !expect_punct(self, .LBrace, "'{' in aggregate type") { ret 0; }
var members = ref_vec();
var ref = new_node(self, .TypeAggregate(.{
.kind_name = ByteView.from_slice(kind_name),
.members = members,
}), bt);
while !self.failed {
var t = peek(self);
if t == null { break; }
match t.kind { .Eof => { break; }, _ => {} }
if kind_is_punct(t.kind, .RBrace) { advance(self); break; }
var member = parse_container_declaration(self);
if member != 0 {
var node = Ast.node(self.ast, ref);
match node.body {
.TypeAggregate(a) => {
match Vec.push(&a.members, &member as *u8) {
.Ok => {},
.Err(e) => { ret 0; },
}
node.body = .TypeAggregate(a);
},
_ => {},
}
}
t = peek(self);
if t != null &&
(kind_is_punct(t.kind, .Comma) || kind_is_punct(t.kind, .Semicolon)) {
advance(self);
}
}
ret ref;
}
},
_ => {},
}
match bt.kind {
.Ident => {
advance(self);
// 类型调用 `Name(T, ...)`(comptime 类型构造,如 `vec(i32)`)。
if at_punct(self, .LParen) {
advance(self);
var ref = new_node(self, .TypePath(.{
.name = bt.text,
.generic_args = ref_vec(),
}), bt);
var args = ref_vec();
while !self.failed && !at_punct(self, .RParen) {
var a = parse_type_expr(self);
match Vec.push(&args, &a as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
if at_punct(self, .Comma) { advance(self); continue; }
break;
}
expect_punct(self, .RParen, "')' in type call");
var tn = Ast.node(self.ast, ref);
match tn.body {
.TypePath(tp) => {
tp.generic_args = args;
tn.body = .TypePath(tp);
},
_ => {},
}
ret ref;
}
ret new_node(self, .TypePath(.{
.name = bt.text,
.generic_args = ref_vec(),
}), bt);
},
.Keyword(k) => {
// stage1 is_builtin_type_kw:bool / any 作为类型路径;stage3 增加 `type`。
if @enum2int(k) == @enum2int(Keyword.Bool) ||
@enum2int(k) == @enum2int(Keyword.Any) ||
@enum2int(k) == @enum2int(Keyword.Type) {
advance(self);
ret new_node(self, .TypePath(.{
.name = bt.text,
.generic_args = ref_vec(),
}), bt);
}
},
.Punct(p) => {
if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) {
ret parse_type_comptime_call(self);
}
},
_ => {},
}
parse_error(self, bt, "unrecognized type");
ret 0;
}
fn parse_type_expr(self: *Parser): usize {
var t = peek(self);
if t == null { ret 0; }
if kind_is_punct(t.kind, .Mul) {
advance(self);
// stage1 先分配 POINTER,再解析 pointee。
var ref = new_node(self, .TypePointer(.{ .pointee = 0 }), t);
var pointee = parse_type_expr(self);
var pn = Ast.node(self.ast, ref);
match pn.body {
.TypePointer(p) => {
p.pointee = pointee;
pn.body = .TypePointer(p);
},
_ => {},
}
ret ref;
}
if kind_is_punct(t.kind, .LBracket) {
advance(self);
var has_len = false;
var is_underscore = false;
var len: usize = 0;
t = peek(self);
if t != null {
match t.kind {
.Int(radix) => {
len = int_token_u64(self, t, radix) as usize;
has_len = true;
advance(self);
},
.Keyword(k) => {
if @enum2int(k) == @enum2int(Keyword.Underscore) {
has_len = true;
is_underscore = true;
advance(self);
}
},
_ => {},
}
}
expect_punct(self, .RBracket, "']' in array type");
var elem = parse_type_expr(self);
if has_len {
ret new_node(self, .TypeArray(.{
.has_len = true,
.len = len as isize,
.is_underscore_len = is_underscore,
.elem_type = elem,
}), t);
}
ret new_node(self, .TypeSlice(.{ .elem_type = elem }), t);
}
if kind_is_punct(t.kind, .LParen) {
advance(self);
var types = ref_vec();
if at_punct(self, .RParen) {
advance(self);
ret new_node(self, .TypeParen(.{ .types = types }), t);
}
var first = parse_type_expr(self);
match Vec.push(&types, &first as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
while at_punct(self, .Comma) {
advance(self);
if at_punct(self, .RParen) { break; }
var next = parse_type_expr(self);
match Vec.push(&types, &next as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
}
expect_punct(self, .RParen, "')' in parenthesized type");
if types.len == 1 { Vec.drop(&types); ret first; }
ret new_node(self, .TypeParen(.{ .types = types }), t);
}
var base = parse_type_base(self);
if self.failed || base == 0 { ret base; }
while at_punct(self, .Dot) {
var dot = peek(self);
advance(self);
var seg = peek(self);
if seg == null { ret base; }
match seg.kind { .Ident => {}, _ => { parse_error(self, seg, "type: expected identifier after '.'"); ret base; } }
advance(self);
base = new_node(self, .ExprField(.{ .obj = base, .name = seg.text }), dot);
}
ret base;
}
// ---- 表达式 ----
type BinInfo = struct { op: Punct, prec: i32, right: bool }
type BinResult = enum { No, Yes: BinInfo }
// 只判断优先级/结合性;算子本体就是 Punct 自身。
fn binop_of(p: Punct): BinResult {
var prec: i32 = 0;
var right = false;
match p {
.Assign, .AddAssign, .SubAssign, .MulAssign, .DivAssign, .ModAssign,
.AndAssign, .OrAssign, .XorAssign, .LShAssign, .RShAssign => {
prec = 1;
right = true;
},
.OrOr => { prec = 2; },
.AndAnd => { prec = 3; },
.Or => { prec = 4; },
.Xor => { prec = 5; },
.And => { prec = 6; },
.Eq, .Neq => { prec = 7; },
.Le, .Ge, .Lt, .Gt => { prec = 8; },
.LeftArrow, .RightArrow, .ArrowBoth, .FatArrowBoth, .RightFatArrow => { prec = 9; },
.Range => { prec = 10; },
.LSh, .RSh => { prec = 11; },
.Add, .Sub => { prec = 12; },
.Mul, .Div, .Mod => { prec = 13; },
_ => { ret .No; },
}
ret .Yes(BinInfo { .op = p, .prec = prec, .right = right });
}
fn mk_bin(self: *Parser, loc: *Token, op: Punct, left: usize, right: usize): usize {
ret new_node(self, .ExprBinary(.{ .op = op, .left = left, .right = right }), loc);
}
fn mk_unary(self: *Parser, loc: *Token, op: Punct, operand: usize): usize {
ret new_node(self, .ExprUnary(.{ .op = op, .operand = operand }), loc);
}
fn parse_expr_prec(self: *Parser, min_prec: i32): usize {
var left = parse_cast(self);
if self.failed { ret left; }
while !self.failed {
var t = peek(self);
if t == null { break; }
var info: BinInfo = undefined;
var found = false;
match t.kind {
.Punct(p) => {
match binop_of(p) {
.Yes(bi) => {
info = bi;
found = true;
},
.No => {},
}
},
_ => {},
}
if !found { break; }
if info.prec < min_prec { break; }
advance(self);
if @enum2int(info.op) == @enum2int(Punct.Range) {
var right: usize = 0;
var nx = peek(self);
if nx != null && can_start_expr(nx.kind) {
right = parse_expr_prec(self, info.prec + 1);
}
left = mk_bin(self, t, info.op, left, right);
continue;
}
var next_min = info.prec + 1;
if info.right { next_min = info.prec; }
var right = parse_expr_prec(self, next_min);
left = mk_bin(self, t, info.op, left, right);
}
ret left;
}
fn parse_expr(self: *Parser): usize {
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 = Ast.push_string(self.ast, decode_str(t.text)) }), t);
},
.Keyword(k) => {
if @enum2int(k) == @enum2int(Keyword.True) {
advance(self);
ret new_node(self, .ExprBoolLit(.{ .value = true }), t);
}
if @enum2int(k) == @enum2int(Keyword.False) {
advance(self);
ret new_node(self, .ExprBoolLit(.{ .value = false }), t);
}
if @enum2int(k) == @enum2int(Keyword.Null) {
advance(self);
ret new_node(self, .ExprNullLit, t);
}
if @enum2int(k) == @enum2int(Keyword.Undefined) {
advance(self);
ret new_node(self, .ExprUndefinedLit, t);
}
if @enum2int(k) == @enum2int(Keyword.If) { ret parse_if_expr(self); }
if @enum2int(k) == @enum2int(Keyword.While) { ret parse_while_expr(self); }
if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); }
if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); }
if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); }
// `comptime <expr>`:编译期表达式(可调用普通函数、返回类型等)。
if @enum2int(k) == @enum2int(Keyword.Comptime) {
advance(self);
var cbody = parse_expr(self);
ret new_node(self, .ExprComptime(.{ .body = cbody }), t);
}
// 类型即表达式:仅在 comptime 上下文内允许 `struct { ... }` 等聚合类型。
if self.comptime_depth > 0 {
if @enum2int(k) == @enum2int(Keyword.Struct) ||
@enum2int(k) == @enum2int(Keyword.Union) ||
@enum2int(k) == @enum2int(Keyword.Enum) ||
@enum2int(k) == @enum2int(Keyword.Shape) {
ret parse_type_base(self);
}
}
parse_error(self, t, "unrecognized expression");
ret 0;
},
.Ident => {
advance(self);
if at_punct(self, .LBrace) && self.stop_agg == 0 {
// stage1 先分配 AGGREGATE_LIT,再解析字段。
var ref = new_node(self, .ExprAggregateLit(.{
.name = t.text,
.fields = ref_vec(),
}), t);
var fields = parse_aggregate_fields(self);
var an = Ast.node(self.ast, ref);
match an.body {
.ExprAggregateLit(a) => {
a.fields = fields;
an.body = .ExprAggregateLit(a);
},
_ => {},
}
ret ref;
}
ret new_node(self, .ExprIdent(.{ .name = t.text }), t);
},
.Punct(p) => {
if @enum2int(p) == @enum2int(Punct.Dot) {
advance(self);
var nx = peek(self);
if nx == null { parse_error(self, t, "unexpected '.'"); ret 0; }
if kind_is_punct(nx.kind, .LBrace) && self.stop_agg == 0 {
var ref = new_node(self, .ExprAggregateLit(.{
.name = ByteView.empty(),
.fields = ref_vec(),
}), t);
var fields = parse_aggregate_fields(self);
var an = Ast.node(self.ast, ref);
match an.body {
.ExprAggregateLit(a) => {
a.fields = fields;
an.body = .ExprAggregateLit(a);
},
_ => {},
}
ret ref;
}
if kind_is_punct(nx.kind, .LBracket) {
advance(self);
var elems = parse_expr_list(self);
if !expect_punct(self, .RBracket, "']' in array literal") { ret 0; }
ret new_node(self, .ExprArrayLit(.{
.has_len = false,
.len = 0,
.is_underscore_len = false,
.elem_type = 0,
.elems = elems,
}), t);
}
var is_ident = false;
match nx.kind { .Ident => { is_ident = true; }, _ => {} }
if is_ident { ret parse_variant_construct(self, t); }
parse_error(self, nx, "expected '{', '[' or identifier after '.'");
ret 0;
}
if @enum2int(p) == @enum2int(Punct.LBracket) { ret parse_array_literal(self); }
if @enum2int(p) == @enum2int(Punct.LBrace) { ret parse_block(self); }
if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) {
ret parse_comptime_call(self);
}
if @enum2int(p) == @enum2int(Punct.LParen) {
advance(self);
var elems = ref_vec();
if at_punct(self, .RParen) {
advance(self);
ret new_node(self, .ExprTuple(.{ .items = elems }), t);
}
var first = parse_expr(self);
if at_punct(self, .Comma) {
match Vec.push(&elems, &first as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
while at_punct(self, .Comma) {
advance(self);
if at_punct(self, .RParen) { break; }
var nx = parse_expr(self);
match Vec.push(&elems, &nx as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
}
expect_punct(self, .RParen, "')' in tuple literal");
ret new_node(self, .ExprTuple(.{ .items = elems }), t);
}
expect_punct(self, .RParen, "')' in paren expression");
Vec.drop(&elems);
ret first;
}
},
_ => {},
}
parse_error(self, t, "unrecognized expression");
ret 0;
}
fn parse_aggregate_fields(self: *Parser): Vec {
var fields = ref_vec();
if !expect_punct(self, .LBrace, "'{' in aggregate literal") { ret fields; }
while !self.failed {
var t = peek(self);
if t == null { break; }
var stop = false;
match t.kind { .Eof => { stop = true; }, _ => {} }
if kind_is_punct(t.kind, .RBrace) { stop = true; }
if stop { break; }
if !kind_is_punct(t.kind, .Dot) { break; }
advance(self);
var name = peek(self);
if name == null { break; }
match name.kind { .Ident => {}, _ => { break; } }
advance(self);
var item = new_node(self,
.StructInitItem(.{ .name = name.text, .expr = 0 }), name);
if at_punct(self, .Assign) {
advance(self);
var e = parse_expr(self);
var n = Ast.node(self.ast, item);
match n.body {
.StructInitItem(s) => {
s.expr = e;
n.body = .StructInitItem(s);
},
_ => {},
}
}
match Vec.push(&fields, &item as *u8) { .Ok => {}, .Err(e) => { ret fields; } }
var nt = peek(self);
if nt != null && (kind_is_punct(nt.kind, .Comma) || kind_is_punct(nt.kind, .Semicolon)) {
advance(self);
continue;
}
break;
}
expect_punct(self, .RBrace, "'}' in aggregate literal");
ret fields;
}
fn parse_array_literal(self: *Parser): usize {
var start = peek(self);
if start == null { ret 0; }
if !kind_is_punct(start.kind, .LBracket) { ret 0; }
advance(self);
var has_len = false;
var len: usize = 0;
var is_underscore = false;
var t = peek(self);
if t != null {
match t.kind {
.Int(radix) => {
len = int_token_u64(self, t, radix) as usize;
has_len = true;
advance(self);
},
.Keyword(k) => {
if @enum2int(k) == @enum2int(Keyword.Underscore) {
has_len = true;
is_underscore = true;
advance(self);
}
},
_ => {},
}
}
if !expect_punct(self, .RBracket, "']' in array literal") { ret 0; }
var elem_type = parse_type_expr(self);
if !expect_punct(self, .LBracket, "'[' in array literal") { ret 0; }
var elems = parse_expr_list(self);
if !expect_punct(self, .RBracket, "']' in array literal") { ret 0; }
ret new_node(self, .ExprArrayLit(.{
.has_len = has_len,
.len = len,
.is_underscore_len = is_underscore,
.elem_type = elem_type,
.elems = elems,
}), start);
}
fn parse_comptime_call(self: *Parser): usize {
var start = peek(self);
if start == null { ret 0; }
var ok = kind_is_punct(start.kind, .At) || kind_is_punct(start.kind, .Sharp);
if !ok { ret 0; }
advance(self);
var name = peek(self);
if name == null { ret 0; }
match name.kind { .Ident => {}, _ => { parse_error(self, name, "expected comptime name"); ret 0; } }
advance(self);
var args = ref_vec();
if at_punct(self, .LParen) {
advance(self);
args = parse_expr_list(self);
if !expect_punct(self, .RParen, "')' in comptime call") { ret 0; }
}
// `@name(args){ ... }`:构造该编译期类型的值(聚合初始化)。
if at_punct(self, .LBrace) && self.stop_agg == 0 {
var fields = parse_aggregate_fields(self);
ret new_node(self, .ExprComptimeInit(.{
.name = name.text,
.args = args,
.fields = fields,
}), start);
}
ret new_node(self, .ExprComptimeCall(.{ .name = name.text, .args = args }), start);
}
fn parse_label(self: *Parser): usize {
var t = peek(self);
if t == null { ret 0; }
if !kind_is_punct(t.kind, .Dollar) { ret 0; }
advance(self);
var name = peek(self);
if name == null { ret 0; }
match name.kind { .Ident => {}, _ => { parse_error(self, name, "expected identifier after '$'"); ret 0; } }
advance(self);
ret new_node(self, .Label(.{ .name = name.text }), t);
}
fn parse_expr_list(self: *Parser): Vec {
var vec = ref_vec();
if self.failed { ret vec; }
var t = peek(self);
if t == null || !can_start_expr(t.kind) { ret vec; }
var first = parse_expr(self);
match Vec.push(&vec, &first as *u8) { .Ok => {}, .Err(e) => { ret vec; } }
while !self.failed {
var nt = peek(self);
if nt == null || !kind_is_punct(nt.kind, .Comma) { break; }
advance(self);
var nx = peek(self);
if nx == null || !can_start_expr(nx.kind) { break; }
var e = parse_expr(self);
match Vec.push(&vec, &e as *u8) { .Ok => {}, .Err(er) => { ret vec; } }
}
ret vec;
}
// ---- if / while / loop / for / match ----
fn parse_if_expr(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .If) { ret 0; }
advance(self);
var is_ifvar = false;
var var_decl: usize = 0;
var t = peek(self);
if t != null && kind_is_keyword(t.kind, .Var) {
advance(self);
is_ifvar = true;
var pattern = parse_pattern(self);
if pattern == 0 { ret 0; }
var te: usize = 0;
var init: usize = 0;
t = peek(self);
if t != null && kind_is_punct(t.kind, .Colon) {
advance(self);
te = parse_type_expr(self);
t = peek(self);
if t != null && kind_is_punct(t.kind, .Assign) {
advance(self);
self.stop_agg = self.stop_agg + 1;
init = parse_expr(self);
self.stop_agg = self.stop_agg - 1;
} else {
parse_error(self, t, "expected '=' in if var");
ret 0;
}
} else if t != null && kind_is_punct(t.kind, .Assign) {
advance(self);
self.stop_agg = self.stop_agg + 1;
init = parse_expr(self);
self.stop_agg = self.stop_agg - 1;
} else {
parse_error(self, t, "expected type annotation or '=' in if var");
ret 0;
}
var_decl = new_node(self, .VarDecl(.{
.attr_list = ref_vec(),
.pattern = pattern,
.type_expr = te,
.init_expr = init,
}), kw);
} else {
self.stop_agg = self.stop_agg + 1;
var_decl = parse_expr(self);
self.stop_agg = self.stop_agg - 1;
if var_decl == 0 { ret 0; }
}
var then_block = parse_block(self);
if then_block == 0 { ret 0; }
var else_branch: usize = 0;
t = peek(self);
if t != null && kind_is_keyword(t.kind, .Else) {
advance(self);
t = peek(self);
if t != null && kind_is_keyword(t.kind, .If) { else_branch = parse_if_expr(self); }
else { else_branch = parse_block(self); }
}
if is_ifvar {
ret new_node(self, .ExprIfvar(.{
.var_decl = var_decl,
.then_block = then_block,
.else_branch = else_branch,
}), kw);
}
ret new_node(self, .ExprIf(.{
.cond = var_decl,
.then_block = then_block,
.else_branch = else_branch,
}), kw);
}
fn parse_while_expr(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .While) { ret 0; }
advance(self);
self.stop_agg = self.stop_agg + 1;
var cond = parse_expr(self);
self.stop_agg = self.stop_agg - 1;
if cond == 0 { ret 0; }
var body = parse_block(self);
if body == 0 { ret 0; }
var else_branch: usize = 0;
if at_keyword(self, .Else) {
advance(self);
if at_punct(self, .LBrace) { else_branch = parse_block(self); }
else { else_branch = parse_expr(self); }
}
ret new_node(self, .ExprWhile(.{
.label = 0,
.cond = cond,
.body = body,
.else_branch = else_branch,
}), kw);
}
fn parse_loop_expr(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Loop) { ret 0; }
advance(self);
var body = parse_block(self);
ret new_node(self, .ExprLoop(.{ .label = 0, .body = body }), kw);
}
fn parse_for_expr(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .For) { ret 0; }
advance(self);
var pattern = parse_pattern(self);
if pattern == 0 { ret 0; }
if !expect_punct(self, .Colon, "':' after for pattern") { ret 0; }
var iter_exprs = ref_vec();
self.stop_agg = self.stop_agg + 1;
var e0 = parse_expr(self);
match Vec.push(&iter_exprs, &e0 as *u8) { .Ok => {}, .Err(er) => { ret 0; } }
while at_punct(self, .Comma) {
advance(self);
var e = parse_expr(self);
match Vec.push(&iter_exprs, &e as *u8) { .Ok => {}, .Err(er) => { ret 0; } }
}
self.stop_agg = self.stop_agg - 1;
var body = parse_block(self);
if body == 0 { ret 0; }
var else_branch: usize = 0;
if at_keyword(self, .Else) {
advance(self);
if at_punct(self, .LBrace) { else_branch = parse_block(self); }
else { else_branch = parse_expr(self); }
}
ret new_node(self, .ExprFor(.{
.label = 0,
.iter_exprs = iter_exprs,
.pattern = pattern,
.body = body,
.else_branch = else_branch,
}), kw);
}
fn parse_match_expr(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Match) { ret 0; }
advance(self);
self.stop_agg = self.stop_agg + 1;
var matched = parse_expr(self);
self.stop_agg = self.stop_agg - 1;
if !expect_punct(self, .LBrace, "'{' in match") { ret 0; }
var patterns = ref_vec();
var bodies = ref_vec();
while !self.failed {
var t = peek(self);
if t == null { break; }
var stop = false;
match t.kind { .Eof => { stop = true; }, _ => {} }
if kind_is_punct(t.kind, .RBrace) { stop = true; }
if stop { break; }
var pat = parse_pattern(self);
if pat == 0 { break; }
if !expect_punct(self, .RightFatArrow, "'=>' in match arm") { break; }
var body = parse_block(self);
match Vec.push(&patterns, &pat as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
match Vec.push(&bodies, &body as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
if at_punct(self, .Comma) { advance(self); }
}
if !expect_punct(self, .RBrace, "'}' in match") { ret 0; }
// stage1 先分配 MATCH_EXPR,再逐个分配 MATCH_ARM。
var ref = new_node(self, .ExprMatch(.{
.matched_expr = matched,
.arms = ref_vec(),
}), kw);
var arms = ref_vec();
var i: usize = 0;
while i < patterns.len {
var pv: usize = 0;
var bv: usize = 0;
match Vec.get(&patterns, i) { .Ok(p) => { pv = (p as *usize).*; }, .Err(e) => {} }
match Vec.get(&bodies, i) { .Ok(p) => { bv = (p as *usize).*; }, .Err(e) => {} }
var arm = new_node(self, .MatchArm(.{ .pattern = pv, .body = bv }), null);
match Vec.push(&arms, &arm as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
i = i + 1;
}
Vec.drop(&patterns);
Vec.drop(&bodies);
var mn = Ast.node(self.ast, ref);
match mn.body {
.ExprMatch(m) => {
m.arms = arms;
mn.body = .ExprMatch(m);
},
_ => {},
}
ret ref;
}
// ---- 语句 ----
fn parse_ret_stmt(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Ret) { ret 0; }
advance(self);
var expr: usize = 0;
var t = peek(self);
var can = false;
if t != null {
can = can_start_expr(t.kind);
// comptime 上下文:允许返回类型表达式(如 `ret struct { ... };`)。
if !can && self.comptime_depth > 0 {
if kind_is_keyword(t.kind, .Struct) || kind_is_keyword(t.kind, .Union) ||
kind_is_keyword(t.kind, .Enum) || kind_is_keyword(t.kind, .Shape) {
can = true;
}
}
}
if can { expr = parse_expr(self); }
t = peek(self);
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
ret new_node(self, .StatementRet(.{ .expr = expr }), kw);
}
fn parse_break_stmt(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Break) { ret 0; }
advance(self);
var label = parse_label(self);
var expr: usize = 0;
var t = peek(self);
if t != null && can_start_expr(t.kind) { expr = parse_expr(self); }
t = peek(self);
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
ret new_node(self, .StatementBreak(.{ .label = label, .expr = expr }), kw);
}
fn parse_continue_stmt(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Continue) { ret 0; }
advance(self);
var label = parse_label(self);
var t = peek(self);
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
ret new_node(self, .StatementContinue(.{ .label = label }), kw);
}
fn parse_defer_stmt(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Defer) { ret 0; }
advance(self);
var body: usize = 0;
var t = peek(self);
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
else if t != null && can_start_statement(t.kind) { body = parse_statement(self); }
else {
body = parse_expr(self);
t = peek(self);
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
}
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
}
fn parse_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 expect_contains(buf: *ByteBuffer, needle: []u8) {
var actual = ByteBuffer.as_slice(buf.*);
if needle.len > actual.len { @assert(false, "contains length"); }
var i: usize = 0;
while i + needle.len <= actual.len {
var j: usize = 0;
var ok = true;
while j < needle.len {
if actual.ptr[i + j] != needle.ptr[j] { ok = false; break; }
j = j + 1;
}
if ok { ret; }
i = i + 1;
}
@assert(false, "expected substring not found");
}
// stage3 语法:`comptime fn ... (t: type) type { ... }` 与省略冒号的返回类型。
fn test_parse_comptime_fn() {
var src: []u8 = "comptime fn vec(t: type) type { ret t; }\n";
var toks = lex_tokens(src);
defer { Vec.drop(&toks); }
var sources = set_new();
defer { set_drop(&sources); }
match set_add(&sources, ByteView.from_slice("test.spl"), ByteView.from_slice(src)) {
.Ok(index) => {},
.Err(error) => { @assert(false, "set_add"); },
}
var ast: Ast = undefined;
match Ast.new(&sources) {
.Ok(v) => { ast = v; },
.Err(error) => { @assert(false, "ast new"); },
}
defer { Ast.drop(&ast); }
var p = new(&ast, toks, null);
parse(&p);
@assert(!p.failed, "comptime fn parse ok");
@assert(p.error_count == 0, "comptime fn no errors");
var buf = ByteBuffer.new();
defer { ByteBuffer.drop(&buf); }
@assert(Ast.dump(&ast, &buf), "dump");
expect_contains(&buf, "fn_decl");
expect_contains(&buf, "type_path");
}
fn main() {
test_peek_advance();
test_new_node_span();
test_parse_decls();
test_parse_main();
test_big_int_literal();
test_parse_comptime_fn();
}