macro(if/match): stabilize PeekExpr→If normalization via internal-child; default runner off; propagate child stderr; fix JsonBuilder local_decl; add scope-hints doc; extend PHI smoke; golden for match literal passes
This commit is contained in:
@ -12,7 +12,7 @@ static box ControlFlowBuilder {
|
||||
if_expr(cond_json, then_expr_json, else_expr_json, res_name) {
|
||||
local JB = include "apps/lib/json_builder.nyash"
|
||||
local res_var = JB.variable(res_name)
|
||||
local decl = JB.local([res_name], [null])
|
||||
local decl = JB.local_decl([res_name], [null])
|
||||
local then_s = [ JB.assignment(res_var, then_expr_json) ]
|
||||
local else_s = [ JB.assignment(res_var, else_expr_json) ]
|
||||
local ifn = JB.if_(cond_json, then_s, else_s)
|
||||
@ -31,11 +31,11 @@ static box ControlFlowBuilder {
|
||||
local PT = include "apps/lib/pattern_builder.nyash"
|
||||
|
||||
// scrutinee を一度だけ評価
|
||||
local decl_scrut = JB.local([scrut_name], [scrut_json])
|
||||
local decl_scrut = JB.local_decl([scrut_name], [scrut_json])
|
||||
local scrut_var = JB.variable(scrut_name)
|
||||
|
||||
// res の宣言
|
||||
local res_decl = JB.local([res_name], [null])
|
||||
local res_decl = JB.local_decl([res_name], [null])
|
||||
local res_var = JB.variable(res_name)
|
||||
|
||||
// デフォルト(任意)を抽出
|
||||
@ -96,4 +96,3 @@ static box ControlFlowBuilder {
|
||||
return [ decl_scrut, res_decl, chain ]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,18 +16,17 @@ static box JsonBuilder {
|
||||
return out
|
||||
}
|
||||
|
||||
// JSON string escaping (minimal): backslash, quote, newline, carriage return, tab
|
||||
// JSON string escaping (minimal): backslash, quote
|
||||
escape_string(s) {
|
||||
local out = ""
|
||||
local i = 0
|
||||
loop(i < s.length()) {
|
||||
local ch = s.substring(i, i + 1)
|
||||
if ch == "\\" { out = out + "\\\\" }
|
||||
else if ch == "\"" { out = out + "\\\"" }
|
||||
else if ch == "\n" { out = out + "\\n" }
|
||||
else if ch == "\r" { out = out + "\\r" }
|
||||
else if ch == "\t" { out = out + "\\t" }
|
||||
else { out = out + ch }
|
||||
if ch == "\\" {
|
||||
out = out + "\\\\"
|
||||
} else {
|
||||
if ch == "\"" { out = out + "\\\"" } else { out = out + ch }
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
@ -70,7 +69,7 @@ static box JsonBuilder {
|
||||
return "{\"kind\":\"Return\",\"value\":" + v + "}"
|
||||
}
|
||||
|
||||
local(vars, inits) {
|
||||
local_decl(vars, inits) {
|
||||
// vars: array[string], inits: array[string|null]
|
||||
local vs = []
|
||||
local i = 0
|
||||
|
||||
@ -8,7 +8,6 @@ static box MacroBoxSpec {
|
||||
|
||||
expand(json, ctx) {
|
||||
local JB = include "apps/lib/json_builder.nyash"
|
||||
local CF = include "apps/lib/cf_builder.nyash"
|
||||
|
||||
// --- helpers copied/adapted from loop_normalize ---
|
||||
function parse_value(s, i) {
|
||||
@ -19,8 +18,14 @@ static box MacroBoxSpec {
|
||||
local j = i + 1
|
||||
loop(j < n) {
|
||||
local c = s.substring(j, j+1)
|
||||
if c == "\\" { j = j + 2; continue }
|
||||
if c == "\"" { j = j + 1; break }
|
||||
if c == "\\" {
|
||||
j = j + 2
|
||||
continue
|
||||
}
|
||||
if c == "\"" {
|
||||
j = j + 1
|
||||
break
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
return ("" + j) + "#" + s.substring(i, j)
|
||||
@ -189,7 +194,10 @@ static box MacroBoxSpec {
|
||||
local start = -1
|
||||
local i2 = 0
|
||||
loop(i2 + tok.length() <= obj.length()) {
|
||||
if obj.substring(i2, i2 + tok.length()) == tok { start = i2; break }
|
||||
if obj.substring(i2, i2 + tok.length()) == tok {
|
||||
start = i2
|
||||
break
|
||||
}
|
||||
i2 = i2 + 1
|
||||
}
|
||||
if start < 0 { return obj }
|
||||
@ -201,12 +209,38 @@ static box MacroBoxSpec {
|
||||
return prefix + new_val + suffix
|
||||
}
|
||||
|
||||
function peek_to_if_expr(peek_json) {
|
||||
local scr = get_field(peek_json, "scrutinee")
|
||||
local arms_raw = get_field(peek_json, "arms")
|
||||
local arms = split_array(arms_raw)
|
||||
local else_expr = get_field(peek_json, "else")
|
||||
local current = else_expr
|
||||
local idx = arms.length()
|
||||
loop(idx > 0) {
|
||||
idx = idx - 1
|
||||
local arm_json = arms.get(idx)
|
||||
local lit_json = get_field(arm_json, "literal")
|
||||
local body_json = get_field(arm_json, "body")
|
||||
local cond = JB.binary("==", scr, lit_json)
|
||||
local then_arr = [body_json]
|
||||
local else_arr = null
|
||||
if current != null { else_arr = [current] }
|
||||
local if_json = JB.if_(cond, then_arr, else_arr)
|
||||
current = if_json
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
function rewrite_stmt_node(njson) {
|
||||
// Assignment with If RHS → If(assign ...)
|
||||
if has_kind(njson, "Assignment") {
|
||||
local tgt = get_field(njson, "target")
|
||||
local val_json = get_field(njson, "value")
|
||||
if val_json != null && has_kind(val_json, "PeekExpr") {
|
||||
val_json = peek_to_if_expr(val_json)
|
||||
njson = replace_field(njson, "value", val_json)
|
||||
}
|
||||
if val_json != null && has_kind(val_json, "If") {
|
||||
local tgt = get_field(njson, "target")
|
||||
local cond = get_field(val_json, "condition")
|
||||
local th = get_field(val_json, "then")
|
||||
local el = get_field(val_json, "else")
|
||||
@ -214,55 +248,100 @@ static box MacroBoxSpec {
|
||||
local th_e = null
|
||||
if th != null && th.substring(0,1) == "[" {
|
||||
local parts = split_array(th)
|
||||
th_e = parts.length() > 0 ? parts.get(0) : JB.literal_null()
|
||||
if parts.length() > 0 { th_e = parts.get(0) } else { th_e = JB.literal_null() }
|
||||
}
|
||||
local el_e = null
|
||||
if el == null || el == "null" {
|
||||
el_e = null
|
||||
} else if el.substring(0,1) == "[" {
|
||||
local parts2 = split_array(el)
|
||||
el_e = parts2.length() > 0 ? parts2.get(0) : JB.literal_null()
|
||||
if parts2.length() > 0 { el_e = parts2.get(0) } else { el_e = JB.literal_null() }
|
||||
}
|
||||
local then_s = [ JB.assignment(tgt, th_e) ]
|
||||
local else_s = null
|
||||
if el_e != null { else_s = [ JB.assignment(tgt, el_e) ] }
|
||||
return CF.if_stmt(cond, then_s, else_s)
|
||||
return JB.if_(cond, then_s, else_s)
|
||||
}
|
||||
}
|
||||
// Return with If value → If(Return ...)
|
||||
if has_kind(njson, "Return") {
|
||||
local val_json = get_field(njson, "value")
|
||||
if val_json != null && val_json != "null" && has_kind(val_json, "PeekExpr") {
|
||||
val_json = peek_to_if_expr(val_json)
|
||||
njson = replace_field(njson, "value", val_json)
|
||||
}
|
||||
if val_json != null && val_json != "null" && has_kind(val_json, "If") {
|
||||
local cond = get_field(val_json, "condition")
|
||||
local th = get_field(val_json, "then")
|
||||
local el = get_field(val_json, "else")
|
||||
local th_e = null
|
||||
if th != null && th.substring(0,1) == "[" { local p = split_array(th); th_e = p.length()>0 ? p.get(0) : JB.literal_null() }
|
||||
if th != null && th.substring(0,1) == "[" {
|
||||
local parts = split_array(th)
|
||||
if parts.length() > 0 { th_e = parts.get(0) } else { th_e = JB.literal_null() }
|
||||
}
|
||||
local el_e = null
|
||||
if el == null || el == "null" { el_e = null } else if el.substring(0,1) == "[" { local p2 = split_array(el); el_e = p2.length()>0 ? p2.get(0) : JB.literal_null() }
|
||||
if el == null || el == "null" {
|
||||
el_e = null
|
||||
} else if el.substring(0,1) == "[" {
|
||||
local parts2 = split_array(el)
|
||||
if parts2.length() > 0 { el_e = parts2.get(0) } else { el_e = JB.literal_null() }
|
||||
}
|
||||
local then_s = [ JB.return_(th_e) ]
|
||||
local else_s = null
|
||||
if el_e != null { else_s = [ JB.return_(el_e) ] }
|
||||
return CF.if_stmt(cond, then_s, else_s)
|
||||
return JB.if_(cond, then_s, else_s)
|
||||
}
|
||||
}
|
||||
// Print with If expression → If(Print ...)
|
||||
if has_kind(njson, "Print") {
|
||||
local ex_json = get_field(njson, "expression")
|
||||
if ex_json != null && has_kind(ex_json, "PeekExpr") {
|
||||
ex_json = peek_to_if_expr(ex_json)
|
||||
njson = replace_field(njson, "expression", ex_json)
|
||||
}
|
||||
if ex_json != null && has_kind(ex_json, "If") {
|
||||
local cond = get_field(ex_json, "condition")
|
||||
local th = get_field(ex_json, "then")
|
||||
local el = get_field(ex_json, "else")
|
||||
local th_e = null
|
||||
if th != null && th.substring(0,1) == "[" { local p = split_array(th); th_e = p.length()>0 ? p.get(0) : JB.literal_null() }
|
||||
if th != null && th.substring(0,1) == "[" {
|
||||
local parts = split_array(th)
|
||||
if parts.length() > 0 { th_e = parts.get(0) } else { th_e = JB.literal_null() }
|
||||
}
|
||||
local el_e = null
|
||||
if el == null || el == "null" { el_e = null } else if el.substring(0,1) == "[" { local p2 = split_array(el); el_e = p2.length()>0 ? p2.get(0) : JB.literal_null() }
|
||||
if el == null || el == "null" {
|
||||
el_e = null
|
||||
} else if el.substring(0,1) == "[" {
|
||||
local parts2 = split_array(el)
|
||||
if parts2.length() > 0 { el_e = parts2.get(0) } else { el_e = JB.literal_null() }
|
||||
}
|
||||
local then_s = [ JB.print_(th_e) ]
|
||||
local else_s = null
|
||||
if el_e != null { else_s = [ JB.print_(el_e) ] }
|
||||
return CF.if_stmt(cond, then_s, else_s)
|
||||
return JB.if_(cond, then_s, else_s)
|
||||
}
|
||||
}
|
||||
if has_kind(njson, "Local") {
|
||||
local inits = get_field(njson, "inits")
|
||||
if inits != null && inits.substring(0,1) == "[" {
|
||||
local elems = split_array(inits)
|
||||
local mutated = 0
|
||||
local idx = 0
|
||||
loop(idx < elems.length()) {
|
||||
local init_json = elems.get(idx)
|
||||
if init_json != "null" && has_kind(init_json, "PeekExpr") {
|
||||
local replaced = peek_to_if_expr(init_json)
|
||||
elems.set(idx, replaced)
|
||||
mutated = 1
|
||||
}
|
||||
idx = idx + 1
|
||||
}
|
||||
if mutated == 1 {
|
||||
njson = replace_field(njson, "inits", "[" + JB.join(elems, ",") + "]")
|
||||
}
|
||||
}
|
||||
return njson
|
||||
}
|
||||
// Recurse for If/Loop nodes
|
||||
if has_kind(njson, "If") {
|
||||
local th = get_field(njson, "then")
|
||||
@ -271,14 +350,20 @@ static box MacroBoxSpec {
|
||||
local parts = split_array(th)
|
||||
local rebuilt = []
|
||||
local i3 = 0
|
||||
loop(i3 < parts.length()) { rebuilt.push(rewrite_stmt_node(parts.get(i3))); i3 = i3 + 1 }
|
||||
loop(i3 < parts.length()) {
|
||||
rebuilt.push(rewrite_stmt_node(parts.get(i3)))
|
||||
i3 = i3 + 1
|
||||
}
|
||||
njson = replace_field(njson, "then", "[" + JB.join(rebuilt, ",") + "]")
|
||||
}
|
||||
if el != null && el != "null" && el.substring(0,1) == "[" {
|
||||
local parts2 = split_array(el)
|
||||
local rebuilt2 = []
|
||||
local j3 = 0
|
||||
loop(j3 < parts2.length()) { rebuilt2.push(rewrite_stmt_node(parts2.get(j3))); j3 = j3 + 1 }
|
||||
loop(j3 < parts2.length()) {
|
||||
rebuilt2.push(rewrite_stmt_node(parts2.get(j3)))
|
||||
j3 = j3 + 1
|
||||
}
|
||||
njson = replace_field(njson, "else", "[" + JB.join(rebuilt2, ",") + "]")
|
||||
}
|
||||
return njson
|
||||
@ -289,7 +374,10 @@ static box MacroBoxSpec {
|
||||
local parts = split_array(body)
|
||||
local rebuilt = []
|
||||
local i4 = 0
|
||||
loop(i4 < parts.length()) { rebuilt.push(rewrite_stmt_node(parts.get(i4))); i4 = i4 + 1 }
|
||||
loop(i4 < parts.length()) {
|
||||
rebuilt.push(rewrite_stmt_node(parts.get(i4)))
|
||||
i4 = i4 + 1
|
||||
}
|
||||
njson = replace_field(njson, "body", "[" + JB.join(rebuilt, ",") + "]")
|
||||
}
|
||||
return njson
|
||||
@ -304,7 +392,10 @@ static box MacroBoxSpec {
|
||||
local parts = split_array(stm)
|
||||
local rebuilt = []
|
||||
local i0 = 0
|
||||
loop(i0 < parts.length()) { rebuilt.push(rewrite_stmt_node(parts.get(i0))); i0 = i0 + 1 }
|
||||
loop(i0 < parts.length()) {
|
||||
rebuilt.push(rewrite_stmt_node(parts.get(i0)))
|
||||
i0 = i0 + 1
|
||||
}
|
||||
return replace_field(json, "statements", "[" + JB.join(rebuilt, ",") + "]")
|
||||
}
|
||||
}
|
||||
|
||||
7
apps/tests/macro/match/literal_basic.nyash
Normal file
7
apps/tests/macro/match/literal_basic.nyash
Normal file
@ -0,0 +1,7 @@
|
||||
local d = 2
|
||||
local r = match d {
|
||||
1 => 10
|
||||
2 => 20
|
||||
_ => 30
|
||||
}
|
||||
print(r)
|
||||
Reference in New Issue
Block a user