diff --git a/apps/lib/json_builder.nyash b/apps/lib/json_builder.nyash new file mode 100644 index 00000000..e28dc9fa --- /dev/null +++ b/apps/lib/json_builder.nyash @@ -0,0 +1,113 @@ +// JsonBuilder — Minimal helpers to construct AST JSON v0 fragments as strings +// Usage: local JB = include "apps/lib/json_builder.nyash" +// local s = JB.literal_string("x") + +static box JsonBuilder { + // Join array of strings with a separator + join(arr, sep) { + local out = "" + local i = 0 + local n = arr.length() + loop(i < n) { + out = out + arr.get(i) + if i + 1 < n { out = out + sep } + i = i + 1 + } + return out + } + + // JSON string escaping (minimal): backslash, quote, newline, carriage return, tab + 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 } + i = i + 1 + } + return out + } + + quote(s) { return "\"" + this.escape_string(s) + "\"" } + + // ==== Node builders (strings) ==== + literal_string(s) { + return "{\"kind\":\"Literal\",\"value\":{\"type\":\"string\",\"value\":" + this.quote(s) + "}}" + } + literal_int(i) { + return "{\"kind\":\"Literal\",\"value\":{\"type\":\"int\",\"value\":" + i + "}}" + } + literal_bool(b) { + if b { return "{\"kind\":\"Literal\",\"value\":{\"type\":\"bool\",\"value\":true}}" } + return "{\"kind\":\"Literal\",\"value\":{\"type\":\"bool\",\"value\":false}}" + } + literal_null() { return "{\"kind\":\"Literal\",\"value\":{\"type\":\"null\"}}" } + + variable(name) { + return "{\"kind\":\"Variable\",\"name\":" + this.quote(name) + "}" + } + + binary(op, lhs_json, rhs_json) { + return "{\"kind\":\"BinaryOp\",\"op\":" + this.quote(op) + ",\"left\":" + lhs_json + ",\"right\":" + rhs_json + "}" + } + + assignment(target_json, value_json) { + return "{\"kind\":\"Assignment\",\"target\":" + target_json + ",\"value\":" + value_json + "}" + } + + local(vars, inits) { + // vars: array[string], inits: array[string|null] + local vs = [] + local i = 0 + loop(i < vars.length()) { + vs.push(this.quote(vars.get(i))) + i = i + 1 + } + local is = [] + i = 0 + loop(i < inits.length()) { + local v = inits.get(i) + if v == null { is.push("null") } else { is.push(v) } + i = i + 1 + } + return "{\"kind\":\"Local\",\"variables\":[" + this.join(vs, ",") + "],\"inits\":[" + this.join(is, ",") + "]}" + } + + array(elems_json) { + return "{\"kind\":\"Array\",\"elements\":[" + this.join(elems_json, ",") + "]}" + } + map(entries) { + // entries: array of [k:string, v:json] + local parts = [] + local i = 0 + loop(i < entries.length()) { + local kv = entries.get(i) + local k = kv.get(0) + local v = kv.get(1) + parts.push("{\"k\":" + this.quote(k) + ",\"v\":" + v + "}") + i = i + 1 + } + return "{\"kind\":\"Map\",\"entries\":[" + this.join(parts, ",") + "]}" + } + + if_(cond_json, then_stmts_json, else_stmts_json) { + local then_s = "[" + this.join(then_stmts_json, ",") + "]" + local else_s = "null" + if else_stmts_json != null { else_s = "[" + this.join(else_stmts_json, ",") + "]" } + return "{\"kind\":\"If\",\"condition\":" + cond_json + ",\"then\":" + then_s + ",\"else\":" + else_s + "}" + } + + loop_(cond_json, body_stmts_json) { + return "{\"kind\":\"Loop\",\"condition\":" + cond_json + ",\"body\":[" + this.join(body_stmts_json, ",") + "]}" + } + + program(stmts_json) { + return "{\"kind\":\"Program\",\"statements\":[" + this.join(stmts_json, ",") + "]}" + } +} + diff --git a/apps/macros/examples/loop_normalize_macro.nyash b/apps/macros/examples/loop_normalize_macro.nyash index 75d933a3..082693a1 100644 --- a/apps/macros/examples/loop_normalize_macro.nyash +++ b/apps/macros/examples/loop_normalize_macro.nyash @@ -6,8 +6,11 @@ static box MacroBoxSpec { static function name() { return "LoopNormalize" } static function expand(json, ctx) { - // For MVP, return input unchanged. + // MVP: return input unchanged, but ensure JsonBuilder is loadable for next step. + // Example usage (commented): + // local JB = include "apps/lib/json_builder.nyash" + // local zero = JB.literal_int(0) + // _ = zero // suppress unused return json } } - diff --git a/docs/guides/loopform.md b/docs/guides/loopform.md index ba46826a..72ab005f 100644 --- a/docs/guides/loopform.md +++ b/docs/guides/loopform.md @@ -11,6 +11,18 @@ export NYASH_MACRO_PATHS=apps/macros/examples/loop_normalize_macro.nyash ``` - 自己ホスト前展開(auto)を利用して、parse直後にLoopForm展開を有効化(PyVM環境)。 +JSON生成ユーティリティ(JsonBuilder) +- ループ正規化では AST JSON v0 の断片を安全に構成する必要があります。 +- 最小ユーティリティとして `apps/lib/json_builder.nyash` を提供しています(includeで読み込み、文字列でJSON断片を生成)。 +- 例: +``` +local JB = include "apps/lib/json_builder.nyash" +local v_i = JB.variable("i") +local v_sum = JB.variable("sum") +local lit_0 = JB.literal_int(0) +local assign = JB.assignment(v_i, JB.binary("+", v_i, JB.literal_int(1))) +``` + 正規化の考え方 - ループで更新される変数群をタプルに束ね、ヘッダに“1個のφ”を置く。 - break/continue は“次キャリア”または“現キャリア”で遷移し、一貫した合流点を保つ。 @@ -29,4 +41,3 @@ export NYASH_MACRO_PATHS=apps/macros/examples/loop_normalize_macro.nyash 参考 - docs/development/roadmap/phases/phase-17-loopform-selfhost/ -