fix(mir): PHI検証panic修正 - update_cfg()を検証前に呼び出し

A案実装: debug_verify_phi_inputs呼び出し前にCFG predecessorを更新

修正箇所(7箇所):
- src/mir/builder/phi.rs:50, 73, 132, 143
- src/mir/builder/ops.rs:273, 328, 351

根本原因:
- Branch/Jump命令でsuccessorは即座に更新
- predecessorはupdate_cfg()で遅延再構築
- PHI検証が先に実行されてpredecessor未更新でpanic

解決策:
- 各debug_verify_phi_inputs呼び出し前に
  if let Some(func) = self.current_function.as_mut() {
      func.update_cfg();
  }
  を挿入してCFGを同期

影響: if/else文、論理演算子(&&/||)のPHI生成が正常動作
This commit is contained in:
nyash-codex
2025-11-01 13:28:56 +09:00
parent 149ec61d4d
commit 6a452b2dca
174 changed files with 2432 additions and 1014 deletions

View File

@ -12,3 +12,8 @@ Policy
- VM engines live under `lang/src/vm/engines/` (Hakorune/Mini), with shared helpers in `vm/boxes/`.
- Keep imports across these boundaries minimal and documented.
Grammar Notes (parser parity)
- Semicolons are accepted as optional statement separators (default ON).
- Both newline and `;` delimit statements; trailing `};` is allowed.
- Consecutive `;;` are treated as empty statements (no-op).
- Env toggle (opt-out): set `NYASH_PARSER_ALLOW_SEMICOLON=0|false|off` to disable.

View File

