Files
hakorune/lang/src/shared/json/mir_builder_min.hako
nyash-codex df9068a555 feat(stage-b): Add FLOW keyword support + fix Stage-3 keyword conflicts
##  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>
2025-11-02 04:13:17 +09:00

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
}
}