## ✅ Fixed Issues ### 1. `local` keyword tokenization (commit 9aab64f7) - Added Stage-3 gate for LOCAL/TRY/CATCH/THROW keywords - LOCAL now only active when NYASH_PARSER_STAGE3=1 ### 2. `env.local.get` keyword conflict - File: `lang/src/compiler/entry/compiler_stageb.hako:21-23` - Problem: `.local` in member access tokenized as `.LOCAL` keyword - Fix: Commented out `env.local.get("HAKO_SOURCE")` line - Fallback: Use `--source` argument (still functional) ### 3. `flow` keyword missing - Added FLOW to TokenType enum (`src/tokenizer/kinds.rs`) - Added "flow" → TokenType::FLOW mapping (`src/tokenizer/lex_ident.rs`) - Added FLOW to Stage-3 gate (requires NYASH_PARSER_STAGE3=1) - Added FLOW to parser statement dispatch (`src/parser/statements/mod.rs`) - Added FLOW to declaration handler (`src/parser/statements/declarations.rs`) - Updated box_declaration parser to accept BOX or FLOW (`src/parser/declarations/box_definition.rs`) - Treat `flow FooBox {}` as syntactic sugar for `box FooBox {}` ### 4. Module namespace conversion - Renamed `lang.compiler.builder.ssa.local` → `localvar` (avoid keyword) - Renamed file `local.hako` → `local_ssa.hako` - Converted 152 path-based using statements to namespace format - Added 26+ entries to `nyash.toml` [modules] section ## ⚠️ Remaining Issues ### Stage-B selfhost compiler performance - Stage-B compiler not producing output (hangs/times out after 10+ seconds) - Excessive PHI debug output suggests compilation loop issue - Needs investigation: infinite loop or N² algorithm in hako compiler ### Fallback JSON version mismatch - Rust fallback (`--emit-mir-json`) emits MIR v1 JSON (schema_version: "1.0") - Smoke tests expect MIR v0 JSON (`"version":0, "kind":"Program"`) - stageb_helpers.sh fallback needs adjustment ## Test Status - Parse errors: FIXED ✅ - Keyword conflicts: FIXED ✅ - Stage-B smoke tests: STILL FAILING ❌ (performance issue) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
440 lines
12 KiB
Plaintext
440 lines
12 KiB
Plaintext
// mir_builder_min.nyash — Minimal MIR(JSON v0) builder for selfhost tests
|
|
// Scope: selfhost only (apps/selfhost/...); no core/runtime changes.
|
|
|
|
using selfhost.shared.common.string_helpers as StringHelpers
|
|
using selfhost.shared.common.box_helpers as BoxHelpers
|
|
using lang.compiler.emit.common.json_emit as JsonEmitBox
|
|
using lang.compiler.emit.common.mir_emit as MirEmitBox
|
|
using lang.compiler.emit.common.call_emit as CallEmitBox
|
|
using lang.compiler.emit.common.newbox_emit as NewBoxEmitBox
|
|
using selfhost.shared.json.json_inst_encode_box as JsonInstEncodeBox
|
|
|
|
box MirJsonBuilderMin {
|
|
buf: StringBox
|
|
phase: IntegerBox
|
|
first_inst: IntegerBox
|
|
blocks: ArrayBox
|
|
cur_block_index: IntegerBox
|
|
fn_name: StringBox
|
|
trace: IntegerBox
|
|
verify: IntegerBox
|
|
|
|
birth() {
|
|
me.buf = ""
|
|
me.phase = 0
|
|
me.first_inst = 1
|
|
me.blocks = new ArrayBox()
|
|
me.cur_block_index = -1
|
|
me.fn_name = ""
|
|
me.trace = 0
|
|
me.verify = 0
|
|
}
|
|
|
|
// Internal helpers
|
|
_get_buf() { return me.buf }
|
|
_set_buf(s) { me.buf = s return me }
|
|
_append(s) {
|
|
me.buf = me.buf + s
|
|
return me
|
|
}
|
|
_int_to_str(n) { return StringHelpers.int_to_str(n) }
|
|
_to_i64(x) { return StringHelpers.to_i64(x) }
|
|
|
|
_quote(s) { return StringHelpers.json_quote(s) }
|
|
|
|
_ids_range_json(start, count) {
|
|
local i = 0
|
|
local out = "["
|
|
loop (i < count) {
|
|
if i > 0 { out = out + "," }
|
|
out = out + me._int_to_str(start + i)
|
|
i = i + 1
|
|
}
|
|
out = out + "]"
|
|
return out
|
|
}
|
|
|
|
_ids_array_from_json_text(arr_text) {
|
|
local out = new ArrayBox()
|
|
if arr_text == null { return out }
|
|
local s = "" + arr_text
|
|
local i = 0
|
|
loop (i < s.length()) {
|
|
local ch = s.substring(i, i+1)
|
|
if ch >= "0" && ch <= "9" {
|
|
local j = i
|
|
loop (j < s.length()) {
|
|
local cj = s.substring(j, j+1)
|
|
if !(cj >= "0" && cj <= "9") { break }
|
|
j = j + 1
|
|
}
|
|
local num = me._to_i64(s.substring(i, j))
|
|
out.push(num)
|
|
i = j
|
|
} else {
|
|
i = i + 1
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
_ensure_phase(want) {
|
|
if me.phase + 1 < want { return me }
|
|
return me
|
|
}
|
|
|
|
// Public builder steps
|
|
start_module() {
|
|
if me.trace == 1 { print("[DEBUG start_module] starting") }
|
|
me.phase = 1
|
|
return me._append("{\"functions\":[")
|
|
}
|
|
|
|
start_function(name) {
|
|
if me.trace == 1 { print("[DEBUG start_function] name=" + name) }
|
|
me.phase = 2
|
|
me.fn_name = name
|
|
me.blocks = new ArrayBox()
|
|
me.cur_block_index = -1
|
|
if me.trace == 1 { print("[DEBUG start_function] after set, fn_name=" + me.fn_name) }
|
|
local b = "{\"name\":" + me._quote(name) + ",\"params\":[],\"blocks\":["
|
|
return me._append(b)
|
|
}
|
|
|
|
start_block(id) {
|
|
me.phase = 3
|
|
me.first_inst = 1
|
|
local blk = { id: id, instructions: new ArrayBox() }
|
|
me.blocks.push(blk)
|
|
me.cur_block_index = me.blocks.length() - 1
|
|
local b = "{\"id\":" + me._int_to_str(id) + ",\"instructions\":["
|
|
return me._append(b)
|
|
}
|
|
|
|
end_block_continue() {
|
|
return me._append("]},")
|
|
}
|
|
|
|
_comma_if_needed() {
|
|
if me.first_inst == 1 {
|
|
me.first_inst = 0
|
|
return me
|
|
}
|
|
return me._append(",")
|
|
}
|
|
|
|
add_const(dst, val) {
|
|
me._comma_if_needed()
|
|
local node = MirEmitBox.make_const(dst, val)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_compare(kind, lhs, rhs, dst) {
|
|
me._comma_if_needed()
|
|
local node = MirEmitBox.make_compare(kind, lhs, rhs, dst)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_binop(kind, lhs, rhs, dst) {
|
|
me._comma_if_needed()
|
|
local node = { op:"binop", op_kind:kind, lhs:lhs, rhs:rhs, dst:dst }
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_ret(val) {
|
|
me._comma_if_needed()
|
|
local node = MirEmitBox.make_ret(val)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_call_ids(name, args_json_text, dst) {
|
|
me._comma_if_needed()
|
|
local args = me._ids_array_from_json_text(args_json_text)
|
|
local node = CallEmitBox.make_call(name, args, dst)
|
|
node.set("args_text", args_json_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_call_range(name, start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) {
|
|
args.push(start + i)
|
|
i = i + 1
|
|
}
|
|
local args_text = me._ids_range_json(start, count)
|
|
local node = CallEmitBox.make_call(name, args, dst)
|
|
node.set("args_text", args_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_boxcall_range(method, recv_id, args_start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) {
|
|
args.push(args_start + i)
|
|
i = i + 1
|
|
}
|
|
local args_text = me._ids_range_json(args_start, count)
|
|
local node = CallEmitBox.make_boxcall(method, recv_id, args, dst)
|
|
node.set("args_text", args_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_newbox_range(box_type, args_start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) {
|
|
args.push(args_start + i)
|
|
i = i + 1
|
|
}
|
|
local node = NewBoxEmitBox.with_args_array(NewBoxEmitBox.make_new(box_type, args, dst), args)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_newbox_ids(box_type, args_json_text, dst) {
|
|
me._comma_if_needed()
|
|
local args = me._ids_array_from_json_text(args_json_text)
|
|
local node = NewBoxEmitBox.make_new(box_type, args, dst)
|
|
node = NewBoxEmitBox.with_args_text(node, args_json_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
_args_array_json_from_ids(ids_start, count) {
|
|
return me._ids_range_json(ids_start, count)
|
|
}
|
|
|
|
add_mir_call_global_ids(name, args_json_text, dst) {
|
|
me._comma_if_needed()
|
|
local args = me._ids_array_from_json_text(args_json_text)
|
|
local node = CallEmitBox.make_mir_call_global(name, args, dst)
|
|
node.set("args_text", args_json_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_extern_ids(name, args_json_text, dst) {
|
|
me._comma_if_needed()
|
|
local args = me._ids_array_from_json_text(args_json_text)
|
|
local node = CallEmitBox.make_mir_call_extern(name, args, dst)
|
|
node.set("args_text", args_json_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_method_ids(method, recv_id, args_json_text, dst) {
|
|
me._comma_if_needed()
|
|
local args = me._ids_array_from_json_text(args_json_text)
|
|
local node = CallEmitBox.make_mir_call_method(method, recv_id, args, dst)
|
|
node.set("args_text", args_json_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_constructor_ids(box_type, args_json_text, dst) {
|
|
me._comma_if_needed()
|
|
local args = me._ids_array_from_json_text(args_json_text)
|
|
local node = CallEmitBox.make_mir_call_constructor(box_type, args, dst)
|
|
node.set("args_text", args_json_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_global_range(name, start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) {
|
|
args.push(start + i)
|
|
i = i + 1
|
|
}
|
|
local args_text = me._ids_range_json(start, count)
|
|
local node = CallEmitBox.make_mir_call_global(name, args, dst)
|
|
node.set("args_text", args_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_extern_range(name, start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) { args.push(start + i) i = i + 1 }
|
|
local args_text = me._ids_range_json(start, count)
|
|
local node = CallEmitBox.make_mir_call_extern(name, args, dst)
|
|
node.set("args_text", args_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_method_range(method, recv_id, args_start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) {
|
|
args.push(args_start + i)
|
|
i = i + 1
|
|
}
|
|
local args_text = me._ids_range_json(args_start, count)
|
|
local node = CallEmitBox.make_mir_call_method(method, recv_id, args, dst)
|
|
node.set("args_text", args_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_mir_call_constructor_range(box_type, args_start, count, dst) {
|
|
me._comma_if_needed()
|
|
local args = new ArrayBox()
|
|
local i = 0
|
|
loop(i < count) {
|
|
args.push(args_start + i)
|
|
i = i + 1
|
|
}
|
|
local args_text = me._ids_range_json(args_start, count)
|
|
local node = CallEmitBox.make_mir_call_constructor(box_type, args, dst)
|
|
node.set("args_text", args_text)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_branch(cond, then_id, else_id) {
|
|
me._comma_if_needed()
|
|
local node = MirEmitBox.make_branch(cond, then_id, else_id)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_jump(target) {
|
|
me._comma_if_needed()
|
|
local node = MirEmitBox.make_jump(target)
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
add_copy(dst, src) {
|
|
me._comma_if_needed()
|
|
local node = { op:"copy", dst:dst, src:src }
|
|
me._push_inst_map(node)
|
|
return me._append(JsonEmitBox.to_json(node))
|
|
}
|
|
|
|
end_all() {
|
|
me.phase = 5
|
|
return me._append("]}]}]}")
|
|
}
|
|
|
|
to_string() { return me._get_buf() }
|
|
|
|
_cur_insts() {
|
|
local blks = me.blocks
|
|
if blks == null { return null }
|
|
local n = 0
|
|
n = BoxHelpers.array_len(blks)
|
|
if n <= 0 { return null }
|
|
local idx = me.cur_block_index
|
|
if idx == null { idx = -1 }
|
|
if idx < 0 || idx >= n { idx = n - 1 }
|
|
local blk = BoxHelpers.array_get(blks, idx)
|
|
if blk == null { return null }
|
|
return BoxHelpers.map_get(blk, "instructions")
|
|
}
|
|
|
|
verify_builder_state() { return 0 }
|
|
|
|
_verify_enabled() {
|
|
return me.verify
|
|
}
|
|
|
|
_trace_enabled() {
|
|
return me.trace
|
|
}
|
|
|
|
_push_inst(node) {
|
|
local before = 0
|
|
local insts0 = me._cur_insts()
|
|
before = BoxHelpers.array_len(insts0)
|
|
if insts0 == null || insts0.push == null { return }
|
|
insts0.push(node)
|
|
if me._verify_enabled() == 1 {
|
|
local insts1 = me._cur_insts()
|
|
local after = BoxHelpers.array_len(insts1)
|
|
local op_str = BoxHelpers.map_get(node, "op")
|
|
if after != before + 1 { print("[builder-verify] size mismatch before=" + before + " after=" + after + " op=" + op_str) }
|
|
}
|
|
if me._trace_enabled() == 1 {
|
|
local op_str_trace = BoxHelpers.map_get(node, "op")
|
|
print("[builder-trace] push op=" + op_str_trace)
|
|
}
|
|
}
|
|
|
|
_push_inst_map(node) { return me._push_inst(node) }
|
|
|
|
enable_trace(on) {
|
|
if on == null { on = 1 }
|
|
me.trace = on
|
|
return me
|
|
}
|
|
|
|
enable_verify(on) {
|
|
if on == null { on = 1 }
|
|
me.verify = on
|
|
return me
|
|
}
|
|
|
|
get_current_instructions() { return me._cur_insts() }
|
|
|
|
get_blocks_array() { return me.blocks }
|
|
|
|
get_function_structure() {
|
|
local blks = me.get_blocks_array()
|
|
return { name: me.fn_name, params: new ArrayBox(), blocks: blks }
|
|
}
|
|
|
|
emit_to_string() { return me.to_string() }
|
|
|
|
_inst_json(node) { return JsonInstEncodeBox.encode(node) }
|
|
|
|
to_string_rebuild() {
|
|
local name = me.fn_name
|
|
local blks = me.get_blocks_array()
|
|
local blks_size_str = "null"
|
|
local blks_len = BoxHelpers.array_len(blks)
|
|
if blks_len >= 0 { blks_size_str = me._int_to_str(blks_len) }
|
|
print("[DEBUG rebuild] fn_name=" + name + " blks.length()=" + blks_size_str)
|
|
local out = "{\"functions\":[{\"name\":" + me._quote(name) + ",\"params\":[],\"blocks\":["
|
|
local n = blks_len
|
|
print("[DEBUG rebuild] n=" + me._int_to_str(n))
|
|
local i = 0
|
|
loop (i < n) {
|
|
if i > 0 { out = out + "," }
|
|
local blk = BoxHelpers.array_get(blks, i)
|
|
local bid = BoxHelpers.map_get(blk, "id")
|
|
out = out + "{\"id\":" + me._int_to_str(bid) + ",\"instructions\":["
|
|
local insts = BoxHelpers.map_get(blk, "instructions")
|
|
local m = BoxHelpers.array_len(insts)
|
|
local j = 0
|
|
loop (j < m) {
|
|
if j > 0 { out = out + "," }
|
|
out = out + me._inst_json(BoxHelpers.array_get(insts, j))
|
|
j = j + 1
|
|
}
|
|
out = out + "]}"
|
|
i = i + 1
|
|
}
|
|
out = out + "]}]}"
|
|
return out
|
|
}
|
|
}
|