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