2193 lines
75 KiB
Plaintext
2193 lines
75 KiB
Plaintext
// 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();
|
||
}
|