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

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