behavior-contracts 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generator/emit-raw-abi-go.d.ts +45 -0
- package/dist/generator/emit-raw-abi-go.d.ts.map +1 -0
- package/dist/generator/emit-raw-abi-go.js +345 -0
- package/dist/generator/emit-raw-abi-go.js.map +1 -0
- package/dist/generator/emit-raw-abi-rust.d.ts +45 -0
- package/dist/generator/emit-raw-abi-rust.d.ts.map +1 -0
- package/dist/generator/emit-raw-abi-rust.js +380 -0
- package/dist/generator/emit-raw-abi-rust.js.map +1 -0
- package/dist/generator/emit-straightline-go.d.ts +27 -46
- package/dist/generator/emit-straightline-go.d.ts.map +1 -1
- package/dist/generator/emit-straightline-go.js +688 -190
- package/dist/generator/emit-straightline-go.js.map +1 -1
- package/dist/generator/emit-straightline-php.d.ts +1 -1
- package/dist/generator/emit-straightline-php.d.ts.map +1 -1
- package/dist/generator/emit-straightline-php.js +451 -20
- package/dist/generator/emit-straightline-php.js.map +1 -1
- package/dist/generator/emit-straightline-python.d.ts.map +1 -1
- package/dist/generator/emit-straightline-python.js +426 -22
- package/dist/generator/emit-straightline-python.js.map +1 -1
- package/dist/generator/emit-straightline-rust.d.ts +5 -0
- package/dist/generator/emit-straightline-rust.d.ts.map +1 -1
- package/dist/generator/emit-straightline-rust.js +596 -48
- package/dist/generator/emit-straightline-rust.js.map +1 -1
- package/dist/generator/emit-straightline-typed-go.d.ts +95 -2
- package/dist/generator/emit-straightline-typed-go.d.ts.map +1 -1
- package/dist/generator/emit-straightline-typed-go.js +42 -3
- package/dist/generator/emit-straightline-typed-go.js.map +1 -1
- package/dist/generator/emit-straightline-typed-rust.d.ts +98 -2
- package/dist/generator/emit-straightline-typed-rust.d.ts.map +1 -1
- package/dist/generator/emit-straightline-typed-rust.js +50 -7
- package/dist/generator/emit-straightline-typed-rust.js.map +1 -1
- package/dist/generator/emit-straightline-typescript.d.ts +26 -15
- package/dist/generator/emit-straightline-typescript.d.ts.map +1 -1
- package/dist/generator/emit-straightline-typescript.js +703 -147
- package/dist/generator/emit-straightline-typescript.js.map +1 -1
- package/dist/generator/index.d.ts +4 -2
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +11 -2
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/literal.d.ts +3 -1
- package/dist/generator/literal.d.ts.map +1 -1
- package/dist/generator/literal.js +13 -4
- package/dist/generator/literal.js.map +1 -1
- package/dist/generator/straightline.d.ts +81 -0
- package/dist/generator/straightline.d.ts.map +1 -1
- package/dist/generator/straightline.js +176 -0
- package/dist/generator/straightline.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { emitStraightlineModule, } from "./straightline.js";
|
|
1
|
+
import { emitDocLiteralCompact } from "./literal.js";
|
|
2
|
+
import { emitStraightlineModule, buildComponentPlan, sequentialOrder, analyzeSequentialOps, classifyExpr, isScopeFree, componentNeedsScope, } from "./straightline.js";
|
|
3
3
|
/** runtime package alias(生成コード内)。emit-go.ts と同じ `dslcontracts`。 */
|
|
4
4
|
const PKG = "dslcontracts";
|
|
5
|
+
/** scope 式(RunPlan 経路のみ)。逐次経路は進行形 scope ローカル `scope` を使う。 */
|
|
6
|
+
const SCOPE = "baseScope()";
|
|
7
|
+
/** map 本体の要素 scope 式(RunPlan 経路の map 展開用 — 逐次経路は per-map ローカル)。 */
|
|
8
|
+
const MAP_SCOPE = "mscope()";
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
10
|
+
* 生成中 module の json.Number 使用 / helper 要否の構造的トラッカー(emitModule が emit 前に reset)。
|
|
11
|
+
* 生成テキストの substring 判定(文字列リテラル内容に誤反応)はしない。
|
|
8
12
|
*/
|
|
9
|
-
const
|
|
13
|
+
const usage = {
|
|
14
|
+
json: false,
|
|
15
|
+
runPlan: false,
|
|
16
|
+
slBind: false,
|
|
17
|
+
slField: false,
|
|
18
|
+
slCat: false,
|
|
19
|
+
slEmpty: false,
|
|
20
|
+
slPart: false, // RunPlan 経路の concat closure part(実並行のみ)
|
|
21
|
+
unproduced: false,
|
|
22
|
+
};
|
|
10
23
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
24
|
+
* typed emitter(bc#47 go-typed)が base straight-line モジュールを同梱し、その上に **RunPlan-style の
|
|
25
|
+
* struct-native runner** を積む場合、runner は `sync` と RunPlan-path native helper(slRef/slConcat/slPart/
|
|
26
|
+
* slVal)を参照する。bc#75 で base は逐次 component から RunPlan-path helper を落とすため、typed 層は
|
|
27
|
+
* この flag で base に「RunPlan-path helper と sync を必ず含めよ」と要求する(typed 経路の後方互換)。
|
|
14
28
|
*/
|
|
15
|
-
|
|
29
|
+
let forceRunPlanHelpers = false;
|
|
30
|
+
export function setGoForceRunPlanHelpers(on) {
|
|
31
|
+
forceRunPlanHelpers = on;
|
|
32
|
+
}
|
|
33
|
+
function resetUsage() {
|
|
34
|
+
for (const k of Object.keys(usage))
|
|
35
|
+
usage[k] = false;
|
|
36
|
+
}
|
|
16
37
|
/** IR リテラルを単一行 Go 式として emit(primitive 被演算子用)。 */
|
|
17
38
|
function lit(node) {
|
|
18
|
-
return emitDocLiteralCompact(node, "go");
|
|
39
|
+
return emitDocLiteralCompact(node, "go", "$", usage);
|
|
19
40
|
}
|
|
20
41
|
/** args 配列(binary/unary primitive の被演算子)を個別 IR リテラルへ落として `, ` 連結。 */
|
|
21
42
|
function argList(arg) {
|
|
@@ -23,9 +44,8 @@ function argList(arg) {
|
|
|
23
44
|
}
|
|
24
45
|
/**
|
|
25
46
|
* OP_PRIM — overflow/短絡/型判定を持つ演算子 → A0 primitive 直呼びの綴じ方。
|
|
26
|
-
* 命名は go/primitives.go の `Prim*`。被演算子は expr.go の Expr 契約どおり**未評価 IR
|
|
27
|
-
*
|
|
28
|
-
* NB: ここに `ref`/`refOpt`/`concat` は無い(native 化するため — AC#2)。
|
|
47
|
+
* 命名は go/primitives.go の `Prim*`。被演算子は expr.go の Expr 契約どおり**未評価 IR リテラル**。
|
|
48
|
+
* NB: ここに `ref`/`refOpt`/`concat` は無い(native 化する — AC#2)。
|
|
29
49
|
*/
|
|
30
50
|
const OP_PRIM = {
|
|
31
51
|
int: (a, s) => `${PKG}.PrimIntLit(${lit(a)}, ${s})`,
|
|
@@ -52,25 +72,10 @@ const OP_PRIM = {
|
|
|
52
72
|
len: (a, s) => `${PKG}.PrimLen(${argList(a)}, ${s})`,
|
|
53
73
|
};
|
|
54
74
|
/**
|
|
55
|
-
* emitExpr — Expression IR ノードを「scope を受けて (Value, error) を返す」Go
|
|
56
|
-
*
|
|
57
|
-
* 単純式は **native Go helper** へ(脱解釈 — AC#2)、overflow/短絡系は A0 primitive SSoT へ、
|
|
58
|
-
* むき出しスカラは EvalExpr(同一 SSoT)へ落とす。生成される Go 式はどれも `(Value, error)` を
|
|
59
|
-
* 返すので、直線関数側は `must(<expr>)` で 1 行に畳んで scope へ束ねる。
|
|
60
|
-
* - `{ref:[...]}` → `${PKG}.slRef(<path>, scope)` (native scope/field walk)
|
|
61
|
-
* - `{refOpt:[...]}` → `${PKG}? no — slRefOpt` (native, null 伝播)
|
|
62
|
-
* - `{concat:[...]}` → `slConcat([]slPart{...}, scope)` (native strings.Builder)
|
|
63
|
-
* - overflow/短絡 → `${PKG}.PrimAdd(...)` 等 (SSoT 直呼び)
|
|
64
|
-
* - bare scalar → `${PKG}.EvalExpr(<lit>, scope)` (SSoT)
|
|
65
|
-
* slRef/slRefOpt/slConcat は **生成モジュール内の native helper**(`EvaluateExpression`/`Prim*`
|
|
66
|
-
* を呼ばない)で、pure ref/concat の生成コードには evaluate/primitive 呼びが一切現れない
|
|
67
|
-
* (AC#2 の構造的 grep 証明)。
|
|
75
|
+
* emitExpr — Expression IR ノードを「scope を受けて (Value, error) を返す」Go 式に落とす
|
|
76
|
+
* (**dynamic 式専用**の legacy 経路 — RunPlan 経路と typed emitter が流用する seam)。
|
|
68
77
|
*/
|
|
69
78
|
function emitExpr(node, scopeVar) {
|
|
70
|
-
// ── bare スカラ literal: 型判定の無い string/bool/null は native 化(脱解釈)──────
|
|
71
|
-
// これらは expr.go でも素通し(INVALID_LITERAL/range 判定を持たない)ので、native Go 値を
|
|
72
|
-
// そのまま (Value, error) へ包む。number は checked-range/型区別(int vs float)の意味論を
|
|
73
|
-
// 持つため native 化せず EvalExpr(SSoT) に委譲する(下の fall-through)。
|
|
74
79
|
if (node === null)
|
|
75
80
|
return `slVal(nil)`;
|
|
76
81
|
if (typeof node === "boolean")
|
|
@@ -82,22 +87,20 @@ function emitExpr(node, scopeVar) {
|
|
|
82
87
|
if (keys.length === 1) {
|
|
83
88
|
const op = keys[0];
|
|
84
89
|
const arg = node[op];
|
|
85
|
-
// ── 単純式: native 化(脱解釈) ─────────────────────────────────────────
|
|
86
90
|
if (op === "ref" || op === "refOpt") {
|
|
87
|
-
// path
|
|
91
|
+
// RunPlan-path: native slRef/slRefOpt(slPart 枝で定義)を使う。逐次 slFieldVE ではない。
|
|
92
|
+
usage.slPart = true;
|
|
88
93
|
return `slRef${op === "refOpt" ? "Opt" : ""}(${emitStringPath(arg)}, ${scopeVar})`;
|
|
89
94
|
}
|
|
90
95
|
if (op === "concat") {
|
|
91
|
-
|
|
96
|
+
usage.slPart = true;
|
|
92
97
|
return `slConcat([]slPart{${emitConcatParts(arg, scopeVar)}}, ${scopeVar})`;
|
|
93
98
|
}
|
|
94
|
-
// ── overflow/短絡/型判定: A0 primitive SSoT(再実装しない) ──────────────
|
|
95
99
|
const prim = OP_PRIM[op];
|
|
96
100
|
if (prim)
|
|
97
101
|
return prim(arg, scopeVar);
|
|
98
102
|
}
|
|
99
103
|
}
|
|
100
|
-
// むき出しスカラ(string/number/bool/null)等 — evaluate SSoT に委譲(同一意味論)。
|
|
101
104
|
return `${PKG}.EvalExpr(${lit(node)}, ${scopeVar})`;
|
|
102
105
|
}
|
|
103
106
|
/** ref/refOpt の path(string リテラル配列)を Go の `[]string{...}` へ。非 string は不正 IR。 */
|
|
@@ -111,19 +114,453 @@ function emitStringPath(arg) {
|
|
|
111
114
|
});
|
|
112
115
|
return `[]string{${segs.join(", ")}}`;
|
|
113
116
|
}
|
|
114
|
-
/**
|
|
115
|
-
* concat の各 part を native `slPart` へ。slPart は「self-eval なクロージャ」= func(scope) (Value, error)。
|
|
116
|
-
* part 自身が式なので `func(s *${PKG}.Obj) (${PKG}.Value, error) { return <emitExpr(part)>; }` で
|
|
117
|
-
* 再帰 native emit する(part が別の concat/ref なら native、overflow なら primitive のまま)。
|
|
118
|
-
*/
|
|
117
|
+
/** concat の各 part を native `slPart` closure へ(RunPlan 経路のみ)。 */
|
|
119
118
|
function emitConcatParts(arg, scopeVar) {
|
|
120
119
|
if (!Array.isArray(arg))
|
|
121
120
|
throw new Error("straight-line go: concat args must be an array literal");
|
|
122
|
-
// part の scope 変数は helper 引数 `s`(呼び時点の scope を受ける)。scopeVar は無視される。
|
|
123
121
|
void scopeVar;
|
|
124
122
|
return arg.map((p) => `func(s *${PKG}.Obj) (${PKG}.Value, error) { return ${emitExpr(p, "s")} }`).join(", ");
|
|
125
123
|
}
|
|
126
|
-
|
|
124
|
+
/** dynamic 式に渡す scope 式(scope-free なら空 scope 定数)。 */
|
|
125
|
+
function seqScope(node, ctx) {
|
|
126
|
+
if (isScopeFree(node)) {
|
|
127
|
+
usage.slEmpty = true;
|
|
128
|
+
return "slEmpty()";
|
|
129
|
+
}
|
|
130
|
+
if (ctx.mapAs)
|
|
131
|
+
return ctx.mapAs.mscopeVar;
|
|
132
|
+
if (!ctx.scopeVar)
|
|
133
|
+
throw new Error("straight-line go: scope-reading expr in a scopeless component (analysis bug)");
|
|
134
|
+
return ctx.scopeVar;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 静的 ref の head を配線解決した Go 式(`slVE` を返す)。ローカル直読み / input fallback /
|
|
138
|
+
* UNKNOWN_BINDING。Go は多値 spread が「唯一引数」のときしか許さないため、逐次経路の式は
|
|
139
|
+
* すべて単一の `slVE` 値({Val, Err})へ落とし、slField/slCat が合成できるようにする。
|
|
140
|
+
*/
|
|
141
|
+
function seqRefHead(head, ctx) {
|
|
142
|
+
if (ctx.mapAs && head === ctx.mapAs.as) {
|
|
143
|
+
usage.slBind = true;
|
|
144
|
+
return `slOkVE(${ctx.mapAs.elVar})`;
|
|
145
|
+
}
|
|
146
|
+
const idx = ctx.idToIndex.get(head);
|
|
147
|
+
if (idx !== undefined) {
|
|
148
|
+
const executedBefore = ctx.atOutput || ctx.pos[idx] < ctx.currentPos;
|
|
149
|
+
if (executedBefore) {
|
|
150
|
+
const meta = ctx.metas[idx];
|
|
151
|
+
const local = ctx.local.get(head);
|
|
152
|
+
usage.slBind = true;
|
|
153
|
+
if (ctx.atOutput || !(meta.canSkip || meta.alwaysSkip))
|
|
154
|
+
return `slOkVE(${local})`;
|
|
155
|
+
return `slHeadOrVE(${ctx.okFlag.get(head)}, ${local}, input, ${JSON.stringify(head)})`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
usage.slBind = true;
|
|
159
|
+
return `slBindVE(input, ${JSON.stringify(head)})`;
|
|
160
|
+
}
|
|
161
|
+
/** 分類済み StaticExpr を Go 式へ(`slVE` を返す — 逐次経路のネイティブ inline)。 */
|
|
162
|
+
function seqStatic(e, ctx) {
|
|
163
|
+
switch (e.kind) {
|
|
164
|
+
case "str":
|
|
165
|
+
usage.slBind = true;
|
|
166
|
+
return `slValVE(${JSON.stringify(e.value)})`;
|
|
167
|
+
case "bool":
|
|
168
|
+
usage.slBind = true;
|
|
169
|
+
return `slValVE(${e.value ? "true" : "false"})`;
|
|
170
|
+
case "null":
|
|
171
|
+
usage.slBind = true;
|
|
172
|
+
return `slValVE(nil)`;
|
|
173
|
+
case "ref": {
|
|
174
|
+
const head = seqRefHead(e.path[0], ctx);
|
|
175
|
+
if (e.path.length === 1)
|
|
176
|
+
return head;
|
|
177
|
+
usage.slField = true;
|
|
178
|
+
const segs = e.path.slice(1).map((p) => JSON.stringify(p)).join(", ");
|
|
179
|
+
return `slFieldVE(${JSON.stringify(e.opt ? "refOpt" : "ref")}, ${head}, []string{${segs}})`;
|
|
180
|
+
}
|
|
181
|
+
case "concat": {
|
|
182
|
+
usage.slCat = true;
|
|
183
|
+
return `slCatVE(${e.parts.map((p) => seqStatic(p, ctx)).join(", ")})`;
|
|
184
|
+
}
|
|
185
|
+
case "dynamic":
|
|
186
|
+
// primitive 経路は (Value, error) を返す — slVE2 で単一 slVE へ包む(唯一引数 spread)。
|
|
187
|
+
usage.slBind = true;
|
|
188
|
+
return `slVE2(${emitExpr(e.node, seqScope(e.node, ctx))})`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** Expression IR → Go 式(逐次経路、`slVE` を返す)。 */
|
|
192
|
+
function seqExpr(node, ctx) {
|
|
193
|
+
return seqStatic(classifyExpr(node), ctx);
|
|
194
|
+
}
|
|
195
|
+
/** component 名を Go 関数名に使える形へ(決定的。名前一意は DUPLICATE_COMPONENT で保証)。 */
|
|
196
|
+
function sanitize(name) {
|
|
197
|
+
return name.replace(/[^A-Za-z0-9_]/g, "_");
|
|
198
|
+
}
|
|
199
|
+
/** node id 群 → 衝突しない決定的ローカル名。 */
|
|
200
|
+
function allocLocals(prefix, ids) {
|
|
201
|
+
const out = new Map();
|
|
202
|
+
const taken = new Set();
|
|
203
|
+
ids.forEach((id, i) => {
|
|
204
|
+
let name = `${prefix}${sanitize(id)}`;
|
|
205
|
+
if (taken.has(name))
|
|
206
|
+
name = `${name}_${i}`;
|
|
207
|
+
taken.add(name);
|
|
208
|
+
out.set(id, name);
|
|
209
|
+
});
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
/** 未生成表現の Go 式(single → nil / connection → 空 connection Obj)。 */
|
|
213
|
+
function unproducedGo(relationKind) {
|
|
214
|
+
usage.unproduced = true;
|
|
215
|
+
return `${PKG}.UnproducedValue(${JSON.stringify(relationKind === "connection" ? "connection" : "single")})`;
|
|
216
|
+
}
|
|
217
|
+
/** policy 解釈(fail/retry → OP_FAILED、continue → skip)。plan.go interpretOutcome と code/msg 一致。 */
|
|
218
|
+
function seqPolicyThrow(outcomeVar, opId, meta, ind) {
|
|
219
|
+
if (meta.policy === "retry") {
|
|
220
|
+
return [
|
|
221
|
+
`${ind}if ${outcomeVar}.IsError {`,
|
|
222
|
+
`${ind}\treturn nil, ${PKG}.NewPlanFailure("OP_FAILED", fmt.Sprintf("operation '%s' failed under 'retry' policy (exhausted): %s", ${JSON.stringify(opId)}, ${outcomeVar}.Err))`,
|
|
223
|
+
`${ind}}`,
|
|
224
|
+
];
|
|
225
|
+
}
|
|
226
|
+
// fail(既定・未知は手前で UNKNOWN_POLICY throw 済みで到達しない)。
|
|
227
|
+
return [
|
|
228
|
+
`${ind}if ${outcomeVar}.IsError {`,
|
|
229
|
+
`${ind}\treturn nil, ${PKG}.NewPlanFailure("OP_FAILED", fmt.Sprintf("operation '%s' failed under 'fail' policy: %s", ${JSON.stringify(opId)}, ${outcomeVar}.Err))`,
|
|
230
|
+
`${ind}}`,
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* emitSeqOp — 逐次直線化された 1 op の文列。policy 検証 → skip 判定(parent 連鎖 / bindField)→
|
|
235
|
+
* 実行(componentRef/map/cond)→ policy 解釈 → 結果ローカル格納(+進行形 scope 書き足し)。
|
|
236
|
+
*/
|
|
237
|
+
function emitSeqOp(op, plan, ctx, lines) {
|
|
238
|
+
const i = op.index;
|
|
239
|
+
const meta = ctx.metas[i];
|
|
240
|
+
const o = plan.opsLiteral[i];
|
|
241
|
+
const id = o.id;
|
|
242
|
+
const local = ctx.local.get(id);
|
|
243
|
+
lines.push(`\t// ── op '${id}' (${op.kind === "cond" ? "cond" : op.kind === "map" ? `map ${op.component}` : op.component}${o.policy ? `, policy ${JSON.stringify(o.policy)}` : ""}) ──`);
|
|
244
|
+
// policy 検証は skip 判定より先(plan.go preflightOp と同順)。
|
|
245
|
+
if (typeof meta.policy === "object") {
|
|
246
|
+
lines.push(`\treturn nil, ${PKG}.NewPlanFailure("UNKNOWN_POLICY", ${JSON.stringify(`unknown policy kind: ${meta.policy.invalid}`)})`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const canSkip = meta.canSkip || meta.alwaysSkip;
|
|
250
|
+
if (canSkip) {
|
|
251
|
+
lines.push(`\tvar ${local} ${PKG}.Value = nil`, `\t${ctx.okFlag.get(id)} := false`);
|
|
252
|
+
}
|
|
253
|
+
if (meta.alwaysSkip) {
|
|
254
|
+
lines.push(`\t// parent '${o.parent}' runs later — preflight sees an unsettled parent: unconditional skip.`);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const guards = [];
|
|
258
|
+
if (o.parent !== null) {
|
|
259
|
+
const pMeta = ctx.metas[o.parent];
|
|
260
|
+
if (pMeta.canSkip || pMeta.alwaysSkip)
|
|
261
|
+
guards.push(ctx.okFlag.get(plan.opsLiteral[o.parent].id));
|
|
262
|
+
}
|
|
263
|
+
if (o.bindField !== undefined && o.parent !== null) {
|
|
264
|
+
const pLocal = ctx.local.get(plan.opsLiteral[o.parent].id);
|
|
265
|
+
const bv = `bv_${sanitize(id)}`;
|
|
266
|
+
const inner = [];
|
|
267
|
+
const ind = guards.length > 0 ? "\t\t\t" : "\t\t";
|
|
268
|
+
// parent result[bindField](parent が Obj のときのみ)。null/欠落なら skip。
|
|
269
|
+
inner.push(`${ind}${bv}Obj, ${bv}IsObj := ${pLocal}.(*${PKG}.Obj)`);
|
|
270
|
+
inner.push(`${ind}if ${bv}IsObj {`);
|
|
271
|
+
inner.push(`${ind}\t${bv}, ${bv}Present := ${bv}Obj.Get(${JSON.stringify(o.bindField)})`);
|
|
272
|
+
inner.push(`${ind}\tif ${bv}Present && ${bv} != nil {`);
|
|
273
|
+
emitSeqExec(op, plan, ctx, inner, `${ind}\t\t`);
|
|
274
|
+
inner.push(`${ind}\t}`);
|
|
275
|
+
inner.push(`${ind}}`);
|
|
276
|
+
if (guards.length > 0) {
|
|
277
|
+
lines.push(`\tif ${guards[0]} {`);
|
|
278
|
+
lines.push(...inner);
|
|
279
|
+
lines.push(`\t}`);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
lines.push(...inner);
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (guards.length > 0) {
|
|
287
|
+
lines.push(`\tif ${guards[0]} {`);
|
|
288
|
+
emitSeqExec(op, plan, ctx, lines, "\t\t");
|
|
289
|
+
lines.push(`\t}`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
emitSeqExec(op, plan, ctx, lines, "\t");
|
|
293
|
+
}
|
|
294
|
+
/** op 本体(handler 呼び + policy 解釈 + 結果格納)を indent 付きで emit。 */
|
|
295
|
+
function emitSeqExec(op, plan, ctx, lines, ind) {
|
|
296
|
+
const i = op.index;
|
|
297
|
+
const meta = ctx.metas[i];
|
|
298
|
+
const o = plan.opsLiteral[i];
|
|
299
|
+
const id = o.id;
|
|
300
|
+
const local = ctx.local.get(id);
|
|
301
|
+
const canSkip = meta.canSkip || meta.alwaysSkip;
|
|
302
|
+
// 成功時の格納(ローカル + 進行形 scope)。
|
|
303
|
+
const store = (valueExpr, target, sind) => {
|
|
304
|
+
if (canSkip) {
|
|
305
|
+
target.push(`${sind}${local} = ${valueExpr}`, `${sind}${ctx.okFlag.get(id)} = true`);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
target.push(`${sind}${local} := ${valueExpr}`, `${sind}_ = ${local}`);
|
|
309
|
+
}
|
|
310
|
+
if (ctx.scopeVar)
|
|
311
|
+
target.push(`${sind}${ctx.scopeVar}.Set(${JSON.stringify(id)}, ${local})`);
|
|
312
|
+
};
|
|
313
|
+
if (op.kind === "cond") {
|
|
314
|
+
const c = plan.component.body[i].cond;
|
|
315
|
+
const node = { cond: [c.if, c.then, c.else] };
|
|
316
|
+
const v = `v_${sanitize(id)}`;
|
|
317
|
+
lines.push(`${ind}${v}, ${v}Err := ${emitExpr(node, seqScope(node, ctx))}`);
|
|
318
|
+
lines.push(`${ind}if ${v}Err != nil {`, `${ind}\treturn nil, ${v}Err`, `${ind}}`);
|
|
319
|
+
store(v, lines, ind);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (op.kind === "map") {
|
|
323
|
+
emitSeqMap(op, plan, ctx, lines, ind, store);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// componentRef: ports 構築 → handler 呼び → policy 解釈。
|
|
327
|
+
const savedPos = ctx.currentPos;
|
|
328
|
+
const portsVar = `ports_${sanitize(id)}`;
|
|
329
|
+
lines.push(`${ind}${portsVar} := ${PKG}.NewObj()`);
|
|
330
|
+
for (const p of op.ports) {
|
|
331
|
+
const node = plan.component.body[i].ports[p.name];
|
|
332
|
+
const pv = `pv_${sanitize(id)}_${p.name.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
333
|
+
lines.push(`${ind}${pv} := ${seqExpr(node, ctx)}`);
|
|
334
|
+
lines.push(`${ind}if ${pv}.Err != nil {`, `${ind}\treturn nil, ${pv}.Err`, `${ind}}`);
|
|
335
|
+
lines.push(`${ind}${portsVar}.Set(${JSON.stringify(p.name)}, ${pv}.Val)`);
|
|
336
|
+
}
|
|
337
|
+
ctx.currentPos = savedPos;
|
|
338
|
+
const oc = `o_${sanitize(id)}`;
|
|
339
|
+
lines.push(`${ind}${oc}, ${oc}Resolved := ${PKG}.ExecHandler(handlers, ${JSON.stringify(id)}, ${JSON.stringify(op.component)}, ${portsVar}, nil)`);
|
|
340
|
+
lines.push(`${ind}if !${oc}Resolved {`, `${ind}\treturn nil, ${PKG}.NewBehaviorFailure("UNKNOWN_COMPONENT", "component '${op.component}' has no handler (fail-closed)")`, `${ind}}`);
|
|
341
|
+
if (meta.policy === "continue") {
|
|
342
|
+
lines.push(`${ind}if !${oc}.IsError {`);
|
|
343
|
+
store(`${oc}.Ok`, lines, `${ind}\t`);
|
|
344
|
+
lines.push(`${ind}}`);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
lines.push(...seqPolicyThrow(oc, id, meta, ind));
|
|
348
|
+
store(`${oc}.Ok`, lines, ind);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/** emitSeqMap — map ノードの逐次直線展開(RunPlan 経路と観測等価。per-expr scope snapshot 無し)。 */
|
|
352
|
+
function emitSeqMap(op, plan, ctx, lines, ind, store) {
|
|
353
|
+
const i = op.index;
|
|
354
|
+
const meta = ctx.metas[i];
|
|
355
|
+
const m = plan.component.body[i].map;
|
|
356
|
+
const id = op.id;
|
|
357
|
+
const s = sanitize(id);
|
|
358
|
+
const comp = JSON.stringify(op.component);
|
|
359
|
+
const asKey = JSON.stringify(m.as);
|
|
360
|
+
const guardIR = m.when === undefined ? undefined : { cond: [m.when, true, false] };
|
|
361
|
+
const mapNeedsScope = (guardIR !== undefined && !isScopeFree(guardIR)) ||
|
|
362
|
+
Object.keys(m.ports).some((k) => {
|
|
363
|
+
const cls = classifyExpr(m.ports[k]);
|
|
364
|
+
const needs = (e) => e.kind === "dynamic" ? !isScopeFree(e.node) : e.kind === "concat" ? e.parts.some(needs) : false;
|
|
365
|
+
return needs(cls);
|
|
366
|
+
});
|
|
367
|
+
const baseScopeExpr = ctx.scopeVar;
|
|
368
|
+
const elVar = `el_${s}`;
|
|
369
|
+
const mscopeVar = `mscope_${s}`;
|
|
370
|
+
const overVar = `over_${s}`;
|
|
371
|
+
const keptVar = `kept_${s}`;
|
|
372
|
+
const collected = `collected_${s}`;
|
|
373
|
+
const contFlag = meta.policy === "continue";
|
|
374
|
+
const skipVar = `skip_${s}`;
|
|
375
|
+
const mapCtx = { ...ctx, mapAs: { as: m.as, elVar, mscopeVar } };
|
|
376
|
+
lines.push(`${ind}${overVar}VE := ${seqExpr(m.over, ctx)}`);
|
|
377
|
+
lines.push(`${ind}if ${overVar}VE.Err != nil {`, `${ind}\treturn nil, ${overVar}VE.Err`, `${ind}}`);
|
|
378
|
+
lines.push(`${ind}${overVar}, ${overVar}IsArr := ${overVar}VE.Val.([]${PKG}.Value)`);
|
|
379
|
+
lines.push(`${ind}if !${overVar}IsArr {`, `${ind}\treturn nil, ${PKG}.NewBehaviorFailure("MAP_OVER_NOT_ARRAY", "map '${id}': 'over' did not evaluate to an array")`, `${ind}}`);
|
|
380
|
+
lines.push(`${ind}${keptVar} := make([]int, 0, len(${overVar}))`);
|
|
381
|
+
if (contFlag)
|
|
382
|
+
lines.push(`${ind}${skipVar} := false`);
|
|
383
|
+
const mscopeLines = (ind2) => {
|
|
384
|
+
if (!mapNeedsScope)
|
|
385
|
+
return [];
|
|
386
|
+
return [
|
|
387
|
+
`${ind2}${mscopeVar} := ${PKG}.NewObj()`,
|
|
388
|
+
...(baseScopeExpr
|
|
389
|
+
? [`${ind2}for _, ${mscopeVar}k := range ${baseScopeExpr}.Keys {`, `${ind2}\t${mscopeVar}.Set(${mscopeVar}k, ${baseScopeExpr}.Vals[${mscopeVar}k])`, `${ind2}}`]
|
|
390
|
+
: [`${ind2}for _, ${mscopeVar}k := range input.Keys {`, `${ind2}\t${mscopeVar}.Set(${mscopeVar}k, input.Vals[${mscopeVar}k])`, `${ind2}}`]),
|
|
391
|
+
`${ind2}${mscopeVar}.Set(${asKey}, ${elVar})`,
|
|
392
|
+
];
|
|
393
|
+
};
|
|
394
|
+
const guardLines = (ind2) => {
|
|
395
|
+
if (guardIR === undefined)
|
|
396
|
+
return [];
|
|
397
|
+
const g = `g_${s}`;
|
|
398
|
+
return [
|
|
399
|
+
`${ind2}${g}, ${g}Err := ${emitExpr(guardIR, isScopeFree(guardIR) ? "slEmpty()" : mscopeVar)}`,
|
|
400
|
+
`${ind2}if ${g}Err != nil {`,
|
|
401
|
+
`${ind2}\treturn nil, ${g}Err`,
|
|
402
|
+
`${ind2}}`,
|
|
403
|
+
`${ind2}if ${g} != true {`,
|
|
404
|
+
`${ind2}\tcontinue`,
|
|
405
|
+
`${ind2}}`,
|
|
406
|
+
];
|
|
407
|
+
};
|
|
408
|
+
const portLines = (ind2, portsVar) => {
|
|
409
|
+
const out = [`${ind2}${portsVar} := ${PKG}.NewObj()`];
|
|
410
|
+
for (const p of op.ports) {
|
|
411
|
+
const pv = `pv_${s}_${p.name.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
412
|
+
out.push(`${ind2}${pv} := ${seqExpr(m.ports[p.name], mapCtx)}`);
|
|
413
|
+
out.push(`${ind2}if ${pv}.Err != nil {`, `${ind2}\treturn nil, ${pv}.Err`, `${ind2}}`);
|
|
414
|
+
out.push(`${ind2}${portsVar}.Set(${JSON.stringify(p.name)}, ${pv}.Val)`);
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
};
|
|
418
|
+
if (op.batched) {
|
|
419
|
+
const items = `items_${s}`;
|
|
420
|
+
lines.push(`${ind}${items} := make([]${PKG}.Value, 0, len(${overVar}))`);
|
|
421
|
+
lines.push(`${ind}for ${elVar}Idx, ${elVar} := range ${overVar} {`);
|
|
422
|
+
lines.push(...mscopeLines(`${ind}\t`), ...guardLines(`${ind}\t`), ...portLines(`${ind}\t`, `ports_${s}`));
|
|
423
|
+
lines.push(`${ind}\t${items} = append(${items}, ports_${s})`, `${ind}\t${keptVar} = append(${keptVar}, ${elVar}Idx)`, `${ind}}`);
|
|
424
|
+
lines.push(`${ind}var ${collected} []${PKG}.Value`);
|
|
425
|
+
lines.push(`${ind}if len(${items}) == 0 {`, `${ind}\t${collected} = []${PKG}.Value{}`, `${ind}} else {`);
|
|
426
|
+
const bp = `bp_${s}`;
|
|
427
|
+
lines.push(`${ind}\t${bp} := ${PKG}.NewObj()`, `${ind}\t${bp}.Set("items", ${items})`);
|
|
428
|
+
const oc = `mo_${s}`;
|
|
429
|
+
lines.push(`${ind}\t${oc}, ${oc}Resolved := ${PKG}.ExecHandler(handlers, ${JSON.stringify(id)}, ${comp}, ${bp}, nil)`);
|
|
430
|
+
lines.push(`${ind}\tif !${oc}Resolved {`, `${ind}\t\treturn nil, ${PKG}.NewBehaviorFailure("UNKNOWN_COMPONENT", "component '${op.component}' has no handler (fail-closed)")`, `${ind}\t}`);
|
|
431
|
+
if (contFlag) {
|
|
432
|
+
lines.push(`${ind}\tif ${oc}.IsError {`, `${ind}\t\t${skipVar} = true`, `${ind}\t} else {`);
|
|
433
|
+
lines.push(...batchOk(oc, collected, items, id, s, `${ind}\t\t`));
|
|
434
|
+
lines.push(`${ind}\t}`);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
lines.push(...seqPolicyThrow(oc, id, meta, `${ind}\t`));
|
|
438
|
+
lines.push(...batchOk(oc, collected, items, id, s, `${ind}\t`));
|
|
439
|
+
}
|
|
440
|
+
lines.push(`${ind}}`);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
lines.push(`${ind}${collected} := make([]${PKG}.Value, 0, len(${overVar}))`);
|
|
444
|
+
lines.push(`${ind}for ${elVar}Idx, ${elVar} := range ${overVar} {`);
|
|
445
|
+
lines.push(...mscopeLines(`${ind}\t`), ...guardLines(`${ind}\t`), ...portLines(`${ind}\t`, `ports_${s}`));
|
|
446
|
+
const oc = `mo_${s}`;
|
|
447
|
+
lines.push(`${ind}\t${oc}, ${oc}Resolved := ${PKG}.ExecHandler(handlers, ${JSON.stringify(id)}, ${comp}, ports_${s}, ${elVar})`);
|
|
448
|
+
lines.push(`${ind}\tif !${oc}Resolved {`, `${ind}\t\treturn nil, ${PKG}.NewBehaviorFailure("UNKNOWN_COMPONENT", "component '${op.component}' has no handler (fail-closed)")`, `${ind}\t}`);
|
|
449
|
+
if (contFlag) {
|
|
450
|
+
lines.push(`${ind}\tif ${oc}.IsError {`, `${ind}\t\t${skipVar} = true`, `${ind}\t\tbreak`, `${ind}\t}`);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
lines.push(...seqPolicyThrow(oc, id, meta, `${ind}\t`));
|
|
454
|
+
}
|
|
455
|
+
lines.push(`${ind}\t${collected} = append(${collected}, ${oc}.Ok)`, `${ind}\t${keptVar} = append(${keptVar}, ${elVar}Idx)`, `${ind}}`);
|
|
456
|
+
}
|
|
457
|
+
// finish(into zip-attach or collected)— continue はエラー時 skip して未生成。
|
|
458
|
+
const finish = [];
|
|
459
|
+
const fInd = contFlag ? `${ind}\t` : ind;
|
|
460
|
+
if (m.into === undefined) {
|
|
461
|
+
store(collected, finish, fInd);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
const aug = `aug_${s}`;
|
|
465
|
+
finish.push(`${fInd}${aug} := make([]${PKG}.Value, 0, len(${overVar}))`);
|
|
466
|
+
finish.push(`${fInd}mk_${s} := 0`);
|
|
467
|
+
finish.push(`${fInd}for mj_${s}, oel_${s} := range ${overVar} {`);
|
|
468
|
+
finish.push(`${fInd}\tif mk_${s} < len(${keptVar}) && ${keptVar}[mk_${s}] == mj_${s} {`);
|
|
469
|
+
finish.push(`${fInd}\t\toelObj_${s}, isObj_${s} := oel_${s}.(*${PKG}.Obj)`);
|
|
470
|
+
finish.push(`${fInd}\t\tif !isObj_${s} {`, `${fInd}\t\t\treturn nil, ${PKG}.NewBehaviorFailure("MAP_INTO_ELEMENT_NOT_OBJECT", fmt.Sprintf("map '${id}': 'into' requires object elements (element %d is not an object)", mj_${s}))`, `${fInd}\t\t}`);
|
|
471
|
+
finish.push(`${fInd}\t\tcp_${s} := ${PKG}.NewObj()`);
|
|
472
|
+
finish.push(`${fInd}\t\tfor _, fk_${s} := range oelObj_${s}.Keys {`, `${fInd}\t\t\tcp_${s}.Set(fk_${s}, oelObj_${s}.Vals[fk_${s}])`, `${fInd}\t\t}`);
|
|
473
|
+
finish.push(`${fInd}\t\tcp_${s}.Set(${JSON.stringify(m.into)}, ${collected}[mk_${s}])`);
|
|
474
|
+
finish.push(`${fInd}\t\t${aug} = append(${aug}, cp_${s})`, `${fInd}\t\tmk_${s}++`);
|
|
475
|
+
finish.push(`${fInd}\t} else {`, `${fInd}\t\t${aug} = append(${aug}, oel_${s})`, `${fInd}\t}`);
|
|
476
|
+
finish.push(`${fInd}}`);
|
|
477
|
+
store(aug, finish, fInd);
|
|
478
|
+
}
|
|
479
|
+
if (contFlag) {
|
|
480
|
+
lines.push(`${ind}if !${skipVar} {`);
|
|
481
|
+
lines.push(...finish);
|
|
482
|
+
lines.push(`${ind}}`);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
lines.push(...finish);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/** batched handler の ok を collected へ(長さ整合検査つき)。 */
|
|
489
|
+
function batchOk(oc, collected, items, id, s, ind) {
|
|
490
|
+
return [
|
|
491
|
+
`${ind}r_batch_${s}, isArr_${s} := ${oc}.Ok.([]${PKG}.Value)`,
|
|
492
|
+
`${ind}if !isArr_${s} || len(r_batch_${s}) != len(${items}) {`,
|
|
493
|
+
`${ind}\treturn nil, ${PKG}.NewBehaviorFailure("MAP_BATCH_RESULT_MISMATCH", fmt.Sprintf("map '${id}': batched handler must return a list aligned to items (want %d)", len(${items})))`,
|
|
494
|
+
`${ind}}`,
|
|
495
|
+
`${ind}${collected} = r_batch_${s}`,
|
|
496
|
+
];
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* emitSequentialGoFn — 逐次 plan の component を素の文列として綴じる(bc#75 の核)。
|
|
500
|
+
* RunPlan / OpSpec テーブル / baseScope snapshot / exec closure / per-op ID 探索は無い。
|
|
501
|
+
*/
|
|
502
|
+
function emitSequentialGoFn(plan, order) {
|
|
503
|
+
const metas = analyzeSequentialOps(plan.opsLiteral, order);
|
|
504
|
+
const pos = new Array(plan.opsLiteral.length);
|
|
505
|
+
order.forEach((opIdx, k) => (pos[opIdx] = k));
|
|
506
|
+
const idToIndex = new Map();
|
|
507
|
+
plan.opsLiteral.forEach((o, i) => idToIndex.set(o.id, i));
|
|
508
|
+
const needScope = componentNeedsScope(plan);
|
|
509
|
+
const ids = plan.opsLiteral.map((o) => o.id);
|
|
510
|
+
const local = allocLocals("r_", ids);
|
|
511
|
+
const okFlag = allocLocals("ok_", ids);
|
|
512
|
+
const ctx = {
|
|
513
|
+
pos,
|
|
514
|
+
idToIndex,
|
|
515
|
+
metas,
|
|
516
|
+
local,
|
|
517
|
+
okFlag,
|
|
518
|
+
scopeVar: needScope ? "scope" : null,
|
|
519
|
+
currentPos: 0,
|
|
520
|
+
atOutput: false,
|
|
521
|
+
};
|
|
522
|
+
const lines = [];
|
|
523
|
+
lines.push(`func run_${sanitize(plan.name)}(handlers ${PKG}.ComponentExec, input *${PKG}.Obj) (${PKG}.Value, error) {`);
|
|
524
|
+
lines.push(`\tif input == nil {`, `\t\tinput = ${PKG}.NewObj()`, `\t}`);
|
|
525
|
+
if (needScope) {
|
|
526
|
+
// 進行形 scope: input のコピー(bind 側で input は private コピー済み)へノード結果を書き足す。
|
|
527
|
+
lines.push(`\tscope := ${PKG}.NewObj()`);
|
|
528
|
+
lines.push(`\tfor _, k := range input.Keys {`, `\t\tscope.Set(k, input.Vals[k])`, `\t}`);
|
|
529
|
+
}
|
|
530
|
+
let unreachable = false;
|
|
531
|
+
for (const k of order.keys()) {
|
|
532
|
+
const opIdx = order[k];
|
|
533
|
+
ctx.currentPos = k;
|
|
534
|
+
if (unreachable)
|
|
535
|
+
break;
|
|
536
|
+
emitSeqOp(plan.ops[opIdx], plan, ctx, lines);
|
|
537
|
+
if (typeof metas[opIdx].policy === "object")
|
|
538
|
+
unreachable = true;
|
|
539
|
+
}
|
|
540
|
+
if (!unreachable) {
|
|
541
|
+
ctx.atOutput = true;
|
|
542
|
+
for (const i of order) {
|
|
543
|
+
const meta = metas[i];
|
|
544
|
+
if (!(meta.canSkip || meta.alwaysSkip))
|
|
545
|
+
continue;
|
|
546
|
+
const id = plan.opsLiteral[i].id;
|
|
547
|
+
lines.push(`\tif !${okFlag.get(id)} {`);
|
|
548
|
+
lines.push(`\t\t${local.get(id)} = ${unproducedGo(plan.opsLiteral[i].relationKind)}`);
|
|
549
|
+
if (needScope)
|
|
550
|
+
lines.push(`\t\tscope.Set(${JSON.stringify(id)}, ${local.get(id)})`);
|
|
551
|
+
lines.push(`\t}`);
|
|
552
|
+
}
|
|
553
|
+
const outNode = plan.component.output;
|
|
554
|
+
lines.push(`\toutVE := ${seqExpr(outNode, ctx)}`);
|
|
555
|
+
lines.push(`\tif outVE.Err != nil {`, `\t\treturn nil, outVE.Err`, `\t}`);
|
|
556
|
+
lines.push(`\treturn outVE.Val, nil`);
|
|
557
|
+
}
|
|
558
|
+
lines.push(`}`);
|
|
559
|
+
return lines.join("\n");
|
|
560
|
+
}
|
|
561
|
+
// ══════════════════════════════════════════════════════════════════════════════════
|
|
562
|
+
// RunPlan 経路(実並行 plan のみ — bounded 並行の意味論 SSoT を保持)
|
|
563
|
+
// ══════════════════════════════════════════════════════════════════════════════════
|
|
127
564
|
/** ops リテラル(RunPlan に渡す []OpSpec の Go リテラル)を綴じる。 */
|
|
128
565
|
function emitOpsLiteral(plan) {
|
|
129
566
|
const rows = plan.opsLiteral.map((o) => {
|
|
@@ -143,7 +580,6 @@ function emitExecArm(op) {
|
|
|
143
580
|
if (op.kind === "map")
|
|
144
581
|
return emitMapArm(op);
|
|
145
582
|
if (op.kind === "cond") {
|
|
146
|
-
// cond は純 Expression。scope を渡して 1 式評価(handler は呼ばない)。式は (Value,error)。
|
|
147
583
|
return `\t\tif i == ${op.index} {
|
|
148
584
|
\t\t\tv, err := ${op.expr}
|
|
149
585
|
\t\t\tif err != nil {
|
|
@@ -152,7 +588,6 @@ function emitExecArm(op) {
|
|
|
152
588
|
\t\t\treturn ${PKG}.OkOutcome(v)
|
|
153
589
|
\t\t}`;
|
|
154
590
|
}
|
|
155
|
-
// componentRef: 各 port を名前付き native/primitive 呼びで評価 → handler 注入呼び。
|
|
156
591
|
const portEvals = op.ports
|
|
157
592
|
.map((p) => `\t\t\t${goPortVar(p.name)}, err := ${p.expr}
|
|
158
593
|
\t\t\tif err != nil {
|
|
@@ -170,11 +605,11 @@ function emitExecArm(op) {
|
|
|
170
605
|
\t\t\treturn outcome
|
|
171
606
|
\t\t}`;
|
|
172
607
|
}
|
|
173
|
-
/** port ローカル変数名(Go
|
|
608
|
+
/** port ローカル変数名(Go 識別子として安全)。 */
|
|
174
609
|
function goPortVar(name) {
|
|
175
610
|
return `pv_${name.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
176
611
|
}
|
|
177
|
-
/** map arm の要素 ports
|
|
612
|
+
/** map arm の要素 ports 構築(RunPlan 経路)。 */
|
|
178
613
|
function emitMapPortEvals(op) {
|
|
179
614
|
if (op.ports.length === 0)
|
|
180
615
|
return `\t\t\t\tports := ${PKG}.NewObj()`;
|
|
@@ -187,13 +622,7 @@ function emitMapPortEvals(op) {
|
|
|
187
622
|
.join("\n");
|
|
188
623
|
return `\t\t\t\tports := ${PKG}.NewObj()\n${lines}`;
|
|
189
624
|
}
|
|
190
|
-
/**
|
|
191
|
-
* emitMapArm — map ノードの直線展開(要素反復・guard skip・per-element/batched handler・into
|
|
192
|
-
* zip-attach)。RunBehavior の map 分岐(behavior.go)を生成 Go へ直線展開する。op-log 等価のため
|
|
193
|
-
* ExecHandler は RunBehavior と同じ ctx(非 batched は bound=要素値、batched は nil)・同じ ports で
|
|
194
|
-
* 呼ぶ。guard は PrimCond(strict-bool SSoT)、into は inline zip-attach(MAP_INTO_ELEMENT_NOT_OBJECT
|
|
195
|
-
* fail-closed で Failure code 一致)。要素反復は宣言どおり逐次。
|
|
196
|
-
*/
|
|
625
|
+
/** emitMapArm — map ノードの直線展開(RunPlan 経路)。 */
|
|
197
626
|
function emitMapArm(op) {
|
|
198
627
|
const comp = JSON.stringify(op.component);
|
|
199
628
|
const nodeId = JSON.stringify(op.id);
|
|
@@ -293,16 +722,27 @@ ${body}
|
|
|
293
722
|
\t\t}`;
|
|
294
723
|
}
|
|
295
724
|
/**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
725
|
+
* emitPlanDrivenGoFn — 実並行 plan(複数 op の stage)の component を RunPlan primitive で綴じる。
|
|
726
|
+
* bounded 並行・Skip 伝播・Policy Kind の意味論 SSoT は RunPlan が保持する。per-op exec は index で
|
|
727
|
+
* 確定した分岐(`for k := range ops` 線形 ID 探索は idx 解決に一度だけ・switch はハードコード)。
|
|
299
728
|
*/
|
|
300
|
-
function
|
|
729
|
+
function emitPlanDrivenGoFn(plan) {
|
|
730
|
+
usage.runPlan = true;
|
|
731
|
+
usage.unproduced = true;
|
|
301
732
|
const arms = plan.ops.map(emitExecArm).join("\n");
|
|
302
733
|
const planLiteral = plan.component.plan
|
|
303
734
|
? emitBehaviorPlanLiteral(plan.component.plan)
|
|
304
735
|
: `(*${PKG}.ExecutionPlanSpec)(nil)`;
|
|
305
736
|
const output = emitExpr(plan.component.output, SCOPE);
|
|
737
|
+
// arms は buildComponentPlan 由来の op.expr(emitModule の reset より前に生成)。json/slField/slPart/
|
|
738
|
+
// slConcat の使用は実 emit テキストで確定させる(arms は我々の生成した go 式で、user 由来 string は
|
|
739
|
+
// JObjOf/リテラルの内側にあるため token 出現=実使用で安全)。
|
|
740
|
+
if (/json\.Number\(|json\.RawMessage/.test(arms))
|
|
741
|
+
usage.json = true;
|
|
742
|
+
// slRef/slRefOpt/slConcat/slPart は RunPlan-path helper(usage.slPart 枝で定義)。逐次 slFieldVE
|
|
743
|
+
// (usage.slField 枝・slVE 型必須)ではないので slPart を立てる。
|
|
744
|
+
if (/slRef(Opt)?\(|slConcat\(\[\]slPart\{/.test(arms))
|
|
745
|
+
usage.slPart = true;
|
|
306
746
|
const relationKinds = plan.opsLiteral.map((o) => (o.relationKind === "connection" ? "connection" : "single"));
|
|
307
747
|
const rkLiteral = `[]string{${relationKinds.map((r) => JSON.stringify(r)).join(", ")}}`;
|
|
308
748
|
return `func run_${sanitize(plan.name)}(handlers ${PKG}.ComponentExec, input *${PKG}.Obj) (${PKG}.Value, error) {
|
|
@@ -312,7 +752,8 @@ function emitComponentFn(plan) {
|
|
|
312
752
|
\t}
|
|
313
753
|
\tresults := ${PKG}.NewObj()
|
|
314
754
|
\tvar resultsMu sync.Mutex
|
|
315
|
-
\t// baseScope = input params + accumulated ok results (snapshot at call time)
|
|
755
|
+
\t// baseScope = input params + accumulated ok results (snapshot at call time) — REAL
|
|
756
|
+
\t// concurrency needs a deterministic scope snapshot per exec (bc#75: only this path).
|
|
316
757
|
\tbaseScope := func() *${PKG}.Obj {
|
|
317
758
|
\t\ts := ${PKG}.NewObj()
|
|
318
759
|
\t\tfor _, k := range input.Keys {
|
|
@@ -326,28 +767,25 @@ function emitComponentFn(plan) {
|
|
|
326
767
|
\t\treturn s
|
|
327
768
|
\t}
|
|
328
769
|
\trelationKinds := ${rkLiteral}
|
|
329
|
-
\t// exec-internal failures (BehaviorFailure/ExprFailure) cannot unwind through the
|
|
330
|
-
\t// RunPlan ExecOutcome seam, so each op records its own error and returns a synthetic
|
|
331
|
-
\t// aborted ErrOutcome; sequential precedence is restored after RunPlan (bc#23).
|
|
332
770
|
\texecErrs := make([]error, len(ops))
|
|
333
771
|
\tabort := func(idx int, err error) ${PKG}.ExecOutcome {
|
|
334
772
|
\t\texecErrs[idx] = err
|
|
335
773
|
\t\treturn ${PKG}.ErrOutcome("aborted")
|
|
336
774
|
\t}
|
|
337
|
-
\
|
|
338
|
-
\t
|
|
339
|
-
\
|
|
340
|
-
\t\t\tif ops[k].ID == op.ID {
|
|
341
|
-
\t\t\t\tidx = k
|
|
342
|
-
\t\t\t\tbreak
|
|
343
|
-
\t\t\t}
|
|
344
|
-
\t\t}
|
|
775
|
+
\t// per-op exec は op の index(RunPlan が渡す OpSpec の位置)で確定する switch。opIndex は
|
|
776
|
+
\t// ops リテラルと 1:1 なので線形 ID 探索は不要(RunPlan が visit 順に呼ぶ i を使う)。
|
|
777
|
+
\texecByIndex := func(idx int) ${PKG}.ExecOutcome {
|
|
345
778
|
\t\ti := idx
|
|
346
779
|
${arms}
|
|
347
|
-
\t\treturn abort(idx, ${PKG}.NewBehaviorFailure("UNKNOWN_NODE_KIND", "op
|
|
780
|
+
\t\treturn abort(idx, ${PKG}.NewBehaviorFailure("UNKNOWN_NODE_KIND", "op has no generated exec arm (fail-closed)"))
|
|
781
|
+
\t}
|
|
782
|
+
\topIndex := make(map[string]int, len(ops))
|
|
783
|
+
\tfor k := range ops {
|
|
784
|
+
\t\topIndex[ops[k].ID] = k
|
|
348
785
|
\t}
|
|
349
786
|
\twrappedExec := func(op ${PKG}.OpSpec, bound ${PKG}.Value) ${PKG}.ExecOutcome {
|
|
350
|
-
\t\
|
|
787
|
+
\t\t_ = bound
|
|
788
|
+
\t\toutcome := execByIndex(opIndex[op.ID])
|
|
351
789
|
\t\tif !outcome.IsError {
|
|
352
790
|
\t\t\tresultsMu.Lock()
|
|
353
791
|
\t\t\tresults.Set(op.ID, outcome.Ok)
|
|
@@ -363,17 +801,11 @@ ${arms}
|
|
|
363
801
|
\tif err != nil {
|
|
364
802
|
\t\treturn nil, err
|
|
365
803
|
\t}
|
|
366
|
-
\t// Skipped nodes contribute their unproduced representation to the scope.
|
|
367
804
|
\tfor _, id := range run.Skipped {
|
|
368
805
|
\t\tif _, present := results.Get(id); present {
|
|
369
806
|
\t\t\tcontinue
|
|
370
807
|
\t\t}
|
|
371
|
-
\t\
|
|
372
|
-
\t\t\tif ops[k].ID == id {
|
|
373
|
-
\t\t\t\tresults.Set(id, ${PKG}.UnproducedValue(relationKinds[k]))
|
|
374
|
-
\t\t\t\tbreak
|
|
375
|
-
\t\t\t}
|
|
376
|
-
\t\t}
|
|
808
|
+
\t\tresults.Set(id, ${PKG}.UnproducedValue(relationKinds[opIndex[id]]))
|
|
377
809
|
\t}
|
|
378
810
|
\treturn ${output}
|
|
379
811
|
}`;
|
|
@@ -386,130 +818,116 @@ function emitBehaviorPlanLiteral(plan) {
|
|
|
386
818
|
const conc = p.concurrency ?? 1;
|
|
387
819
|
return `&${PKG}.ExecutionPlanSpec{Groups: ${groupsLit}, Concurrency: ${conc}}`;
|
|
388
820
|
}
|
|
389
|
-
/** component 名を Go 関数名に使える形へ(決定的。名前一意は DUPLICATE_COMPONENT で保証)。 */
|
|
390
|
-
function sanitize(name) {
|
|
391
|
-
return name.replace(/[^A-Za-z0-9_]/g, "_");
|
|
392
|
-
}
|
|
393
821
|
function emitComponent(plan, _ctx) {
|
|
394
|
-
|
|
822
|
+
const order = sequentialOrder(plan.component);
|
|
823
|
+
if (order !== null)
|
|
824
|
+
return emitSequentialGoFn(plan, order);
|
|
825
|
+
return emitPlanDrivenGoFn(plan);
|
|
395
826
|
}
|
|
396
827
|
function emitModule(plans, ctx) {
|
|
397
828
|
const { fingerprint, specVersions, componentNames, runtimeImport } = ctx;
|
|
398
829
|
const names = componentNames.map((n) => JSON.stringify(n)).join(", ");
|
|
399
|
-
|
|
830
|
+
// buildComponentPlan は逐次経路が使わない op.expr を先に emit するため usage を過大に立てる。
|
|
831
|
+
// 実 emit 直前で reset し、emitComponent 内の呼び出しだけが import/helper 要否を立てるようにする。
|
|
832
|
+
resetUsage();
|
|
400
833
|
const fns = plans.map((p) => emitComponent(p, ctx)).join("\n\n");
|
|
834
|
+
if (forceRunPlanHelpers) {
|
|
835
|
+
// typed 層(go-typed)の struct-native runner は RunPlan-path helper(slRef/slRefOpt/slConcat/slPart/
|
|
836
|
+
// slVal)と sync を参照する。それらは usage.slPart 枝(slPart type + slConcat + slRef/slRefOpt/
|
|
837
|
+
// slRefWalk)に集約されている。逐次 slVE 系(slField=slFieldVE 等)は typed runner が使わないので
|
|
838
|
+
// 立てない(誤って slFieldVE を emit すると slVE 型が要る)。
|
|
839
|
+
usage.slPart = true;
|
|
840
|
+
usage.runPlan = true; // sync import
|
|
841
|
+
}
|
|
842
|
+
const usesJson = usage.json;
|
|
401
843
|
const dispatchArms = plans
|
|
402
844
|
.map((p) => `\tif name == ${JSON.stringify(p.name)} {\n\t\treturn run_${sanitize(p.name)}(handlers, input)\n\t}`)
|
|
403
845
|
.join("\n");
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
//
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
import (
|
|
420
|
-
\t"encoding/json"
|
|
421
|
-
\t"fmt"
|
|
422
|
-
\t"strings"
|
|
423
|
-
\t"sync"
|
|
424
|
-
|
|
425
|
-
\t${PKG} ${JSON.stringify(runtimeImport)}
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
// ExpectedSpecVersions are the spec versions baked at generation time
|
|
429
|
-
// (fail-closed constant comparison at init).
|
|
430
|
-
var ExpectedSpecVersions = map[string]int64{"behavior": ${specVersions.behavior}, "expression": ${specVersions.expression}, "plan": ${specVersions.plan}}
|
|
431
|
-
|
|
432
|
-
// IRFingerprint is the FNV-1a 64 fingerprint of the source portable IR
|
|
433
|
-
// (CanonicalJSON discipline, #208).
|
|
434
|
-
const IRFingerprint = ${JSON.stringify(fingerprint)}
|
|
435
|
-
|
|
436
|
-
// ComponentNames are the components exposed by Bind(), in IR declaration order.
|
|
437
|
-
var ComponentNames = []string{${names}}
|
|
438
|
-
|
|
439
|
-
// IR is the portable component-graph IR, embedded for the init-time fingerprint
|
|
440
|
-
// self-check only (the straight-line functions do NOT interpret it at run time).
|
|
441
|
-
var IR ${PKG}.JNode = ${irLiteral}
|
|
442
|
-
|
|
443
|
-
// init — load-time fail-closed checks (#208 prepared-artifact discipline).
|
|
444
|
-
func init() {
|
|
445
|
-
\tfor k, want := range ExpectedSpecVersions {
|
|
446
|
-
\t\tif got := ${PKG}.SpecVersions[k]; got != want {
|
|
447
|
-
\t\t\tpanic(fmt.Sprintf("behavior-contracts generated module: spec-version skew for '%s' (generated=%d, runtime=%d) — regenerate against this runtime (fail-closed)", k, want, got))
|
|
448
|
-
\t\t}
|
|
449
|
-
\t}
|
|
450
|
-
\tgot, err := ${PKG}.FingerprintComponentGraph(IR)
|
|
451
|
-
\tif err != nil {
|
|
452
|
-
\t\tpanic(fmt.Sprintf("behavior-contracts generated module: fingerprint recompute failed: %v (fail-closed)", err))
|
|
453
|
-
\t}
|
|
454
|
-
\tif got != IRFingerprint {
|
|
455
|
-
\t\tpanic(fmt.Sprintf("behavior-contracts generated module: IR fingerprint mismatch (baked=%s, embedded literal=%s) — the generated code was modified or corrupted (fail-closed)", IRFingerprint, got))
|
|
456
|
-
\t}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// ── native simple-expression helpers (Phase B de-interpretation, bc#38) ────────
|
|
460
|
-
// These are NATIVE Go — they never call EvaluateExpression / Prim*. They reproduce
|
|
461
|
-
// expr.go's ref/refOpt/concat semantics (same Failure codes) so the straight-line
|
|
462
|
-
// path is observationally equivalent to RunBehavior for simple ports while removing
|
|
463
|
-
// the expression-tree interpretation.
|
|
464
|
-
|
|
465
|
-
// slPart is a self-evaluating concat part (recursively native / primitive).
|
|
466
|
-
type slPart = func(scope *${PKG}.Obj) (${PKG}.Value, error)
|
|
467
|
-
|
|
468
|
-
// slVal wraps a native Go literal (string/bool/null) as a (Value, error) result —
|
|
469
|
-
// bare scalars carry no INVALID_LITERAL/range semantics, so no evaluate is needed.
|
|
470
|
-
func slVal(v ${PKG}.Value) (${PKG}.Value, error) { return v, nil }
|
|
471
|
-
|
|
472
|
-
// slRef walks a non-empty string path over scope (native; expr.go ref semantics).
|
|
473
|
-
func slRef(path []string, scope *${PKG}.Obj) (${PKG}.Value, error) {
|
|
474
|
-
\treturn slRefWalk("ref", path, scope)
|
|
846
|
+
// 生成 helper(構造的トラッキングで必要なものだけ emit)。
|
|
847
|
+
const helpers = [];
|
|
848
|
+
helpers.push(`// slVal wraps a native Go literal as a (Value, error) result (dynamic/RunPlan path).
|
|
849
|
+
func slVal(v ${PKG}.Value) (${PKG}.Value, error) { return v, nil }`);
|
|
850
|
+
if (usage.slEmpty)
|
|
851
|
+
helpers.push(`// slEmpty is the empty scope constant passed to scope-free primitive exprs (bc#75:
|
|
852
|
+
// literal eval never reads scope, so no snapshot is materialized).
|
|
853
|
+
func slEmpty() *${PKG}.Obj { return ${PKG}.NewObj() }`);
|
|
854
|
+
if (usage.slBind)
|
|
855
|
+
helpers.push(`// slVE packs a single (Value, error) result so statically-wired inline exprs compose as
|
|
856
|
+
// one expression (bc#75: Go only spreads a multi-return call as the sole argument).
|
|
857
|
+
type slVE struct {
|
|
858
|
+
\tVal ${PKG}.Value
|
|
859
|
+
\tErr error
|
|
475
860
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
861
|
+
// slVE2 folds a primitive (Value, error) return into an slVE (dynamic residual exprs).
|
|
862
|
+
func slVE2(v ${PKG}.Value, err error) slVE { return slVE{Val: v, Err: err} }
|
|
863
|
+
// slValVE wraps a native literal (string/bool/null) — no evaluate needed.
|
|
864
|
+
func slValVE(v ${PKG}.Value) slVE { return slVE{Val: v} }
|
|
865
|
+
// slOkVE wraps an already-resolved local (statically-wired ref read: no scope snapshot).
|
|
866
|
+
func slOkVE(v ${PKG}.Value) slVE { return slVE{Val: v} }
|
|
867
|
+
// slBindVE reads a scope-bound head directly (UNKNOWN_BINDING matches expr.go).
|
|
868
|
+
func slBindVE(scope *${PKG}.Obj, head string) slVE {
|
|
869
|
+
\tv, ok := scope.Get(head)
|
|
870
|
+
\tif !ok {
|
|
871
|
+
\t\treturn slVE{Err: ${PKG}.NewExprFailure("UNKNOWN_BINDING", "unknown binding: "+head)}
|
|
872
|
+
\t}
|
|
873
|
+
\treturn slVE{Val: v}
|
|
480
874
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
\
|
|
875
|
+
// slHeadOrVE — a skippable node's ref head: its local if produced, else the same-named input
|
|
876
|
+
// binding (scope-composition fall-through), else UNKNOWN_BINDING (expr.go semantics).
|
|
877
|
+
func slHeadOrVE(ok bool, local ${PKG}.Value, scope *${PKG}.Obj, head string) slVE {
|
|
878
|
+
\tif ok {
|
|
879
|
+
\t\treturn slVE{Val: local}
|
|
485
880
|
\t}
|
|
486
|
-
\
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
881
|
+
\treturn slBindVE(scope, head)
|
|
882
|
+
}`);
|
|
883
|
+
if (usage.slField)
|
|
884
|
+
helpers.push(`// slFieldVE walks a static field path over an already-resolved head (bc#75: the head is a
|
|
885
|
+
// local read, not a scope walk). Reproduces expr.go ref/refOpt field semantics.
|
|
886
|
+
func slFieldVE(op string, head slVE, path []string) slVE {
|
|
887
|
+
\tif head.Err != nil {
|
|
888
|
+
\t\treturn head
|
|
490
889
|
\t}
|
|
491
|
-
\
|
|
890
|
+
\tcur := head.Val
|
|
891
|
+
\tfor _, seg := range path {
|
|
492
892
|
\t\tif cur == nil {
|
|
493
893
|
\t\t\tif op == "refOpt" {
|
|
494
|
-
\t\t\t\treturn
|
|
894
|
+
\t\t\t\treturn slVE{Val: nil}
|
|
495
895
|
\t\t\t}
|
|
496
|
-
\t\t\treturn
|
|
896
|
+
\t\t\treturn slVE{Err: ${PKG}.NewExprFailure("NULL_REF", "null intermediate at ."+seg+" (use ?.)")}
|
|
497
897
|
\t\t}
|
|
498
898
|
\t\to, ok := cur.(*${PKG}.Obj)
|
|
499
899
|
\t\tif !ok {
|
|
500
|
-
\t\t\treturn
|
|
900
|
+
\t\t\treturn slVE{Err: ${PKG}.NewExprFailure("TYPE_MISMATCH", "cannot access ."+seg+" on "+${PKG}.TypeName(cur))}
|
|
501
901
|
\t\t}
|
|
502
902
|
\t\tnext, present := o.Get(seg)
|
|
503
903
|
\t\tif !present {
|
|
504
|
-
\t\t\treturn
|
|
904
|
+
\t\t\treturn slVE{Err: ${PKG}.NewExprFailure("MISSING_PROP", "missing property ."+seg)}
|
|
505
905
|
\t\t}
|
|
506
906
|
\t\tcur = next
|
|
507
907
|
\t}
|
|
508
|
-
\treturn cur
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
//
|
|
908
|
+
\treturn slVE{Val: cur}
|
|
909
|
+
}`);
|
|
910
|
+
if (usage.slCat)
|
|
911
|
+
helpers.push(`// slCatVE concatenates already-evaluated string parts natively (bc#75: no closures, no
|
|
912
|
+
// slPart func values). Same "all-eval then string check" order + TYPE_MISMATCH as expr.go.
|
|
913
|
+
func slCatVE(parts ...slVE) slVE {
|
|
914
|
+
\tvar b strings.Builder
|
|
915
|
+
\tfor _, p := range parts {
|
|
916
|
+
\t\tif p.Err != nil {
|
|
917
|
+
\t\t\treturn p
|
|
918
|
+
\t\t}
|
|
919
|
+
\t\ts, ok := p.Val.(string)
|
|
920
|
+
\t\tif !ok {
|
|
921
|
+
\t\t\treturn slVE{Err: ${PKG}.NewExprFailure("TYPE_MISMATCH", "concat: strings only (got "+${PKG}.TypeName(p.Val)+"; no implicit toString)")}
|
|
922
|
+
\t\t}
|
|
923
|
+
\t\tb.WriteString(s)
|
|
924
|
+
\t}
|
|
925
|
+
\treturn slVE{Val: b.String()}
|
|
926
|
+
}`);
|
|
927
|
+
if (usage.slPart)
|
|
928
|
+
helpers.push(`// slPart is a self-evaluating concat part (RunPlan path only — real-concurrency plans).
|
|
929
|
+
type slPart = func(scope *${PKG}.Obj) (${PKG}.Value, error)
|
|
930
|
+
// slConcat concatenates n>=2 string parts (RunPlan path); arity<2 is invalid IR.
|
|
513
931
|
func slConcat(parts []slPart, scope *${PKG}.Obj) (${PKG}.Value, error) {
|
|
514
932
|
\tif len(parts) < 2 {
|
|
515
933
|
\t\treturn nil, ${PKG}.NewExprFailure("INVALID_NODE", fmt.Sprintf("concat expects >= 2 args, got %d", len(parts)))
|
|
@@ -528,15 +946,98 @@ func slConcat(parts []slPart, scope *${PKG}.Obj) (${PKG}.Value, error) {
|
|
|
528
946
|
\t}
|
|
529
947
|
\treturn b.String(), nil
|
|
530
948
|
}
|
|
949
|
+
// slRef/slRefOpt: RunPlan-path native scope/field walk (expr.go ref semantics).
|
|
950
|
+
func slRef(path []string, scope *${PKG}.Obj) (${PKG}.Value, error) { return slRefWalk("ref", path, scope) }
|
|
951
|
+
func slRefOpt(path []string, scope *${PKG}.Obj) (${PKG}.Value, error) { return slRefWalk("refOpt", path, scope) }
|
|
952
|
+
func slRefWalk(op string, path []string, scope *${PKG}.Obj) (${PKG}.Value, error) {
|
|
953
|
+
\tif len(path) == 0 {
|
|
954
|
+
\t\treturn nil, ${PKG}.NewExprFailure("INVALID_NODE", op+" expects a non-empty string path")
|
|
955
|
+
\t}
|
|
956
|
+
\tcur, ok := scope.Get(path[0])
|
|
957
|
+
\tif !ok {
|
|
958
|
+
\t\treturn nil, ${PKG}.NewExprFailure("UNKNOWN_BINDING", "unknown binding: "+path[0])
|
|
959
|
+
\t}
|
|
960
|
+
\tfor _, seg := range path[1:] {
|
|
961
|
+
\t\tif cur == nil {
|
|
962
|
+
\t\t\tif op == "refOpt" {
|
|
963
|
+
\t\t\t\treturn nil, nil
|
|
964
|
+
\t\t\t}
|
|
965
|
+
\t\t\treturn nil, ${PKG}.NewExprFailure("NULL_REF", "null intermediate at ."+seg+" (use ?.)")
|
|
966
|
+
\t\t}
|
|
967
|
+
\t\to, isObj := cur.(*${PKG}.Obj)
|
|
968
|
+
\t\tif !isObj {
|
|
969
|
+
\t\t\treturn nil, ${PKG}.NewExprFailure("TYPE_MISMATCH", "cannot access ."+seg+" on "+${PKG}.TypeName(cur))
|
|
970
|
+
\t\t}
|
|
971
|
+
\t\tnext, present := o.Get(seg)
|
|
972
|
+
\t\tif !present {
|
|
973
|
+
\t\t\treturn nil, ${PKG}.NewExprFailure("MISSING_PROP", "missing property ."+seg)
|
|
974
|
+
\t\t}
|
|
975
|
+
\t\tcur = next
|
|
976
|
+
\t}
|
|
977
|
+
\treturn cur, nil
|
|
978
|
+
}`);
|
|
979
|
+
// 依存 import の構造的判定。
|
|
980
|
+
const imports = [];
|
|
981
|
+
if (usesJson)
|
|
982
|
+
imports.push(`\t"encoding/json"`);
|
|
983
|
+
imports.push(`\t"fmt"`);
|
|
984
|
+
if (usage.slCat || usage.slPart)
|
|
985
|
+
imports.push(`\t"strings"`);
|
|
986
|
+
if (usage.runPlan)
|
|
987
|
+
imports.push(`\t"sync"`);
|
|
988
|
+
imports.push(``);
|
|
989
|
+
imports.push(`\t${PKG} ${JSON.stringify(runtimeImport)}`);
|
|
990
|
+
return `// GENERATED by behavior-contracts common generator (bc#38/#75, straight-line) — DO NOT EDIT.
|
|
991
|
+
// Input: portable component-graph IR only (scp-ir-architecture.md §5/§7.4).
|
|
992
|
+
// This is the STRAIGHT-LINE codegen endpoint (typed-codegen.md §4.1): each component is
|
|
993
|
+
// emitted as STATIC native Go (bc#75). Strictly-sequential plans compile to plain
|
|
994
|
+
// statements in dependency order — no RunPlan, no []OpSpec table, no per-op ID search, no
|
|
995
|
+
// baseScope() per-expression snapshots. Statically-wired refs read locals directly; static
|
|
996
|
+
// concat is a flat slCat(...) over already-evaluated values. Only plans with REAL bounded
|
|
997
|
+
// concurrency (multi-op stages) drive the RunPlan primitive (semantics SSoT), and there the
|
|
998
|
+
// per-op exec is index-resolved (no linear ID scan). Fail-closed operator semantics
|
|
999
|
+
// (overflow arithmetic, strict-bool short-circuit, obj/arr construction, checked literals)
|
|
1000
|
+
// still route through the A0 Prim* primitives (#36) — the semantics SSoT stays single-
|
|
1001
|
+
// sourced. Handlers are ALWAYS injected at the boundary; they are never generated.
|
|
1002
|
+
// The portable IR is NOT embedded: this module carries only IRFingerprint + ComponentNames.
|
|
1003
|
+
// irFingerprint: ${fingerprint}
|
|
1004
|
+
|
|
1005
|
+
package behaviors
|
|
1006
|
+
|
|
1007
|
+
import (
|
|
1008
|
+
${imports.join("\n")}
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
// ExpectedSpecVersions are the spec versions baked at generation time
|
|
1012
|
+
// (fail-closed constant comparison at init).
|
|
1013
|
+
var ExpectedSpecVersions = map[string]int64{"behavior": ${specVersions.behavior}, "expression": ${specVersions.expression}, "plan": ${specVersions.plan}}
|
|
1014
|
+
|
|
1015
|
+
// IRFingerprint is the FNV-1a 64 fingerprint of the source portable IR
|
|
1016
|
+
// (CanonicalJSON discipline, #208), computed at GENERATION time.
|
|
1017
|
+
const IRFingerprint = ${JSON.stringify(fingerprint)}
|
|
1018
|
+
|
|
1019
|
+
// ComponentNames are the components exposed by Bind(), in IR declaration order.
|
|
1020
|
+
var ComponentNames = []string{${names}}
|
|
1021
|
+
|
|
1022
|
+
// init — load-time fail-closed checks (#208 prepared-artifact discipline).
|
|
1023
|
+
func init() {
|
|
1024
|
+
\tfor k, want := range ExpectedSpecVersions {
|
|
1025
|
+
\t\tif got := ${PKG}.SpecVersions[k]; got != want {
|
|
1026
|
+
\t\t\tpanic(fmt.Sprintf("behavior-contracts generated module: spec-version skew for '%s' (generated=%d, runtime=%d) — regenerate against this runtime (fail-closed)", k, want, got))
|
|
1027
|
+
\t\t}
|
|
1028
|
+
\t}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// ── native static-wiring helpers (bc#75) — no scope snapshots, no closures ─────
|
|
1032
|
+
// These are NATIVE Go — they never call EvaluateExpression / Prim*. They reproduce
|
|
1033
|
+
// expr.go's ref/refOpt/concat semantics (same Failure codes) for the statically-wired
|
|
1034
|
+
// inline path while removing the expression-tree interpretation.
|
|
1035
|
+
${helpers.join("\n\n")}
|
|
531
1036
|
|
|
532
1037
|
// ── straight-line component functions (no RunBehavior tree-walk) ───────────────
|
|
533
1038
|
${fns}
|
|
534
1039
|
|
|
535
|
-
// Bind — inject handlers (boundary injection
|
|
536
|
-
// one executor per component. Each executor runs the straight-line function for that
|
|
537
|
-
// component (plan via RunPlan primitive + per-node native/primitive port eval +
|
|
538
|
-
// injected handler) — NOT the RunBehavior tree-walk. Observationally equivalent to the
|
|
539
|
-
// RunBehavior path: same values, same emitted op sequence, same Failure code.
|
|
1040
|
+
// Bind — inject handlers (boundary injection) and get one executor per component.
|
|
540
1041
|
func Bind(handlers ${PKG}.ComponentExec) map[string]func(input *${PKG}.Obj) (${PKG}.Value, error) {
|
|
541
1042
|
\tbound := make(map[string]func(input *${PKG}.Obj) (${PKG}.Value, error), len(ComponentNames))
|
|
542
1043
|
\tfor _, name := range ComponentNames {
|
|
@@ -559,12 +1060,12 @@ const dialect = {
|
|
|
559
1060
|
mapScopeVar: MAP_SCOPE,
|
|
560
1061
|
emitComponent,
|
|
561
1062
|
emitModule,
|
|
1063
|
+
beginModule() {
|
|
1064
|
+
resetUsage();
|
|
1065
|
+
},
|
|
562
1066
|
};
|
|
563
1067
|
/**
|
|
564
|
-
* go-typed(bc#47)が struct-native exec を綴じるために再利用する内部 emit ヘルパ。
|
|
565
|
-
* plan/handler-dispatch SSoT(RunPlan + ExecHandler + native/primitive port eval)をそのまま使い、
|
|
566
|
-
* 「結果を Value scope でなく struct scope に持つ」差分だけを typed 側で足すための seam。runtime 語彙
|
|
567
|
-
* (PKG)と ops/plan リテラル・式 emit を共有する(意味論の二重実装を避ける)。
|
|
1068
|
+
* go-typed(bc#47)が struct-native exec を綴じるために再利用する内部 emit ヘルパ。
|
|
568
1069
|
*/
|
|
569
1070
|
export const goStraightlineInternals = {
|
|
570
1071
|
PKG,
|
|
@@ -578,10 +1079,6 @@ export const goStraightlineInternals = {
|
|
|
578
1079
|
function emit(ctx) {
|
|
579
1080
|
return emitStraightlineModule(ctx, dialect, SCOPE);
|
|
580
1081
|
}
|
|
581
|
-
/**
|
|
582
|
-
* goStraightlineEmitter — 言語識別子 `"go-straightline"` の emitter plugin。
|
|
583
|
-
* 既存の `go`(リテラル焼き込み・endpoint 3-literal)と併存する additive な第2モード。
|
|
584
|
-
*/
|
|
585
1082
|
export const goStraightlineEmitter = {
|
|
586
1083
|
language: "go-straightline",
|
|
587
1084
|
fileExtension: ".go",
|
|
@@ -589,4 +1086,5 @@ export const goStraightlineEmitter = {
|
|
|
589
1086
|
filenameHint: "behaviors.straightline.generated.go",
|
|
590
1087
|
emit,
|
|
591
1088
|
};
|
|
1089
|
+
void buildComponentPlan;
|
|
592
1090
|
//# sourceMappingURL=emit-straightline-go.js.map
|