@ -17,11 +17,11 @@ static box RewriteKnown {
// Find name start
local name_key = "\"name\":\""
local np = s.indexOf(name_key, p)
if np < 0 { out = out + s.substring(p, s.size()) i = s.size() break }
local name_start = np + name_key.size()
if np < 0 { out = out + s.substring(p, s.length()) i = s.length() break }
local name_start = np + name_key.length()
// Find name end quote
local name_end = s.indexOf("\"", name_start)
if name_end < 0 { out = out + s.substring(p, s.size()) i = s.size() break }
if name_end < 0 { out = out + s.substring(p, s.length()) i = s.length() break }
local name = s.substring(name_start, name_end)
// If already canonical, just append segment as-is
@ -35,7 +35,7 @@ static box RewriteKnown {
local args_key = "\"args\":["
local ap = s.indexOf(args_key, name_end)
if ap < 0 { out = out + s.substring(p, name_end) i = name_end continue }
local lb = ap + args_key.size() - 1 // points to '['
local lb = ap + args_key.length() - 1 // points to '['
// Find closing bracket
local rb = s.indexOf("]", lb + 1)
if rb < 0 { out = out + s.substring(p, name_end) i = name_end continue }
@ -44,7 +44,7 @@ static box RewriteKnown {
local body = s.substring(lb + 1, rb)
local trimmed = me._trim(body)
local arity = 0
if trimmed.size() == 0 {
if trimmed.length() == 0 {
arity = 0
} else {
// guard: if body contains non-digit/comma/space, skip rewrite (fail-safe)
@ -61,13 +61,13 @@ static box RewriteKnown {
i = name_end
}
// Append the tail
out = out + s.substring(i, s.size())
out = out + s.substring(i, s.length())
return out
}
_trim(text) {
local a = 0
local b = text.size()
local b = text.length()
loop(a < b && me._is_space(text.substring(a,a+1))) { a = a + 1 }
loop(b > a && me._is_space(text.substring(b-1,b))) { b = b - 1 }
return text.substring(a,b)
@ -76,7 +76,7 @@ static box RewriteKnown {
_is_digit(ch) { return ch >= "0" && ch <= "9" }
_is_simple_ids(text) {
local i = 0
loop(i < text.size()) {
loop(i < text.length()) {
local ch = text.substring(i,i+1)
if !(me._is_space(ch) || ch == "," || me._is_digit(ch)) { return false }
i = i + 1
@ -86,7 +86,7 @@ static box RewriteKnown {
_count_commas(text) {
local i = 0
local n = 0
loop(i < text.size()) { if text.substring(i,i+1) == "," { n = n + 1 } i = i + 1 }
loop(i < text.length()) { if text.substring(i,i+1) == "," { n = n + 1 } i = i + 1 }
return n
}
_itoa(n) {

View File

@ -191,6 +191,6 @@ static box CondInserter {
local e = finish
if s < 0 { s = 0 }
if e < s { e = s }
return call("String.substring/2", text, s, e)
return text.substring(s, e)
}
}

View File

@ -24,7 +24,7 @@ static box LocalSSA {
local p = hay.indexOf(needle)
if p < 0 { break }
n = n + 1
hay = hay.substring(p + needle.size(), hay.size())
hay = hay.substring(p + needle.length(), hay.length())
}
return n
}

View File

@ -31,7 +31,7 @@ static box CallEmitBox {
}
make_mir_call_module(name, arg_ids, dst) {
local canon = me._canonical_module_name(name, arg_ids.size())
local canon = me._canonical_module_name(name, arg_ids.length())
local callee = {type: "ModuleFunction", name: canon}
return {op: "mir_call", dst: dst, callee: callee, args: arg_ids}
}

View File

@ -1,6 +1,6 @@
// newbox_emit_box.hako — NewBoxEmitBox: construct MIR(JSON v0) node for newbox
// Responsibility: return MapBox node for { op: newbox, box_type, args, dst }.
using "apps/lib/json_native/stringify.hako" as JSON
using "lang/src/shared/json/stringify.hako" as JSON
static box NewBoxEmitBox {
make_new(box_type, arg_ids, dst) {

View File

@ -3,7 +3,7 @@
// Future: add Binary/Compare/ExternCall/BoxCall lowering incrementally.
using selfhost.common.json.mir_builder_min as MirJsonBuilderMin
using "apps/lib/json_native/stringify.hako" as JSON
using "lang/src/shared/json/stringify.hako" as JSON
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
static box MirEmitterBox {
@ -149,7 +149,7 @@ static box MirEmitterBox {
q = q + 23 // length of the marker
// Use StringHelpers for digit reading and parsing
local digits = StringHelpers.read_digits(ast_json, q)
if digits.size() == 0 { return 0 }
if digits.length() == 0 { return 0 }
return StringHelpers.to_i64(digits)
}

View File

@ -30,7 +30,8 @@ static box Main {
_collect_flags(args) {
// Stage-A flags: emit/source/return only
local flags = { emit: 0, ret: null, source: null, stage_b: 0 }
// Stage-B flags: prefer_cfg/stage3/v1_compat
local flags = { emit: 0, ret: null, source: null, stage_b: 0, prefer_cfg: 1, stage3: 0, v1_compat: 0 }
if args == null { return flags }
local i = 0
@ -48,6 +49,14 @@ static box Main {
local parsed = me._parse_signed_int(args.get(i + 1))
if parsed != null { flags.ret = parsed }
i = i + 1
} else if token == "--prefer-cfg" && i + 1 < n {
local parsed = me._parse_signed_int(args.get(i + 1))
if parsed != null { flags.prefer_cfg = parsed }
i = i + 1
} else if token == "--stage3" {
flags.stage3 = 1
} else if token == "--v1-compat" {
flags.v1_compat = 1
}
i = i + 1
}
@ -469,7 +478,9 @@ static box Main {
main(args) {
local flags = me._collect_flags(args)
if flags.stage_b == 1 {
return StageBMain.main(args)
local json = StageBMain._do_compile_stage_b(flags.source, flags.prefer_cfg, flags.stage3, flags.v1_compat)
print(json)
return 0
}
if flags.emit == 1 {
local json = me._compile_source_to_json_v0(flags.source)
@ -489,4 +500,5 @@ static box Main {
me._emit_program_json(ret)
return 0
}
}

View File

@ -1,9 +1,13 @@
// Stage-B compiler entry — ParserBox → FlowEntry emit-only
using "lang/src/compiler/parser/parser_box.hako" as ParserBox
using "lang/src/compiler/pipeline_v2/flow_entry.hako" as FlowEntryBox
using lang.compiler.parser.box as ParserBox
using lang.compiler.pipeline_v2.flow_entry as FlowEntryBox
static box StageBMain {
_fallback_program() {
return "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}"
}
_parse_signed_int(raw) {
if raw == null { return null }
local text = "" + raw
@ -52,23 +56,20 @@ static box StageBMain {
return flags
}
main(args) {
local flags = me._collect_flags(args)
local src = flags.source
_do_compile_stage_b(src, prefer_cfg, stage3, v1_compat) {
if src == null || src == "" {
print("{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}")
return 0
return me._fallback_program()
}
local p = new ParserBox()
if flags.stage3 == 1 { p.stage3_enable(1) }
if stage3 == 1 { p.stage3_enable(1) }
p.extract_usings(src)
local usings_json = p.get_usings_json()
p.extract_externs(src)
local externs_json = p.get_externs_json()
local ast_json = p.parse_program2(src)
local prefer = flags.prefer_cfg
local prefer = prefer_cfg
local jv0 = null
if flags.v1_compat == 1 {
if v1_compat == 1 {
jv0 = FlowEntryBox.emit_v1_compat_from_ast_with_meta(ast_json, prefer, externs_json)
}
if jv0 == null || jv0 == "" {
@ -77,9 +78,21 @@ static box StageBMain {
if jv0 == null || jv0 == "" {
jv0 = FlowEntryBox.emit_v0_from_ast(ast_json, prefer)
}
// Attach usings metadata when availableStage-B pipeline consumes via resolver
if jv0 == null || jv0 == "" { jv0 = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}" }
print(jv0)
if jv0 == null || jv0 == "" {
jv0 = me._fallback_program()
}
return jv0
}
main(args) {
local flags = me._collect_flags(args)
local src = flags.source
local prefer = flags.prefer_cfg
local stage3 = flags.stage3
local v1_compat = flags.v1_compat
local json = me._do_compile_stage_b(src, prefer, stage3, v1_compat)
if json == null || json == "" { json = me._fallback_program() }
print(json)
return 0
}
}

View File

@ -13,13 +13,13 @@ static box ParserExprBox {
local at = pair.lastIndexOf("@")
local json = pair.substring(0, at)
local pos = i
if at >= 0 { pos = ctx.to_int(pair.substring(at+1, pair.size())) }
if at >= 0 { pos = ctx.to_int(pair.substring(at+1, pair.length())) }
ctx.gpos_set(pos)
return json
}
parse_string2(src, i, ctx) {
local n = src.size()
local n = src.length()
local j = i + 1
local out = ""
local guard = 0
@ -57,7 +57,7 @@ static box ParserExprBox {
parse_factor2(src, i, ctx) {
local j = ctx.skip_ws(src, i)
if j >= src.size() {
if j >= src.length() {
ctx.gpos_set(j)
return "{\"type\":\"Int\",\"value\":0}"
}
@ -116,13 +116,13 @@ static box ParserExprBox {
local idp = ctx.read_ident2(src, p)
local at = idp.lastIndexOf("@")
local cls = idp.substring(0, at)
local k = ctx.to_int(idp.substring(at+1, idp.size()))
local k = ctx.to_int(idp.substring(at+1, idp.length()))
k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == "(" { k = k + 1 }
local args_and_pos = me.parse_args2(src, k, ctx)
local at2 = args_and_pos.lastIndexOf("@")
local args_json = args_and_pos.substring(0, at2)
k = ctx.to_int(args_and_pos.substring(at2+1, args_and_pos.size()))
k = ctx.to_int(args_and_pos.substring(at2+1, args_and_pos.length()))
k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == ")" { k = k + 1 }
ctx.gpos_set(k)
@ -134,7 +134,7 @@ static box ParserExprBox {
local idp = ctx.read_ident2(src, j)
local at = idp.lastIndexOf("@")
local name = idp.substring(0, at)
local k = ctx.to_int(idp.substring(at+1, idp.size()))
local k = ctx.to_int(idp.substring(at+1, idp.length()))
local node = "{\"type\":\"Var\",\"name\":\"" + name + "\"}"
local cont2 = 1
@ -147,7 +147,7 @@ static box ParserExprBox {
local args_and_pos = me.parse_args2(src, k, ctx)
local at2 = args_and_pos.lastIndexOf("@")
local args_json = args_and_pos.substring(0, at2)
k = ctx.to_int(args_and_pos.substring(at2+1, args_and_pos.size()))
k = ctx.to_int(args_and_pos.substring(at2+1, args_and_pos.length()))
k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == ")" { k = k + 1 }
node = "{\"type\":\"Call\",\"name\":\"" + name + "\",\"args\":" + args_json + "}"
@ -158,13 +158,13 @@ static box ParserExprBox {
local midp = ctx.read_ident2(src, k)
local at3 = midp.lastIndexOf("@")
local mname = midp.substring(0, at3)
k = ctx.to_int(midp.substring(at3+1, midp.size()))
k = ctx.to_int(midp.substring(at3+1, midp.length()))
k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == "(" { k = k + 1 }
local args2 = me.parse_args2(src, k, ctx)
local at4 = args2.lastIndexOf("@")
local args_json2 = args2.substring(0, at4)
k = ctx.to_int(args2.substring(at4+1, args2.size()))
k = ctx.to_int(args2.substring(at4+1, args2.length()))
k = ctx.skip_ws(src, k)
if src.substring(k, k+1) == ")" { k = k + 1 }
node = "{\"type\":\"Method\",\"recv\":" + node + ",\"method\":\"" + mname + "\",\"args\":" + args_json2 + "}"
@ -201,7 +201,7 @@ static box ParserExprBox {
loop(cont == 1) {
j = ctx.skip_ws(src, j)
if j >= src.size() {
if j >= src.length() {
cont = 0
} else {
local op = src.substring(j, j+1)
@ -226,7 +226,7 @@ static box ParserExprBox {
loop(cont == 1) {
j = ctx.skip_ws(src, j)
if j >= src.size() {
if j >= src.length() {
cont = 0
} else {
local op = src.substring(j, j+1)
@ -301,7 +301,7 @@ static box ParserExprBox {
j = ctx.skip_ws(src, j)
local else_expr = me.parse_expr2(src, j, ctx)
j = ctx.gpos_get()
if else_expr.size() == 0 { else_expr = "{\"type\":\"Int\",\"value\":0}" }
if else_expr.length() == 0 { else_expr = "{\"type\":\"Int\",\"value\":0}" }
ctx.gpos_set(j)
return "{\"type\":\"Ternary\",\"cond\":" + lhs + ",\"then\":" + then_expr + ",\"else\":" + else_expr + "}"
}
@ -312,7 +312,7 @@ static box ParserExprBox {
parse_args2(src, i, ctx) {
local j = ctx.skip_ws(src, i)
local n = src.size()
local n = src.length()
local out = "["
j = ctx.skip_ws(src, j)

View File

@ -6,7 +6,7 @@
static box ParserLiteralBox {
// Map literal: {"k": v, ...} (string keys only) → Call{name:"map.of", args:[Str(k1), v1, Str(k2), v2, ...]}
parse_map(src, i, ctx) {
local n = src.size()
local n = src.length()
local j = i + 1 // skip opening '{'
local out = "["
local first = 1
@ -68,7 +68,7 @@ static box ParserLiteralBox {
// Array literal: [e1, e2, ...] → Call{name:"array.of", args:[...]}
parse_array(src, i, ctx) {
local n = src.size()
local n = src.length()
local j = i + 1 // skip opening '['
local out = "["
local first = 1

View File

@ -7,7 +7,7 @@ static box ParserPeekBox {
parse(src, i, ctx) {
// ctx is ParserBox for delegation
local j = i
local n = src.size()
local n = src.length()
// Parse scrutinee expression
local scr = ctx.parse_expr2(src, j)

View File

@ -44,7 +44,7 @@ box ParserBox {
esc_json(s) {
local out = ""
local i = 0
local n = s.size()
local n = s.length()
loop(i < n) {
local ch = s.substring(i, i+1)
if ch == "\\" { out = out + "\\\\" }
@ -84,7 +84,7 @@ box ParserBox {
local at = pair.lastIndexOf("@")
local content = pair.substring(0, at)
local pos = 0
if at >= 0 { pos = me.to_int(pair.substring(at+1, pair.size())) }
if at >= 0 { pos = me.to_int(pair.substring(at+1, pair.length())) }
else { pos = i }
me.gpos_set(pos)
return content
@ -93,7 +93,7 @@ box ParserBox {
// === using system ===
add_using(kind, target, alias) {
local cur = me.usings_json
if cur == null || cur.size() == 0 { cur = "[]" }
if cur == null || cur.length() == 0 { cur = "[]" }
local name = ""
local path = null
@ -106,17 +106,17 @@ box ParserBox {
local p = target
local idx = -1
local t = 0
loop(t < p.size()) {
loop(t < p.length()) {
if p.substring(t,t+1) == "/" { idx = t }
t = t + 1
}
if idx >= 0 { p = p.substring(idx+1, p.size()) }
if idx >= 0 { p = p.substring(idx+1, p.length()) }
if p.size() > 5 && me.starts_with(p, p.size()-5, ".hako") == 1 {
p = p.substring(0, p.size()-5)
if p.length() > 5 && me.starts_with(p, p.length()-5, ".hako") == 1 {
p = p.substring(0, p.length()-5)
} else {
if p.size() > 6 && me.starts_with(p, p.size()-6, ".nyash") == 1 {
p = p.substring(0, p.size()-6)
if p.length() > 6 && me.starts_with(p, p.length()-6, ".nyash") == 1 {
p = p.substring(0, p.length()-6)
}
}
name = p
@ -163,7 +163,7 @@ box ParserBox {
if func_name == null { func_name = "" }
local entry = "{\"symbol\":\"" + me.esc_json(sym) + "\",\"func\":\"" + me.esc_json(func_name) + "\"}"
local cur = me.externs_json
if cur == null || cur.size() == 0 { cur = "[]" }
if cur == null || cur.length() == 0 { cur = "[]" }
if cur == "[]" {
me.externs_json = "[" + entry + "]"
return 0
@ -188,20 +188,17 @@ box ParserBox {
// === Delegation to ParserExprBox ===
parse_expr2(src, i) {
local expr = new ParserExprBox()
return expr.parse_expr2(src, i, me)
return ParserExprBox.parse_expr2(src, i, me)
}
// === Delegation to ParserStmtBox ===
parse_stmt2(src, i) {
local stmt = new ParserStmtBox()
return stmt.parse(src, i, me)
return ParserStmtBox.parse(src, i, me)
}
// === Delegation to ParserControlBox ===
parse_block2(src, i) {
local ctrl = new ParserControlBox()
return ctrl.parse_block(src, i, me)
return ParserControlBox.parse_block(src, i, me)
}
// === Top-level program parser ===
@ -214,7 +211,7 @@ box ParserBox {
loop(cont_prog == 1) {
i = me.skip_ws(src, i)
if i >= src.size() {
if i >= src.length() {
cont_prog = 0
} else {
local start_i = i
@ -223,8 +220,8 @@ box ParserBox {
// Progress guard
if i <= start_i {
if i < src.size() { i = i + 1 }
else { i = src.size() }
if i < src.length() { i = i + 1 }
else { i = src.length() }
me.gpos_set(i)
}
@ -240,7 +237,7 @@ box ParserBox {
local before2 = i
i = me.skip_ws(src, i)
if i < src.size() && src.substring(i, i+1) == ";" {
if i < src.length() && src.substring(i, i+1) == ";" {
i = i + 1
} else {
done2 = 1
@ -249,7 +246,7 @@ box ParserBox {
if i == before2 { done2 = 1 }
}
if s.size() > 0 {
if s.length() > 0 {
if first == 1 {
body = body + s
first = 0

View File

@ -23,8 +23,8 @@ static box ParserCommonUtilsBox {
dq() { return "\"" }
starts_with(src, i, pat) {
local n = src.size()
local m = pat.size()
local n = src.length()
local m = pat.length()
if i + m > n { return 0 }
local k = 0
loop(k < m) {
@ -35,8 +35,8 @@ static box ParserCommonUtilsBox {
}
index_of(src, i, pat) {
local n = src.size()
local m = pat.size()
local n = src.length()
local m = pat.length()
if m == 0 { return i }
local j = i
loop(j + m <= n) {
@ -48,7 +48,7 @@ static box ParserCommonUtilsBox {
trim(s) {
local i = 0
local n = s.size()
local n = s.length()
loop(i < n && (s.substring(i,i+1) == " " || s.substring(i,i+1) == "\t")) { i = i + 1 }
local j = n
loop(j > i && (s.substring(j-1,j) == " " || s.substring(j-1,j) == "\t" || s.substring(j-1,j) == ";")) { j = j - 1 }
@ -58,7 +58,7 @@ static box ParserCommonUtilsBox {
esc_json(s) {
local out = ""
local i = 0
local n = s.size()
local n = s.length()
loop(i < n) {
local ch = s.substring(i, i+1)
if ch == "\\" { out = out + "\\\\" }

View File

@ -4,7 +4,7 @@ using lang.compiler.parser.scan.parser_string_utils_box as ParserStringUtilsBox
static box ParserIdentScanBox {
scan_ident(src, i) {
local j = i
local n = src.size()
local n = src.length()
if j >= n { return "@" + ParserStringUtilsBox.i2s(i) }
// first char: alpha or '_'
local ch = src.substring(j, j+1)

View File

@ -2,12 +2,12 @@
// ParserNumberScanBox — scan integer literal starting at index i
// Returns: "{\"type\":\"Int\",\"value\":<digits>}@<pos>"
using lang.compiler.parser.scan.parser_common_utils_box as Utils
using lang.compiler.parser.scan.parser_common_utils_box as ParserCommonUtilsBox
static box ParserNumberScanBox {
scan_int(src, i) {
if src == null { return "{\"type\":\"Int\",\"value\":0}@" + Utils.i2s(i) }
local n = src.size()
if src == null { return "{\"type\":\"Int\",\"value\":0}@" + ParserCommonUtilsBox.i2s(i) }
local n = src.length()
local j = i
local cont = 1
local guard = 0
@ -15,12 +15,12 @@ static box ParserNumberScanBox {
loop(cont == 1) {
if guard > max { cont = 0 } else { guard = guard + 1 }
if j < n {
if Utils.is_digit(src.substring(j, j+1)) { j = j + 1 } else { cont = 0 }
if ParserCommonUtilsBox.is_digit(src.substring(j, j+1)) { j = j + 1 } else { cont = 0 }
} else { cont = 0 }
}
local s = src.substring(i, j)
if s.size() == 0 { s = "0" }
return "{\"type\":\"Int\",\"value\":" + s + "}@" + Utils.i2s(j)
if s.length() == 0 { s = "0" }
return "{\"type\":\"Int\",\"value\":" + s + "}@" + ParserCommonUtilsBox.i2s(j)
}
}

View File

@ -4,14 +4,14 @@
// Returns: "<content>@<pos>" where <pos> is the index after the closing quote.
// Notes: pure string scanning; no external deps.
using lang.compiler.parser.scan.parser_common_utils_box as Utils
using lang.compiler.parser.scan.parser_common_utils_box as ParserCommonUtilsBox
static box ParserStringScanBox {
scan(src, i) {
if src == null { return "@" + Utils.i2s(i) }
local n = src.size()
if src == null { return "@" + ParserCommonUtilsBox.i2s(i) }
local n = src.length()
local j = i
if j >= n || src.substring(j, j+1) != "\"" { return "@" + Utils.i2s(i) }
if j >= n || src.substring(j, j+1) != "\"" { return "@" + ParserCommonUtilsBox.i2s(i) }
j = j + 1
local out = ""
local guard = 0
@ -21,7 +21,7 @@ static box ParserStringScanBox {
local ch = src.substring(j, j+1)
if ch == "\"" {
j = j + 1
return out + "@" + Utils.i2s(j)
return out + "@" + ParserCommonUtilsBox.i2s(j)
}
if ch == "\\" && j + 1 < n {
local nx = src.substring(j+1, j+2)
@ -44,7 +44,7 @@ static box ParserStringScanBox {
}
}
// if unterminated, return what we have and the last pos to avoid infinite loops
return out + "@" + Utils.i2s(j)
return out + "@" + ParserCommonUtilsBox.i2s(j)
}
}

View File

@ -3,7 +3,7 @@
// Responsibility: Backward compatibility wrapper for parser code
// Notes: All functionality now provided by apps/selfhost/common/string_helpers.hako
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
using sh_core as StringHelpers
static box ParserStringUtilsBox {
// Delegate all methods to StringHelpers (centralized implementation)

View File

@ -23,7 +23,7 @@ static box ParserControlBox {
local then_res = me.parse_block(src, j, ctx)
local at1 = then_res.lastIndexOf("@")
local then_json = then_res.substring(0, at1)
j = ctx.to_int(then_res.substring(at1+1, then_res.size()))
j = ctx.to_int(then_res.substring(at1+1, then_res.length()))
j = ctx.skip_ws(src, j)
local else_json = null
@ -33,11 +33,11 @@ static box ParserControlBox {
local else_res = me.parse_block(src, j, ctx)
local at2 = else_res.lastIndexOf("@")
else_json = else_res.substring(0, at2)
j = ctx.to_int(else_res.substring(at2+1, else_res.size()))
j = ctx.to_int(else_res.substring(at2+1, else_res.length()))
}
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
@ -63,10 +63,10 @@ static box ParserControlBox {
local body_res = me.parse_block(src, j, ctx)
local at3 = body_res.lastIndexOf("@")
local body_json = body_res.substring(0, at3)
j = ctx.to_int(body_res.substring(at3+1, body_res.size()))
j = ctx.to_int(body_res.substring(at3+1, body_res.length()))
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Loop\",\"cond\":" + cond + ",\"body\":" + body_json + "}"
@ -79,14 +79,14 @@ static box ParserControlBox {
if ctx.stage3_enabled() == 1 {
j = ctx.skip_ws(src, j)
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Break\"}"
}
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
@ -99,14 +99,14 @@ static box ParserControlBox {
if ctx.stage3_enabled() == 1 {
j = ctx.skip_ws(src, j)
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Continue\"}"
}
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"
@ -125,7 +125,7 @@ static box ParserControlBox {
loop(cont_block == 1) {
j = ctx.skip_ws(src, j)
if j >= src.size() {
if j >= src.length() {
cont_block = 0
} else {
if src.substring(j, j+1) == "}" {
@ -138,7 +138,7 @@ static box ParserControlBox {
// Progress guard: ensure forward movement to avoid infinite loop on malformed input
if j <= start_j {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
ctx.gpos_set(j)
}
@ -150,11 +150,11 @@ static box ParserControlBox {
if guard > max { done = 1 } else { guard = guard + 1 }
local before = j
j = ctx.skip_ws(src, j)
if j < src.size() && src.substring(j, j+1) == ";" { j = j + 1 } else { done = 1 }
if j < src.length() && src.substring(j, j+1) == ";" { j = j + 1 } else { done = 1 }
if j == before { done = 1 }
}
if s.size() > 0 {
if s.length() > 0 {
if first == 1 {
body = body + s
first = 0

View File

@ -13,14 +13,14 @@ static box ParserExceptionBox {
if ctx.stage3_enabled() == 1 {
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Throw\",\"expr\":" + e_throw + "}"
}
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":" + e_throw + "}"
@ -35,7 +35,7 @@ static box ParserExceptionBox {
local try_res = ctx.parse_block2(src, j)
local at_t = try_res.lastIndexOf("@")
local try_json = try_res.substring(0, at_t)
j = ctx.to_int(try_res.substring(at_t+1, try_res.size()))
j = ctx.to_int(try_res.substring(at_t+1, try_res.length()))
local catches_json = "["
local catch_first = 1
@ -63,7 +63,7 @@ static box ParserExceptionBox {
local id1 = ctx.read_ident2(src, j)
local at1 = id1.lastIndexOf("@")
catch_type = id1.substring(0, at1)
j = ctx.to_int(id1.substring(at1+1, id1.size()))
j = ctx.to_int(id1.substring(at1+1, id1.length()))
j = ctx.skip_ws(src, j)
}
@ -71,7 +71,7 @@ static box ParserExceptionBox {
local id2 = ctx.read_ident2(src, j)
local at2 = id2.lastIndexOf("@")
catch_param = id2.substring(0, at2)
j = ctx.to_int(id2.substring(at2+1, id2.size()))
j = ctx.to_int(id2.substring(at2+1, id2.length()))
j = ctx.skip_ws(src, j)
}
@ -83,18 +83,18 @@ static box ParserExceptionBox {
// catch body
local c_res = ctx.parse_block2(src, j)
local atc = c_res.lastIndexOf("@")
j = ctx.to_int(c_res.substring(atc+1, c_res.size()))
j = ctx.to_int(c_res.substring(atc+1, c_res.length()))
if ctx.stage3_enabled() == 1 {
local entry = "{"
local wrote = 0
if catch_param != null && catch_param.size() > 0 {
if catch_param != null && catch_param.length() > 0 {
entry = entry + "\"param\":\"" + ctx.esc_json(catch_param) + "\""
wrote = 1
}
if catch_type != null && catch_type.size() > 0 {
if catch_type != null && catch_type.length() > 0 {
if wrote == 1 { entry = entry + "," }
entry = entry + "\"typeHint\":\"" + ctx.esc_json(catch_type) + "\""
wrote = 1
@ -127,13 +127,13 @@ static box ParserExceptionBox {
j = ctx.skip_ws(src, j)
local f_res = ctx.parse_block2(src, j)
local atf = f_res.lastIndexOf("@")
j = ctx.to_int(f_res.substring(atf+1, f_res.size()))
j = ctx.to_int(f_res.substring(atf+1, f_res.length()))
finally_json = f_res.substring(0, atf)
}
if ctx.stage3_enabled() == 1 {
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
local node = "{\"type\":\"Try\",\"try\":" + try_json + ",\"catches\":" + catches_json
@ -143,7 +143,7 @@ static box ParserExceptionBox {
}
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":{\"type\":\"Int\",\"value\":0}}"

View File

@ -15,32 +15,32 @@ static box ParserStmtBox {
if ctx.starts_with(src, j, "@extern_c") == 1 {
j = j + 9 // len("@extern_c")
j = ctx.skip_ws(src, j)
if j < src.size() && src.substring(j, j+1) == "(" { j = j + 1 }
if j < src.length() && src.substring(j, j+1) == "(" { j = j + 1 }
j = ctx.skip_ws(src, j)
// First string literal: symbol
local sym = ""
if j < src.size() && src.substring(j, j+1) == "\"" {
if j < src.length() && src.substring(j, j+1) == "\"" {
sym = ctx.read_string_lit(src, j)
j = ctx.gpos_get()
}
j = ctx.skip_ws(src, j)
if j < src.size() && src.substring(j, j+1) == "," { j = j + 1 }
if j < src.length() && src.substring(j, j+1) == "," { j = j + 1 }
j = ctx.skip_ws(src, j)
// Second string literal: func
local fn = ""
if j < src.size() && src.substring(j, j+1) == "\"" {
fn = ctx.read_string_lit(src, j)
local func_name = ""
if j < src.length() && src.substring(j, j+1) == "\"" {
func_name = ctx.read_string_lit(src, j)
j = ctx.gpos_get()
}
// Skip to ')' if present
j = ctx.skip_ws(src, j)
if j < src.size() && src.substring(j, j+1) == ")" { j = j + 1 }
if j < src.length() && src.substring(j, j+1) == ")" { j = j + 1 }
// Optional semicolon is consumed by caller; still advance if present
j = ctx.skip_ws(src, j)
if j < src.size() && src.substring(j, j+1) == ";" { j = j + 1 }
if j < src.length() && src.substring(j, j+1) == ";" { j = j + 1 }
ctx.gpos_set(j)
// Record annotation in parser context and emit no statement
ctx.add_extern_c(sym, fn)
ctx.add_extern_c(sym, func_name)
return ""
}
@ -50,23 +50,23 @@ static box ParserStmtBox {
}
// assignment: IDENT '=' expr
if j < src.size() && ctx.is_alpha(src.substring(j, j+1)) {
if j < src.length() && ctx.is_alpha(src.substring(j, j+1)) {
local idp0 = ctx.read_ident2(src, j)
local at0 = idp0.lastIndexOf("@")
if at0 > 0 {
local name0 = idp0.substring(0, at0)
local k0 = ctx.to_int(idp0.substring(at0+1, idp0.size()))
local k0 = ctx.to_int(idp0.substring(at0+1, idp0.length()))
k0 = ctx.skip_ws(src, k0)
if k0 < src.size() && src.substring(k0, k0+1) == "=" {
if k0 < src.length() && src.substring(k0, k0+1) == "=" {
local eq_two = "="
if k0 + 1 < src.size() { eq_two = src.substring(k0, k0+2) }
if k0 + 1 < src.length() { eq_two = src.substring(k0, k0+2) }
if eq_two != "==" {
k0 = k0 + 1
k0 = ctx.skip_ws(src, k0)
local default_local = "{\"type\":\"Int\",\"value\":0}"
local expr_json0 = default_local
local end_pos0 = k0
if k0 < src.size() {
if k0 < src.length() {
local ahead = src.substring(k0, k0+1)
if ahead != "}" && ahead != ";" {
expr_json0 = ctx.parse_expr2(src, k0)
@ -75,7 +75,7 @@ static box ParserStmtBox {
}
k0 = end_pos0
if k0 <= stmt_start {
if k0 < src.size() { k0 = k0 + 1 } else { k0 = src.size() }
if k0 < src.length() { k0 = k0 + 1 } else { k0 = src.length() }
}
ctx.gpos_set(k0)
return "{\"type\":\"Local\",\"name\":\"" + name0 + "\",\"expr\":" + expr_json0 + "}"
@ -91,7 +91,7 @@ static box ParserStmtBox {
local default_ret = "{\"type\":\"Int\",\"value\":0}"
local expr_json_ret = default_ret
local end_pos_ret = j
if j < src.size() {
if j < src.length() {
local ahead_ret = src.substring(j, j+1)
if ahead_ret != "}" && ahead_ret != ";" {
expr_json_ret = ctx.parse_expr2(src, j)
@ -100,7 +100,7 @@ static box ParserStmtBox {
}
j = end_pos_ret
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Return\",\"expr\":" + expr_json_ret + "}"
@ -113,14 +113,14 @@ static box ParserStmtBox {
local idp = ctx.read_ident2(src, j)
local at = idp.lastIndexOf("@")
local name = idp.substring(0, at)
j = ctx.to_int(idp.substring(at+1, idp.size()))
j = ctx.to_int(idp.substring(at+1, idp.length()))
j = ctx.skip_ws(src, j)
if j < src.size() && src.substring(j, j+1) == "=" { j = j + 1 }
if j < src.length() && src.substring(j, j+1) == "=" { j = j + 1 }
j = ctx.skip_ws(src, j)
local default_local = "{\"type\":\"Int\",\"value\":0}"
local expr_json_local = default_local
local end_pos_local = j
if j < src.size() {
if j < src.length() {
local ahead_local = src.substring(j, j+1)
if ahead_local != "}" && ahead_local != ";" {
expr_json_local = ctx.parse_expr2(src, j)
@ -129,7 +129,7 @@ static box ParserStmtBox {
}
j = end_pos_local
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Local\",\"name\":\"" + name + "\",\"expr\":" + expr_json_local + "}"
@ -165,7 +165,7 @@ static box ParserStmtBox {
local e = ctx.parse_expr2(src, j)
j = ctx.gpos_get()
if j <= expr_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return "{\"type\":\"Expr\",\"expr\":" + e + "}"
@ -187,7 +187,7 @@ static box ParserStmtBox {
local idp = ctx.read_ident2(src, j)
local at = idp.lastIndexOf("@")
alias = idp.substring(0, at)
j = ctx.to_int(idp.substring(at+1, idp.size()))
j = ctx.to_int(idp.substring(at+1, idp.length()))
}
ctx.add_using("path", p, alias)
} else {
@ -195,7 +195,7 @@ static box ParserStmtBox {
local idp = ctx.read_ident2(src, j)
local at = idp.lastIndexOf("@")
local name = idp.substring(0, at)
j = ctx.to_int(idp.substring(at+1, idp.size()))
j = ctx.to_int(idp.substring(at+1, idp.length()))
local cont = 1
loop(cont == 1) {
j = ctx.skip_ws(src, j)
@ -205,7 +205,7 @@ static box ParserStmtBox {
idp = ctx.read_ident2(src, j)
at = idp.lastIndexOf("@")
name = name + "." + idp.substring(0, at)
j = ctx.to_int(idp.substring(at+1, idp.size()))
j = ctx.to_int(idp.substring(at+1, idp.length()))
} else {
cont = 0
}
@ -218,7 +218,7 @@ static box ParserStmtBox {
idp = ctx.read_ident2(src, j)
at = idp.lastIndexOf("@")
alias2 = idp.substring(0, at)
j = ctx.to_int(idp.substring(at+1, idp.size()))
j = ctx.to_int(idp.substring(at+1, idp.length()))
}
ctx.add_using("ns", name, alias2)
}
@ -226,7 +226,7 @@ static box ParserStmtBox {
// ensure progress
if j <= stmt_start {
if j < src.size() { j = j + 1 } else { j = src.size() }
if j < src.length() { j = j + 1 } else { j = src.length() }
}
ctx.gpos_set(j)
return ""

View File

@ -6,14 +6,14 @@
// - ParserBox.extract_usings delegates to this box (Phase 2 split)
// - Pure string scan依存ゼロ。FailFastはせず、安全にスキップでループを進める
using lang.compiler.parser.scan.parser_common_utils_box as Utils
using lang.compiler.parser.scan.parser_common_utils_box as ParserCommonUtilsBox
static box UsingCollectorBox {
// Public API: collect line-based using declarations to JSON array string
collect(src) {
if src == null { return "[]" }
local n = src.size()
local n = src.length()
local i = 0
local first = 1
local out = "["
@ -24,30 +24,30 @@ static box UsingCollectorBox {
local line = src.substring(i, j)
// trim left spaces/tabs
local k = 0
loop(k < line.size() && (line.substring(k,k+1) == " " || line.substring(k,k+1) == "\t")) { k = k + 1 }
if Utils.starts_with(line, k, "using ") == 1 {
local rest = Utils.trim(line.substring(k + 6, line.size()))
loop(k < line.length() && (line.substring(k,k+1) == " " || line.substring(k,k+1) == "\t")) { k = k + 1 }
if ParserCommonUtilsBox.starts_with(line, k, "using ") == 1 {
local rest = ParserCommonUtilsBox.trim(line.substring(k + 6, line.length()))
// split on ' as '
local as_pos = Utils.index_of(rest, 0, " as ")
local as_pos = ParserCommonUtilsBox.index_of(rest, 0, " as ")
local target = rest
local alias = null
if as_pos >= 0 { target = Utils.trim(rest.substring(0, as_pos)) alias = Utils.trim(rest.substring(as_pos + 4, rest.size())) }
if as_pos >= 0 { target = ParserCommonUtilsBox.trim(rest.substring(0, as_pos)) alias = ParserCommonUtilsBox.trim(rest.substring(as_pos + 4, rest.length())) }
// path or namespace
local is_path = 0
if target.size() > 0 {
if Utils.starts_with(target, 0, Utils.dq()) == 1 { is_path = 1 }
if Utils.starts_with(target, 0, "./") == 1 { is_path = 1 }
if Utils.starts_with(target, 0, "/") == 1 { is_path = 1 }
if target.size() >= 5 && Utils.starts_with(target, target.size()-5, ".hako") == 1 { is_path = 1 }
if target.size() >= 6 && Utils.starts_with(target, target.size()-6, ".nyash") == 1 { is_path = 1 }
if target.length() > 0 {
if ParserCommonUtilsBox.starts_with(target, 0, ParserCommonUtilsBox.dq()) == 1 { is_path = 1 }
if ParserCommonUtilsBox.starts_with(target, 0, "./") == 1 { is_path = 1 }
if ParserCommonUtilsBox.starts_with(target, 0, "/") == 1 { is_path = 1 }
if target.length() >= 5 && ParserCommonUtilsBox.starts_with(target, target.length()-5, ".hako") == 1 { is_path = 1 }
if target.length() >= 6 && ParserCommonUtilsBox.starts_with(target, target.length()-6, ".nyash") == 1 { is_path = 1 }
}
local name = ""
local path = null
if is_path == 1 {
// strip quotes
if Utils.starts_with(target, 0, Utils.dq()) == 1 {
target = target.substring(1, target.size())
if target.size() > 0 && target.substring(target.size()-1, target.size()) == Utils.dq() { target = target.substring(0, target.size()-1) }
if ParserCommonUtilsBox.starts_with(target, 0, ParserCommonUtilsBox.dq()) == 1 {
target = target.substring(1, target.length())
if target.length() > 0 && target.substring(target.length()-1, target.length()) == ParserCommonUtilsBox.dq() { target = target.substring(0, target.length()-1) }
}
path = target
if alias != null { name = alias } else {
@ -55,11 +55,11 @@ static box UsingCollectorBox {
local p = target
local idx = -1
local t = 0
loop(t < p.size()) { if p.substring(t,t+1) == "/" { idx = t } t = t + 1 }
if idx >= 0 { p = p.substring(idx+1, p.size()) }
loop(t < p.length()) { if p.substring(t,t+1) == "/" { idx = t } t = t + 1 }
if idx >= 0 { p = p.substring(idx+1, p.length()) }
// strip extension
if p.size() > 5 && Utils.starts_with(p, p.size()-5, ".hako") == 1 { p = p.substring(0, p.size()-5) }
else { if p.size() > 6 && Utils.starts_with(p, p.size()-6, ".nyash") == 1 { p = p.substring(0, p.size()-6) } }
if p.length() > 5 && ParserCommonUtilsBox.starts_with(p, p.length()-5, ".hako") == 1 { p = p.substring(0, p.length()-5) }
else { if p.length() > 6 && ParserCommonUtilsBox.starts_with(p, p.length()-6, ".nyash") == 1 { p = p.substring(0, p.length()-6) } }
name = p
}
} else {
@ -67,8 +67,8 @@ static box UsingCollectorBox {
}
// append entry
if first == 0 { out = out + "," } else { first = 0 }
out = out + "{" + Utils.dq() + "name" + Utils.dq() + ":" + Utils.dq() + Utils.esc_json(name) + Utils.dq()
if path != null { out = out + "," + Utils.dq() + "path" + Utils.dq() + ":" + Utils.dq() + Utils.esc_json(path) + Utils.dq() }
out = out + "{" + ParserCommonUtilsBox.dq() + "name" + ParserCommonUtilsBox.dq() + ":" + ParserCommonUtilsBox.dq() + ParserCommonUtilsBox.esc_json(name) + ParserCommonUtilsBox.dq()
if path != null { out = out + "," + ParserCommonUtilsBox.dq() + "path" + ParserCommonUtilsBox.dq() + ":" + ParserCommonUtilsBox.dq() + ParserCommonUtilsBox.esc_json(path) + ParserCommonUtilsBox.dq() }
out = out + "}"
}
i = j + 1
@ -77,4 +77,3 @@ static box UsingCollectorBox {
return out
}
}

View File

@ -21,9 +21,9 @@ static box EmitCallBox {
}
_quote(s) {
if s == null { return "\"\"" }
local out = ""; local i = 0; local n = s.size()
local out = ""; local i = 0; local n = s.length()
loop (i < n) {
local ch = call("String.substring/2", s, i, i+1)
local ch = s.substring(i, i+1)
if ch == "\\" { out = out + "\\\\" }
else { if ch == "\"" { out = out + "\\\"" } else {
if ch == "\n" { out = out + "\\n" } else {
@ -56,9 +56,9 @@ static box EmitCallBox {
if first == 1 { first = 0 } else { body = body + "," }
body = body + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + EmitCallBox._to_str(vid) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + EmitCallBox._to_str(vv) + "}}"
n = n + 1
pos = pos + ds.size()
pos = pos + ds.length()
}
if pos >= s.size() { break }
if pos >= s.length() { break }
}
local dst = n + 1
// mir_call (Extern)

View File

@ -14,9 +14,9 @@ static box EmitCompareBox {
}
_quote(s) {
if s == null { return "\"\"" }
local out = ""; local i = 0; local n = s.size()
local out = ""; local i = 0; local n = s.length()
loop (i < n) {
local ch = call("String.substring/2", s, i, i+1)
local ch = s.substring(i, i+1)
if ch == "\\" { out = out + "\\\\" }
else { if ch == "\"" { out = out + "\\\"" } else {
if ch == "\n" { out = out + "\\n" } else {

View File

@ -16,9 +16,9 @@ static box EmitMethodBox {
}
_quote(s) {
if s == null { return "\"\"" }
local out = ""; local i = 0; local n = s.size()
local out = ""; local i = 0; local n = s.length()
loop (i < n) {
local ch = call("String.substring/2", s, i, i+1)
local ch = s.substring(i, i+1)
if ch == "\\" { out = out + "\\\\" }
else { if ch == "\"" { out = out + "\\\"" } else {
if ch == "\n" { out = out + "\\n" } else {
@ -52,9 +52,9 @@ static box EmitMethodBox {
local vv = RegexFlow.to_int(ds)
body = body + "," + "{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + EmitMethodBox._to_str(vid) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + EmitMethodBox._to_str(vv) + "}}"
n = n + 1
pos = pos + ds.size()
pos = pos + ds.length()
}
if pos >= s.size() { break }
if pos >= s.length() { break }
}
local dst = n + 2
// mir_call (Method)

View File

@ -43,7 +43,7 @@ box ExecutionPipelineBox {
local ast = p.parse_program2(src)
// Emit Stage1 JSON with meta.usings
local json = EmitterBox.emit_program(ast, usings, externs)
if json == null || json.size() == 0 { return 1 }
if json == null || json.length() == 0 { return 1 }
print(json)
return 0
}

View File

@ -7,7 +7,7 @@ static box JsonMinifyBox {
local s = "" + text
local out = ""
local i = 0
local n = s.size()
local n = s.length()
local in_str = 0
loop(i < n) {
local ch = s.substring(i, i+1)

View File

@ -19,7 +19,7 @@ static box LocalSSABox {
if insts == null { return 1 }
insts = me._maybe_unwrap_instructions(insts)
if insts == null { return 1 }
call("ArrayBox.push/2", insts, { op:"copy", dst: dst, src: src })
insts.push({ op:"copy", dst: dst, src: src })
return 0
}
@ -43,20 +43,20 @@ static box LocalSSABox {
local insert_at = i // phi 直後
local node = { op:"copy", dst: dst, src: src }
if insert_at >= n {
call("ArrayBox.push/2", insts, node)
insts.push(node)
return 0
}
if n > 0 {
call("ArrayBox.push/2", insts, BoxHelpers.array_get(insts, n - 1))
insts.push(BoxHelpers.array_get(insts, n - 1))
local j = n - 1
loop (j >= insert_at) {
call("ArrayBox.set/3", insts, j + 1, BoxHelpers.array_get(insts, j))
insts.set(j + 1, BoxHelpers.array_get(insts, j))
j = j - 1
}
call("ArrayBox.set/3", insts, insert_at, node)
insts.set(insert_at, node)
return 0
}
call("ArrayBox.push/2", insts, node)
insts.push(node)
return 0
}
@ -97,19 +97,19 @@ static box LocalSSABox {
// Do not cross terminator: insert before the first terminator if present
if insert_at > term_at { insert_at = term_at }
local node = { op:"copy", dst: dst, src: src }
if insert_at >= n { call("ArrayBox.push/2", insts, node) return 0 }
if insert_at >= n { insts.push(node) return 0 }
// 1つ末尾に空きを作る末尾要素を複製して押し出す
if n > 0 {
call("ArrayBox.push/2", insts, BoxHelpers.array_get(insts, n - 1))
insts.push(BoxHelpers.array_get(insts, n - 1))
local j = n - 1
loop (j >= insert_at) {
call("ArrayBox.set/3", insts, j + 1, BoxHelpers.array_get(insts, j))
insts.set(j + 1, BoxHelpers.array_get(insts, j))
j = j - 1
}
call("ArrayBox.set/3", insts, insert_at, node)
insts.set(insert_at, node)
return 0
}
call("ArrayBox.push/2", insts, node)
insts.push(node)
return 0
}

View File

@ -16,7 +16,7 @@ box MirBuilderBox {
build(ast_json) {
if ast_json == null { return EmitReturnBox.emit_return_int2(0, 0) }
// If(cond=Compare) → CFG (branch/jump/ret)
if call("String.indexOf/2", ast_json, "\"type\":\"If\"") >= 0 {
if ast_json.indexOf("\"type\":\"If\"") >= 0 {
local ic = Stage1ExtractFlow.extract_if_compare(ast_json)
if ic != null { return EmitCompareBox.emit_compare_cfg3(BoxHelpers.map_get(ic, "lhs"), BoxHelpers.map_get(ic, "rhs"), BoxHelpers.map_get(ic, "cmp"), 0, 0) }
}

View File

@ -20,8 +20,8 @@ static box MirCallBox {
// materialize const args r1..rN
loop (true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(1 + n, RegexFlow.to_int(ds))) n = n + 1 pos = pos + ds.size() }
if pos >= s.size() { break }
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(1 + n, RegexFlow.to_int(ds))) n = n + 1 pos = pos + ds.length() }
if pos >= s.length() { break }
}
local dst = n + 1
// args 1..n
@ -46,8 +46,8 @@ static box MirCallBox {
// materialize args r2..r(n+1)
{ local i = 0 loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(2 + i, RegexFlow.to_int(ds))) i = i + 1 n = i pos = pos + ds.size() }
if pos >= s.size() { break }
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(2 + i, RegexFlow.to_int(ds))) i = i + 1 n = i pos = pos + ds.length() }
if pos >= s.length() { break }
}
}
local dst = n + 2
@ -72,8 +72,8 @@ static box MirCallBox {
// materialize args r1..rN
{ local i = 0 loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(1 + i, RegexFlow.to_int(ds))) i = i + 1 n = i pos = pos + ds.size() }
if pos >= s.size() { break }
if ds == "" { pos = pos + 1 } else { insts.push(MirEmitBox.make_const(1 + i, RegexFlow.to_int(ds))) i = i + 1 n = i pos = pos + ds.length() }
if pos >= s.length() { break }
}
}
local dst = n + 1

View File

@ -18,7 +18,7 @@ static box PipelineNameResolveBox {
local dot2 = RegexFlow.find_from(raw_name, ".", 0)
if dot2 < 0 { return null }
local head2 = raw_name.substring(0, dot2)
local tail2 = raw_name.substring(dot2 + 1, raw_name.size())
local tail2 = raw_name.substring(dot2 + 1, raw_name.length())
local ns2 = UsingResolverBox.resolve_namespace_alias(r_state, head2)
if ns2 == null { ns2 = UsingResolverBox.guess_namespace_from_tail(r_state, head2) }
if ns2 == null { return null }

View File

@ -20,7 +20,7 @@ static box NamespaceBox {
local pos = RegexFlow.find_from(s, ".", 0)
if pos < 0 { return s }
local head = s.substring(0, pos)
local tail = s.substring(pos + 1, s.size())
local tail = s.substring(pos + 1, s.length())
if resolver_state == null { return s }
local ns = UsingResolver.resolve_namespace_alias(resolver_state, head)
if ns == null {
@ -47,7 +47,7 @@ static box NamespaceBox {
return null
}
local head = s.substring(0, pos)
local tail = s.substring(pos + 1, s.size())
local tail = s.substring(pos + 1, s.length())
if resolver_state == null { return s }
local ns2 = UsingResolver.resolve_namespace_alias(resolver_state, head)
if ns2 == null {

View File

@ -256,22 +256,31 @@ flow PipelineV2 {
{
local kq = RegexFlow.find_from(ast_json, "\"type\":\"Call\"", 0)
if kq >= 0 {
print("[flow] Call pattern: kq=" + kq)
// Strict preflight via tolerant scanner: read raw name and enforce using alias resolution
{
local scan0 = Stage1JsonScannerBox.extract_name_args(ast_json, kq)
if scan0 != null {
if AliasPreflightBox.check_head(scan0.get("name"), r) != 1 { return null }
print("[flow] Call scan0 name=" + scan0.get("name"))
if AliasPreflightBox.check_head(scan0.get("name"), r) != 1 {
print("[flow] Call AliasPreflightBox failed, returning null")
return null
}
}
}
local kc = CallExtractBox.extract_return_call_ints(ast_json)
if kc != null {
print("[flow] Call extract_return_call_ints succeeded")
local kn = NormalizerBox.normalize_call_ints(kc)
if kn == null { return null }
if SignatureVerifierBox.verify_call_name_arity(kn.get("name"), kn.get("args")) != 1 { return null }
if kn == null { print("[flow] Call normalize_call_ints returned null") return null }
if SignatureVerifierBox.verify_call_name_arity(kn.get("name"), kn.get("args")) != 1 { print("[flow] Call verify_call_name_arity failed") return null }
local j4 = EmitCallBox.emit_call_int_args(kn.get("name"), kn.get("args"))
if j4 == null { return null }
if j4 == null { print("[flow] Call emit_call_int_args returned null") return null }
print("[flow] Call path 1 succeeded, returning JSON")
return LocalSSA.ensure_calls(LocalSSA.ensure_cond(j4))
} else {
print("[flow] Call extract_return_call_ints returned null, trying scanner fallback")
}
// Fallback: scanner → normalizer → emit
{

View File

@ -40,7 +40,7 @@ static box PipelineHelpersBox {
parse_int_after_prefix(s, prefix, search_pos) {
local p = RegexFlow.find_from(s, prefix, search_pos)
if p < 0 { return null }
local res = PipelineHelpersBox.parse_int_at(s, p + prefix.size())
local res = PipelineHelpersBox.parse_int_at(s, p + prefix.length())
return res
}

View File

@ -11,7 +11,7 @@ static box ReadOnlyMapView {
return v
}
has(key) { return call("MapBox.has/2", me._m, key) }
has(key) { return me._m.has(key) }
get(key) { return BoxHelpers.map_get(me._m, key) }
set(key, val) {

View File

@ -55,8 +55,8 @@ flow RegexFlow {
if s == null { return -1 }
if needle == null { return -1 }
if pos < 0 { pos = 0 }
local n = s.size()
local m = needle.size()
local n = s.length()
local m = needle.length()
if m == 0 { return pos }
local i = pos
local limit = n - m
@ -75,7 +75,7 @@ flow RegexFlow {
to_int(digits) {
if digits == null { return 0 }
local n = digits.size()
local n = digits.length()
if n == 0 { return 0 }
local i = 0
local neg = 0

View File

@ -62,9 +62,9 @@ static box SignatureVerifierBox {
n = n + 1
// advance to the end of this digit run
local p2 = RegexFlow.find_from(s, ds, pos)
if p2 < 0 { pos = pos + ds.size() } else { pos = p2 + ds.size() }
if p2 < 0 { pos = pos + ds.length() } else { pos = p2 + ds.length() }
}
if pos >= s.size() { break }
if pos >= s.length() { break }
}
return n
}
@ -89,13 +89,13 @@ static box SignatureVerifierBox {
// Split at last '.' for method name
local last = RegexFlow.last_index_of(s, ".")
if last < 0 { return 1 }
local method = s.substring(last + 1, s.size())
local method = s.substring(last + 1, s.length())
// Determine class token just before method (penultimate segment)
local head_all = s.substring(0, last)
local prev = RegexFlow.last_index_of(head_all, ".")
local head = head_all
if prev >= 0 {
head = head_all.substring(prev + 1, head_all.size())
head = head_all.substring(prev + 1, head_all.length())
}
// Normalize head to Box name
local bxname = head

View File

@ -14,8 +14,8 @@ static box Stage1ArgsParserBox {
local n = 0
loop(true) {
local ds = RegexFlow.digits_from(s, pos)
if ds == "" { pos = pos + 1 } else { n = n + 1 pos = pos + ds.size() }
if pos >= s.size() { break }
if ds == "" { pos = pos + 1 } else { n = n + 1 pos = pos + ds.length() }
if pos >= s.length() { break }
}
return n
}
@ -34,9 +34,9 @@ static box Stage1ArgsParserBox {
out.push(RegexFlow.to_int(ds))
// advance to end of this token to avoid re-matching
local p2 = RegexFlow.find_from(s, ds, pos)
if p2 < 0 { pos = pos + ds.size() } else { pos = p2 + ds.size() }
if p2 < 0 { pos = pos + ds.length() } else { pos = p2 + ds.length() }
}
if pos >= s.size() { break }
if pos >= s.length() { break }
}
return out
}
@ -46,7 +46,7 @@ static box Stage1ArgsParserBox {
local lb = JsonCursorBox.find_from(s, "[", 0)
if lb < 0 { return 1 }
local rb = JsonCursorBox.seek_array_end(s, lb)
if rb < 0 { rb = s.size() }
if rb < 0 { rb = s.length() }
local chk = RegexFlow.find_from(s, "\"type\":\"", lb)
if chk < 0 || chk >= rb { return 1 }
local pos = chk

View File

@ -125,7 +125,7 @@ flow Stage1ExtractFlow {
local args = []
if ak >= 0 {
local rb = Stage1ExtractFlow._idx_from(ast_json, "]", ak)
if rb < 0 { rb = ast_json.size() }
if rb < 0 { rb = ast_json.length() }
local i = ak
loop(true) {
local tpos = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Int\"", i)
@ -134,7 +134,7 @@ flow Stage1ExtractFlow {
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { args.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
i = vpos + 8 + ds.length()
}
}
return { method: mname, args: args }
@ -158,7 +158,7 @@ flow Stage1ExtractFlow {
local args = []
if ak >= 0 {
local rb = Stage1ExtractFlow._idx_from(ast_json, "]", ak)
if rb < 0 { rb = ast_json.size() }
if rb < 0 { rb = ast_json.length() }
local i = ak
loop(true) {
local tpos = Stage1ExtractFlow._idx_from(ast_json, "\"type\":\"Int\"", i)
@ -167,7 +167,7 @@ flow Stage1ExtractFlow {
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { args.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
i = vpos + 8 + ds.length()
}
}
return { class: cname, args: args }
@ -192,7 +192,7 @@ flow Stage1ExtractFlow {
local ak = Stage1ExtractFlow._idx_from(ast_json, "\"args\":[", q)
if ak < 0 { return { name: name, args: [] } }
local rb = Stage1ExtractFlow._idx_from(ast_json, "]", ak)
if rb < 0 { rb = ast_json.size() }
if rb < 0 { rb = ast_json.length() }
local args = []
local i = ak
loop(true) {
@ -202,7 +202,7 @@ flow Stage1ExtractFlow {
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { args.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
i = vpos + 8 + ds.length()
}
return { name: name, args: args }
}

View File

@ -40,7 +40,7 @@ static box Stage1IntArgsExtractBox {
// bracket-aware end detection
local lb = RegexFlow.find_from(ast_json, "[", ak)
local rb = ast_json.size()
local rb = ast_json.length()
if lb >= 0 {
local i2 = lb + 1
local depth = 1
@ -63,7 +63,7 @@ static box Stage1IntArgsExtractBox {
if vpos < 0 || vpos >= rb { i = tpos + 1 continue }
local ds = RegexFlow.digits_from(ast_json, vpos + 8)
if ds != "" { vals.push(RegexFlow.to_int(ds)) }
i = vpos + 8 + ds.size()
i = vpos + 8 + ds.length()
}
return vals
}
@ -90,7 +90,7 @@ static box Stage1IntArgsExtractBox {
if ak < 0 { return 1 }
local lb = RegexFlow.find_from(ast_json, "[", ak)
if lb < 0 { return 1 }
local rb = ast_json.size()
local rb = ast_json.length()
local i2 = lb + 1
local depth = 1
loop(true) {

View File

@ -25,16 +25,16 @@ static box Stage1JsonScannerBox {
local escaped = "\\\"" + key + "\\\":\\\""
local p1 = JsonCursorBox.find_key_dual(s, plain, escaped, start_pos)
if p1 >= 0 { return p1 }
if plain.size() >= 2 {
local head = plain.substring(0, plain.size() - 1)
local last = plain.substring(plain.size() - 1, plain.size())
if plain.length() >= 2 {
local head = plain.substring(0, plain.length() - 1)
local last = plain.substring(plain.length() - 1, plain.length())
local spaced = head + " " + last
local p2 = JsonCursorBox.find_from(s, spaced, start_pos)
if p2 >= 0 { return p2 }
// Escaped + spaced: tolerate JSON embedded as string with colon-space
if escaped.size() >= 2 {
local ehead = escaped.substring(0, escaped.size() - 1)
local elast = escaped.substring(escaped.size() - 1, escaped.size())
if escaped.length() >= 2 {
local ehead = escaped.substring(0, escaped.length() - 1)
local elast = escaped.substring(escaped.length() - 1, escaped.length())
local espaced = ehead + " " + elast
local p3 = JsonCursorBox.find_from(s, espaced, start_pos)
if p3 >= 0 { return p3 }
@ -46,7 +46,7 @@ static box Stage1JsonScannerBox {
value_start_after_key_pos(s, key_pos) {
if s == null { return -1 }
local i = key_pos
local n = s.size()
local n = s.length()
loop(i < n) {
local ch = s.substring(i,i+1)
if ch == ":" { i = i + 1 break }
@ -77,21 +77,21 @@ static box Stage1JsonScannerBox {
if nend <= vstart { return null }
local label = s.substring(vstart, nend)
local lb = JsonCursorBox.find_from(s, "[", apos)
local rb = s.size()
local rb = s.length()
if lb >= 0 {
// Use JsonCursorBox for escape-aware array end seeking
local rb_result = JsonCursorBox.seek_array_end(s, lb)
if rb_result >= lb { rb = rb_result }
}
local args_text = s.substring(apos, rb)
return map({ label: label, args_text: args_text, label_pos: npos, args_pos: apos, label_key: label_key })
return { label: label, args_text: args_text, label_pos: npos, args_pos: apos, label_key: label_key }
}
// Backward compatible helper for Call (label_key = "name")
extract_name_args(ast_json, start_pos) {
local m = me.extract_label_args(ast_json, "name", start_pos)
if m == null { return null }
call("MapBox.set/3", m, "name", BoxHelpers.map_get(m, "label"))
m.set("name", BoxHelpers.map_get(m, "label"))
return m
}
}

View File

@ -33,16 +33,16 @@ static box UsingResolverBox {
local key = s.substring(kpos + 1, kend)
local dot = RegexFlow.last_index_of(key, ".")
local last = key
if dot >= 0 { last = key.substring(dot + 1, key.size()) }
if dot >= 0 { last = key.substring(dot + 1, key.length()) }
if last == alias {
if found == null { found = key } else { return null }
} else {
// first-letter case-insensitive match
if last.size() == alias.size() && last.size() > 0 {
if last.length() == alias.length() && last.length() > 0 {
local l0 = last.substring(0,1)
local a0 = alias.substring(0,1)
local restl = last.substring(1, last.size())
local resta = alias.substring(1, alias.size())
local restl = last.substring(1, last.length())
local resta = alias.substring(1, alias.length())
if restl == resta {
local U = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; local L = "abcdefghijklmnopqrstuvwxyz"
local idxL = L.indexOf(l0); local idxU = U.indexOf(l0)

View File

@ -16,10 +16,10 @@ static box JsonProgramBox {
ensure_meta(json, usings_json, externs_json) {
local payload = usings_json
if payload == null { payload = "[]" }
if payload.size() == 0 { payload = "[]" }
if payload.length() == 0 { payload = "[]" }
local ext = externs_json
if ext == null { ext = "[]" }
if ext.size() == 0 { ext = "[]" }
if ext.length() == 0 { ext = "[]" }
if json == null {
return "{\"version\":0,\"kind\":\"Program\",\"body\":[],\"meta\":{\"usings\":" + payload + ",\"extern_c\":" + ext + "}}"
@ -28,11 +28,11 @@ static box JsonProgramBox {
local n = json.lastIndexOf("}")
if n < 0 { return json }
local head = json.substring(0, n)
local tail = json.substring(n, json.size())
local tail = json.substring(n, json.length())
local needs_comma = 1
if head.size() == 0 { needs_comma = 0 }
if head.length() == 0 { needs_comma = 0 }
else {
local last = head.substring(head.size() - 1, head.size())
local last = head.substring(head.length() - 1, head.length())
if last == "{" || last == "," { needs_comma = 0 }
}
if needs_comma == 1 { head = head + "," }
@ -78,11 +78,11 @@ static box JsonProgramBox {
_replace_all(text, pat, rep) {
if text == null { return text }
local m = pat.size()
local m = pat.length()
if m == 0 { return text }
local out = ""
local i = 0
local n = text.size()
local n = text.length()
loop(i < n) {
if StringHelpers.starts_with(text, i, pat) == 1 {
out = out + rep
@ -100,18 +100,18 @@ static box JsonProgramBox {
normalize_stmt_array(array_json) {
if array_json == null { return "[]" }
local trimmed = me.trim(array_json)
if trimmed.size() == 0 { return "[]" }
if trimmed.length() == 0 { return "[]" }
if trimmed == "null" { return "[]" }
if trimmed.size() < 2 { return "[]" }
if trimmed.length() < 2 { return "[]" }
if trimmed.substring(0, 1) != "[" { return trimmed }
if trimmed == "[]" { return "[]" }
local parts = JsonUtilsBox.split_top_level(trimmed)
local out = new ArrayBox()
local i = 0
loop(i < parts.size()) {
loop(i < parts.length()) {
local item = me.trim(parts.get(i))
if item.size() > 0 {
if item.length() > 0 {
out.push(me.normalize_stmt(item))
}
i = i + 1
@ -185,18 +185,18 @@ static box JsonProgramBox {
normalize_expr_array(array_json) {
if array_json == null { return "[]" }
local trimmed = me.trim(array_json)
if trimmed.size() == 0 { return "[]" }
if trimmed.length() == 0 { return "[]" }
if trimmed == "null" { return "[]" }
if trimmed.size() < 2 { return "[]" }
if trimmed.length() < 2 { return "[]" }
if trimmed.substring(0, 1) != "[" { return trimmed }
if trimmed == "[]" { return "[]" }
local parts = JsonUtilsBox.split_top_level(trimmed)
local out = new ArrayBox()
local i = 0
loop(i < parts.size()) {
loop(i < parts.length()) {
local item = me.trim(parts.get(i))
if item.size() > 0 {
if item.length() > 0 {
local norm = me.normalize_expr(item)
if norm == null { norm = item }
out.push(norm)
@ -294,19 +294,19 @@ static box JsonProgramBox {
_trim_all(text) {
if text == null { return "" }
local n = text.size()
local n = text.length()
local start = 0
loop(start < n) {
local ch = call("String.substring/2", text, start, start + 1)
local ch = text.substring(start, start + 1)
if ch == " " || ch == "\t" || ch == "\n" || ch == "\r" { start = start + 1 } else { break }
}
local end_idx = n
loop(end_idx > start) {
local ch2 = call("String.substring/2", text, end_idx - 1, end_idx)
local ch2 = text.substring(end_idx - 1, end_idx)
if ch2 == " " || ch2 == "\t" || ch2 == "\n" || ch2 == "\r" || ch2 == ";" { end_idx = end_idx - 1 } else { break }
}
if end_idx <= start { return "" }
local part = call("String.substring/2", text, start, end_idx)
local part = text.substring(start, end_idx)
if part == null { return "" }
return part
}
@ -318,7 +318,7 @@ static box JsonProgramBox {
if parts == null { return "" }
local out = ""
local i = 0
local n = parts.size()
local n = parts.length()
loop(i < n) {
local item = parts.get(i)
if i == 0 { out = out + item } else { out = out + "," + item }