## ✅ 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>
212 lines
6.0 KiB
Plaintext
212 lines
6.0 KiB
Plaintext
// JsonUtilsBox — JSON読み取りユーティリティの共通箱
|
|
// 責務: JSON値抽出・JSON構造パース・トップレベル配列分割
|
|
// Extracted from JsonProgramBox (480行 → 345行, 削減 ~135行)
|
|
using selfhost.shared.common.string_helpers as StringHelpers
|
|
|
|
static box JsonUtilsBox {
|
|
// Extract JSON value by key (returns value with '@' + end position marker)
|
|
extract_value(json, key) {
|
|
if json == null { return null }
|
|
local pattern = "\"" + key + "\""
|
|
local idx = StringHelpers.index_of(json, 0, pattern)
|
|
if idx < 0 { return null }
|
|
idx = idx + pattern.length()
|
|
idx = StringHelpers.skip_ws(json, idx)
|
|
if json.substring(idx, idx + 1) != ":" { return null }
|
|
idx = StringHelpers.skip_ws(json, idx + 1)
|
|
local res = me.read_value(json, idx)
|
|
local at = StringHelpers.last_index_of(res, "@")
|
|
if at < 0 { return null }
|
|
return res.substring(0, at)
|
|
}
|
|
|
|
// Extract string value by key with default fallback
|
|
extract_string_value(json, key, default_value) {
|
|
local raw = me.extract_value(json, key)
|
|
if raw == null { return default_value }
|
|
local trimmed = StringHelpers.trim(raw)
|
|
if trimmed.length() >= 2 && trimmed.substring(0,1) == "\"" && trimmed.substring(trimmed.length()-1, trimmed.length()) == "\"" {
|
|
return me.unescape_string(trimmed.substring(1, trimmed.length()-1))
|
|
}
|
|
return default_value
|
|
}
|
|
|
|
// Read JSON value (dispatch to appropriate reader)
|
|
read_value(json, idx) {
|
|
local n = json.length()
|
|
if idx >= n { return "@" + StringHelpers.int_to_str(idx) }
|
|
local ch = json.substring(idx, idx + 1)
|
|
if ch == "\"" { return me.read_string(json, idx) }
|
|
if ch == "{" { return me.read_object(json, idx) }
|
|
if ch == "[" { return me.read_array(json, idx) }
|
|
return me.read_literal(json, idx)
|
|
}
|
|
|
|
// Read JSON string (escape-aware) with position marker
|
|
read_string(json, idx) {
|
|
local i = idx + 1
|
|
local n = json.length()
|
|
local done = 0
|
|
loop(done == 0 && i < n) {
|
|
local ch = json.substring(i, i + 1)
|
|
if ch == "\\" {
|
|
i = i + 2
|
|
} else {
|
|
if ch == "\"" { done = 1 i = i + 1 } else { i = i + 1 }
|
|
}
|
|
}
|
|
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
|
}
|
|
|
|
// Skip JSON string (returns end position)
|
|
skip_string(json, idx) {
|
|
local i = idx + 1
|
|
local n = json.length()
|
|
local done = 0
|
|
loop(done == 0 && i < n) {
|
|
local ch = json.substring(i, i + 1)
|
|
if ch == "\\" { i = i + 2 }
|
|
else { if ch == "\"" { done = 1 i = i + 1 } else { i = i + 1 } }
|
|
}
|
|
return i
|
|
}
|
|
|
|
// Read JSON object (bracket-aware) with position marker
|
|
read_object(json, idx) {
|
|
local depth = 0
|
|
local i = idx
|
|
local n = json.length()
|
|
loop(i < n) {
|
|
local ch = json.substring(i, i + 1)
|
|
if ch == "\"" {
|
|
i = me.skip_string(json, i)
|
|
} else {
|
|
if ch == "{" { depth = depth + 1 }
|
|
else if ch == "}" {
|
|
depth = depth - 1
|
|
if depth == 0 { i = i + 1 break }
|
|
}
|
|
i = i + 1
|
|
}
|
|
}
|
|
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
|
}
|
|
|
|
// Read JSON array (bracket-aware) with position marker
|
|
read_array(json, idx) {
|
|
local depth = 0
|
|
local i = idx
|
|
local n = json.length()
|
|
loop(i < n) {
|
|
local ch = json.substring(i, i + 1)
|
|
if ch == "\"" {
|
|
i = me.skip_string(json, i)
|
|
} else {
|
|
if ch == "[" { depth = depth + 1 }
|
|
else if ch == "]" {
|
|
depth = depth - 1
|
|
if depth == 0 { i = i + 1 break }
|
|
}
|
|
i = i + 1
|
|
}
|
|
}
|
|
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
|
}
|
|
|
|
// Read JSON literal (number/true/false/null) with position marker
|
|
read_literal(json, idx) {
|
|
local n = json.length()
|
|
local i = idx
|
|
loop(i < n) {
|
|
local ch = json.substring(i, i + 1)
|
|
if ch == "," || ch == "}" || ch == "]" { break }
|
|
i = i + 1
|
|
}
|
|
return json.substring(idx, i) + "@" + StringHelpers.int_to_str(i)
|
|
}
|
|
|
|
// Split JSON array at top-level commas (depth-aware, escape-aware)
|
|
split_top_level(array_json) {
|
|
local out = new ArrayBox()
|
|
local n = array_json.length()
|
|
local i = 1
|
|
local start = 1
|
|
local depth = 0
|
|
local in_string = 0
|
|
loop(i < n - 1) {
|
|
local ch = array_json.substring(i, i + 1)
|
|
if in_string == 1 {
|
|
if ch == "\\" {
|
|
i = i + 2
|
|
} else {
|
|
if ch == "\"" { in_string = 0 }
|
|
i = i + 1
|
|
}
|
|
} else {
|
|
if ch == "\"" {
|
|
in_string = 1
|
|
i = i + 1
|
|
} else {
|
|
if ch == "{" || ch == "[" { depth = depth + 1 }
|
|
else if ch == "}" || ch == "]" { depth = depth - 1 }
|
|
else if ch == "," && depth == 0 {
|
|
local part = array_json.substring(start, i)
|
|
out.push(part)
|
|
start = i + 1
|
|
}
|
|
i = i + 1
|
|
}
|
|
}
|
|
}
|
|
if start < n - 1 {
|
|
out.push(array_json.substring(start, n - 1))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Unescape JSON string (convert \n, \t, \r, \\, \" to actual characters)
|
|
unescape_string(s) {
|
|
if s == null { return "" }
|
|
local out = ""
|
|
local i = 0
|
|
local n = s.length()
|
|
loop(i < n) {
|
|
local ch = s.substring(i, i + 1)
|
|
if ch == "\\" && i + 1 < n {
|
|
local nx = s.substring(i + 1, i + 2)
|
|
if nx == "\"" {
|
|
out = out + "\""
|
|
i = i + 2
|
|
} else {
|
|
if nx == "\\" {
|
|
out = out + "\\"
|
|
i = i + 2
|
|
} else {
|
|
if nx == "n" {
|
|
out = out + "\n"
|
|
i = i + 2
|
|
} else {
|
|
if nx == "r" {
|
|
out = out + "\r"
|
|
i = i + 2
|
|
} else {
|
|
if nx == "t" {
|
|
out = out + "\t"
|
|
i = i + 2
|
|
} else {
|
|
out = out + ch
|
|
i = i + 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
out = out + ch
|
|
i = i + 1
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
}
|