@womp/spark-agent 6.5.0 → 8.0.0
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/CHANGELOG.md +86 -0
- package/README.md +7 -4
- package/dist/agent.d.ts +3 -6
- package/dist/agent.js +41 -23
- package/dist/capabilities/geometry.d.ts +17 -5
- package/dist/capabilities/geometry.js +11 -12
- package/dist/compaction.d.ts +41 -8
- package/dist/compaction.js +241 -77
- package/dist/context-window.d.ts +23 -0
- package/dist/context-window.js +141 -0
- package/dist/generated/geometry-declarations.d.ts +1 -1
- package/dist/generated/geometry-declarations.js +1 -1
- package/dist/geometry/backendRegistry.d.ts +1 -0
- package/dist/geometry/common.d.ts +5 -2
- package/dist/geometry/common.js +18 -3
- package/dist/geometry/compiler.js +2 -2
- package/dist/geometry/diagnostics.d.ts +2 -7
- package/dist/geometry/diagnostics.js +3 -51
- package/dist/geometry/graph.d.ts +1 -1
- package/dist/geometry/graph.js +1 -0
- package/dist/geometry/index.d.ts +0 -1
- package/dist/geometry/index.js +0 -3
- package/dist/geometry/lift.js +105 -113
- package/dist/geometry/lower.js +29 -49
- package/dist/geometry/schemas/flowir-dialect.json +117 -117
- package/dist/geometry/writeParamData.js +4 -4
- package/dist/geometry.d.ts +2 -5
- package/dist/geometry.js +7 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/intent-bench.js +1 -1
- package/dist/sdk-geometry-host.d.ts +1 -1
- package/dist/sdk-geometry-host.js +253 -251
- package/dist/ui.d.ts +7 -0
- package/dist/ui.js +1 -0
- package/dist/womp-scene-api.d.ts +9 -1
- package/dist/womp-scene-api.js +41 -32
- package/package.json +2 -2
- package/dist/context-budget.d.ts +0 -65
- package/dist/context-budget.js +0 -364
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type * as flowir from "@womp/flowir";
|
|
2
|
+
import type { BackendPort, BackendRegistry, BackendValue, GraphExt } from "./backendRegistry.js";
|
|
2
3
|
/** Scalar backend storage is zero-initialized. Empty arrays have node-specific semantics. */
|
|
3
4
|
export declare function defaultScalar(port: BackendPort): BackendValue | null;
|
|
4
5
|
export type Edge = {
|
|
@@ -14,11 +15,13 @@ export type Edge = {
|
|
|
14
15
|
export declare const VALUE_BACKEND_NAMES: Record<string, string>;
|
|
15
16
|
/** FlowIR scalar type → backend node name (inverse of VALUE_BACKEND_NAMES). */
|
|
16
17
|
export declare function valueBackendName(typeName: string): string | undefined;
|
|
17
|
-
export declare function titleToAstName(title: string): string;
|
|
18
18
|
export declare function astNameToTitle(s: string): string;
|
|
19
|
+
export declare function storedSymbol(graphExt: GraphExt, bid: string): string | undefined;
|
|
20
|
+
export declare function paramName(graphExt: GraphExt, valueBid: string, inputBid: string): string | undefined;
|
|
19
21
|
export declare function isArrayPortType(type: string): boolean;
|
|
20
22
|
export declare function parseConnection(key: string): Edge | null;
|
|
21
23
|
export declare function buildIncomingMap(connections: Record<string, 0>): Map<string, Edge[]>;
|
|
24
|
+
export declare function backendTypeId(flowirRegistry: flowir.Registry, name: string): number;
|
|
22
25
|
/** Resolve the GIP and value-node type IDs from the backend registry. */
|
|
23
26
|
export declare function resolveParamTypeIds(backendRegistry: BackendRegistry): {
|
|
24
27
|
gipTypeId: number | null;
|
package/dist/geometry/common.js
CHANGED
|
@@ -4,7 +4,7 @@ export function defaultScalar(port) {
|
|
|
4
4
|
return 0;
|
|
5
5
|
if (port.type === "Bool")
|
|
6
6
|
return false;
|
|
7
|
-
if (port.type === "Vec"
|
|
7
|
+
if (port.type === "Vec")
|
|
8
8
|
return { x: 0, y: 0, z: 0 };
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
@@ -25,7 +25,7 @@ export function valueBackendName(typeName) {
|
|
|
25
25
|
}
|
|
26
26
|
return undefined;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
function titleToAstName(title) {
|
|
29
29
|
return title
|
|
30
30
|
.trim()
|
|
31
31
|
.replace(/\s+(.)/g, (_, c) => "_" + c.toLowerCase())
|
|
@@ -36,8 +36,15 @@ export function astNameToTitle(s) {
|
|
|
36
36
|
.replace(/_./g, (m) => " " + m[1].toUpperCase())
|
|
37
37
|
.replace(/^./, (m) => m.toUpperCase());
|
|
38
38
|
}
|
|
39
|
+
export function storedSymbol(graphExt, bid) {
|
|
40
|
+
return graphExt.nodes?.[bid]?.flowirSymbol;
|
|
41
|
+
}
|
|
42
|
+
export function paramName(graphExt, valueBid, inputBid) {
|
|
43
|
+
const label = graphExt.nodes?.[inputBid]?.name;
|
|
44
|
+
return storedSymbol(graphExt, valueBid) ?? (label ? titleToAstName(label) : undefined);
|
|
45
|
+
}
|
|
39
46
|
export function isArrayPortType(type) {
|
|
40
|
-
return type.endsWith("
|
|
47
|
+
return type.endsWith("Array");
|
|
41
48
|
}
|
|
42
49
|
export function parseConnection(key) {
|
|
43
50
|
const m = key.match(/^(\d+):(\d+)-(\d+):(\d+)$/);
|
|
@@ -65,6 +72,14 @@ export function buildIncomingMap(connections) {
|
|
|
65
72
|
}
|
|
66
73
|
return incoming;
|
|
67
74
|
}
|
|
75
|
+
export function backendTypeId(flowirRegistry, name) {
|
|
76
|
+
const node = flowirRegistry.getNode(name);
|
|
77
|
+
if (!node)
|
|
78
|
+
throw new Error(`Unknown dialect node ${name}.`);
|
|
79
|
+
if (!/^\d+$/.test(node.type))
|
|
80
|
+
throw new Error(`Dialect node ${name} has type ${JSON.stringify(node.type)}; the geometry backend needs a decimal type id.`);
|
|
81
|
+
return Number(node.type);
|
|
82
|
+
}
|
|
68
83
|
/** Resolve the GIP and value-node type IDs from the backend registry. */
|
|
69
84
|
export function resolveParamTypeIds(backendRegistry) {
|
|
70
85
|
const gipTypeId = backendRegistry.getSchemaByName("GroupInputParam")?.typeId ?? null;
|
|
@@ -16,9 +16,9 @@ export function compileGeometry(source) {
|
|
|
16
16
|
// of the text, not by whoever forwards it.
|
|
17
17
|
if (!parsed.ok)
|
|
18
18
|
throw new Error(formatBoundedDiagnostics(source, parsed.diagnostics));
|
|
19
|
-
const result = flowir.compileFromProgram(parsed.program,
|
|
19
|
+
const result = flowir.compileFromProgram(parsed.program, flowirRegistry);
|
|
20
20
|
if (!result.ok)
|
|
21
|
-
throw new Error(formatBoundedDiagnostics(
|
|
21
|
+
throw new Error(formatBoundedDiagnostics(source, result.diagnostics));
|
|
22
22
|
const compiled = lower(parsed.program, flowirRegistry, backendRegistry);
|
|
23
23
|
for (const statement of parsed.program.statements) {
|
|
24
24
|
const name = statement.kind === "node"
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as flowir from "@womp/flowir";
|
|
2
2
|
export declare const MAX_REPORTED_DIAGNOSTICS = 5;
|
|
3
3
|
export declare const MAX_DIAGNOSTIC_BYTES: number;
|
|
4
|
-
|
|
5
|
-
export declare function formatBoundedDiagnostics(source: string, diagnostics: readonly DiagnosticLike[], limits?: {
|
|
6
|
-
count?: number;
|
|
7
|
-
bytes?: number;
|
|
8
|
-
}): string;
|
|
4
|
+
export declare function formatBoundedDiagnostics(source: string, diagnostics: readonly flowir.Diagnostic[]): string;
|
|
9
5
|
/**
|
|
10
6
|
* A host-assembled diagnostic list, bounded the same way: each entry to
|
|
11
7
|
* `MAX_DIAGNOSTIC_BYTES`, the list to `MAX_REPORTED_DIAGNOSTICS` entries, with
|
|
@@ -15,4 +11,3 @@ export declare function boundedDiagnostics(diagnostics: readonly string[], limit
|
|
|
15
11
|
count?: number;
|
|
16
12
|
bytes?: number;
|
|
17
13
|
}): string[];
|
|
18
|
-
export {};
|
|
@@ -1,57 +1,9 @@
|
|
|
1
|
+
import * as flowir from "@womp/flowir";
|
|
1
2
|
import { boundedText } from "../bounded-output.js";
|
|
2
|
-
// WHY THIS FILE EXISTS. `@womp/flowir`'s own `formatDiagnostics` emits one
|
|
3
|
-
// four-line block per diagnostic -- header, source line, caret, hint -- and
|
|
4
|
-
// joins them, so a compiler that reports the same one-character mistake five
|
|
5
|
-
// times repeats the offending source line five times and its hint five times.
|
|
6
|
-
// That string is what `compileGeometry` throws and what a rejected
|
|
7
|
-
// `geometry.write` hands the model as a diagnostic; on staging one bad enum
|
|
8
|
-
// value cost a whole turn. The compiler holds the structured diagnostics
|
|
9
|
-
// already, so the bound belongs here rather than behind a package this one
|
|
10
|
-
// only consumes.
|
|
11
|
-
//
|
|
12
|
-
// What is kept: every diagnostic's header (code, line, column, message), the
|
|
13
|
-
// source line once per distinct line, its caret, and each distinct hint once.
|
|
14
|
-
// What is dropped is only repetition and the tail past the limits, and the
|
|
15
|
-
// message says so.
|
|
16
3
|
export const MAX_REPORTED_DIAGNOSTICS = 5;
|
|
17
4
|
export const MAX_DIAGNOSTIC_BYTES = 4 * 1024;
|
|
18
|
-
export function formatBoundedDiagnostics(source, diagnostics
|
|
19
|
-
|
|
20
|
-
const bytes = limits.bytes ?? MAX_DIAGNOSTIC_BYTES;
|
|
21
|
-
if (diagnostics.length === 0)
|
|
22
|
-
return "";
|
|
23
|
-
const lines = source.replace(/\r\n?/gu, "\n").split("\n");
|
|
24
|
-
const gutterWidth = String(lines.length || 1).length;
|
|
25
|
-
const pointerPrefix = `${" ".repeat(gutterWidth)} | `;
|
|
26
|
-
const shownLines = new Set();
|
|
27
|
-
const shownCarets = new Set();
|
|
28
|
-
const shownHints = new Set();
|
|
29
|
-
const out = [];
|
|
30
|
-
for (const diagnostic of diagnostics.slice(0, count)) {
|
|
31
|
-
out.push(`error[${diagnostic.code}] L${diagnostic.line}:${diagnostic.column} ${diagnostic.message}`);
|
|
32
|
-
const sourceLine = lines[diagnostic.line - 1];
|
|
33
|
-
if (sourceLine !== undefined) {
|
|
34
|
-
// A line already quoted is not quoted again -- that repetition is the
|
|
35
|
-
// defect this bounds. Its caret still is, once per column, so every
|
|
36
|
-
// diagnostic keeps a marker against the quoted line above it.
|
|
37
|
-
if (shownLines.has(diagnostic.line) === false) {
|
|
38
|
-
shownLines.add(diagnostic.line);
|
|
39
|
-
out.push(`${String(diagnostic.line).padStart(gutterWidth, " ")} | ${sourceLine}`);
|
|
40
|
-
}
|
|
41
|
-
const caret = `${pointerPrefix}${" ".repeat(Math.max(diagnostic.column - 1, 0))}^`;
|
|
42
|
-
if (shownCarets.has(`${diagnostic.line}:${caret}`) === false) {
|
|
43
|
-
shownCarets.add(`${diagnostic.line}:${caret}`);
|
|
44
|
-
out.push(caret);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (shownHints.has(diagnostic.hint) === false) {
|
|
48
|
-
shownHints.add(diagnostic.hint);
|
|
49
|
-
out.push(`${pointerPrefix}hint: ${diagnostic.hint}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (diagnostics.length > count)
|
|
53
|
-
out.push(`${diagnostics.length - count} more diagnostics were omitted (${count} of ${diagnostics.length} shown). Fix these, then compile again.`);
|
|
54
|
-
return boundedText(out.join("\n"), bytes, "Diagnostics");
|
|
5
|
+
export function formatBoundedDiagnostics(source, diagnostics) {
|
|
6
|
+
return boundedText(flowir.formatDiagnostics(source, diagnostics, MAX_REPORTED_DIAGNOSTICS), MAX_DIAGNOSTIC_BYTES, "Diagnostics");
|
|
55
7
|
}
|
|
56
8
|
/**
|
|
57
9
|
* A host-assembled diagnostic list, bounded the same way: each entry to
|
package/dist/geometry/graph.d.ts
CHANGED
|
@@ -31,9 +31,9 @@ export declare const graphExtSchema: z.ZodObject<{
|
|
|
31
31
|
nodes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
32
32
|
name: z.ZodOptional<z.ZodString>;
|
|
33
33
|
position: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
34
|
+
flowirSymbol: z.ZodOptional<z.ZodString>;
|
|
34
35
|
}, z.core.$catchall<z.ZodJSONSchema>>>>;
|
|
35
36
|
}, z.core.$catchall<z.ZodJSONSchema>>;
|
|
36
37
|
export type GeometryGraph = z.infer<typeof geometryGraphSchema>;
|
|
37
38
|
export type GraphData = z.infer<typeof graphDataSchema>;
|
|
38
|
-
/** FlowIR statement roles map to backend IDs only for the current conversion. */
|
|
39
39
|
export type StatementNodes = Record<string, string>;
|
package/dist/geometry/graph.js
CHANGED
package/dist/geometry/index.d.ts
CHANGED
|
@@ -5,5 +5,4 @@ export { lower } from "./lower.js";
|
|
|
5
5
|
export type { LowerResult } from "./lower.js";
|
|
6
6
|
export { lift } from "./lift.js";
|
|
7
7
|
export { writeParamData } from "./writeParamData.js";
|
|
8
|
-
export declare function loadGeoNodesDialect(): flowir.DialectSchema;
|
|
9
8
|
export declare function createGeoNodesRegistry(): flowir.Registry;
|
package/dist/geometry/index.js
CHANGED
|
@@ -4,9 +4,6 @@ export { BackendRegistry } from "./backendRegistry.js";
|
|
|
4
4
|
export { lower } from "./lower.js";
|
|
5
5
|
export { lift } from "./lift.js";
|
|
6
6
|
export { writeParamData } from "./writeParamData.js";
|
|
7
|
-
export function loadGeoNodesDialect() {
|
|
8
|
-
return dialectJson;
|
|
9
|
-
}
|
|
10
7
|
export function createGeoNodesRegistry() {
|
|
11
8
|
return flowir.Registry.fromJsonObject(dialectJson);
|
|
12
9
|
}
|
package/dist/geometry/lift.js
CHANGED
|
@@ -1,11 +1,42 @@
|
|
|
1
|
-
import { VALUE_BACKEND_NAMES,
|
|
2
|
-
const
|
|
3
|
-
function
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { VALUE_BACKEND_NAMES, backendTypeId, isArrayPortType, paramName, storedSymbol, buildIncomingMap, defaultScalar, } from "./common.js";
|
|
2
|
+
const NO_SOURCE_POSITION = { line: 0, column: 0 };
|
|
3
|
+
function uniqueSymbol(prefix, start, taken) {
|
|
4
|
+
let index = start;
|
|
5
|
+
while (taken.has(`${prefix}${index}`))
|
|
6
|
+
index += 1;
|
|
7
|
+
taken.add(`${prefix}${index}`);
|
|
8
|
+
return `${prefix}${index}`;
|
|
9
|
+
}
|
|
10
|
+
function resolveNames(valueNodes, paramInputBid, dialectBids, graphExt) {
|
|
11
|
+
const names = new Map();
|
|
12
|
+
const taken = new Set();
|
|
13
|
+
const keep = (bid, name) => {
|
|
14
|
+
if (name === undefined)
|
|
15
|
+
return;
|
|
16
|
+
names.set(bid, name);
|
|
17
|
+
taken.add(name);
|
|
18
|
+
};
|
|
19
|
+
for (const vNode of valueNodes) {
|
|
20
|
+
const inputBid = paramInputBid(vNode);
|
|
21
|
+
keep(vNode.bid, inputBid
|
|
22
|
+
? paramName(graphExt, vNode.bid, inputBid)
|
|
23
|
+
: storedSymbol(graphExt, vNode.bid));
|
|
24
|
+
}
|
|
25
|
+
for (const bid of dialectBids)
|
|
26
|
+
keep(bid, storedSymbol(graphExt, bid));
|
|
27
|
+
let paramIndex = 0;
|
|
28
|
+
let constIndex = 0;
|
|
29
|
+
for (const vNode of valueNodes) {
|
|
30
|
+
const isParam = paramInputBid(vNode) !== undefined;
|
|
31
|
+
const index = isParam ? paramIndex++ : constIndex++;
|
|
32
|
+
if (!names.has(vNode.bid))
|
|
33
|
+
names.set(vNode.bid, uniqueSymbol(isParam ? "p" : "c", index, taken));
|
|
34
|
+
}
|
|
35
|
+
dialectBids.forEach((bid, index) => {
|
|
36
|
+
if (!names.has(bid))
|
|
37
|
+
names.set(bid, uniqueSymbol("n", index, taken));
|
|
7
38
|
});
|
|
8
|
-
return
|
|
39
|
+
return names;
|
|
9
40
|
}
|
|
10
41
|
function isVec3Object(v) {
|
|
11
42
|
if (!v || typeof v !== "object")
|
|
@@ -105,70 +136,47 @@ function decodeScalarValue(raw, typeName) {
|
|
|
105
136
|
return {
|
|
106
137
|
kind: "vec3",
|
|
107
138
|
value: [vec.x, vec.y, vec.z],
|
|
108
|
-
|
|
109
|
-
pos: POS,
|
|
139
|
+
...NO_SOURCE_POSITION,
|
|
110
140
|
};
|
|
111
141
|
}
|
|
112
|
-
if (typeName === "Bool" && typeof raw === "boolean") {
|
|
113
|
-
return { kind: "bool", value: raw, pos: POS };
|
|
114
|
-
}
|
|
115
142
|
if (typeName === "Int" && typeof raw === "number") {
|
|
116
143
|
const v = Math.trunc(raw);
|
|
117
|
-
return { kind: "int", value: v,
|
|
144
|
+
return { kind: "int", value: v, ...NO_SOURCE_POSITION };
|
|
118
145
|
}
|
|
119
146
|
const num = typeof raw === "number" ? raw : 0;
|
|
120
|
-
return { kind: "float", value: num,
|
|
147
|
+
return { kind: "float", value: num, ...NO_SOURCE_POSITION };
|
|
121
148
|
}
|
|
122
149
|
function decodeBackendValue(raw, backendPort, dialectPort, flowirRegistry) {
|
|
123
|
-
if (dialectPort.
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
if (backendPort.enumValues?.length && typeof raw === "number") {
|
|
130
|
-
const name = backendPort.enumValues[raw];
|
|
131
|
-
if (name)
|
|
132
|
-
return { kind: "ident", name, pos: POS };
|
|
150
|
+
if (flowirRegistry.isEnum(dialectPort.type)) {
|
|
151
|
+
const name = typeof raw === "number"
|
|
152
|
+
? flowirRegistry.getEnumValues(dialectPort.type)[raw]
|
|
153
|
+
: undefined;
|
|
154
|
+
return name ? { kind: "ident", name, ...NO_SOURCE_POSITION } : null;
|
|
133
155
|
}
|
|
134
156
|
const unwrapped = isArrayPortType(backendPort.type) && Array.isArray(raw) && raw.length === 1
|
|
135
157
|
? raw[0]
|
|
136
158
|
: raw;
|
|
137
|
-
if (isVec3Object(unwrapped))
|
|
138
|
-
return
|
|
139
|
-
kind: "vec3",
|
|
140
|
-
value: [unwrapped.x, unwrapped.y, unwrapped.z],
|
|
141
|
-
raw: `Vec3(${decimal(unwrapped.x)}, ${decimal(unwrapped.y)}, ${decimal(unwrapped.z)})`,
|
|
142
|
-
pos: POS,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
159
|
+
if (isVec3Object(unwrapped))
|
|
160
|
+
return decodeScalarValue(unwrapped, "Vec3");
|
|
145
161
|
if (typeof unwrapped === "boolean") {
|
|
146
|
-
return { kind: "bool", value: unwrapped,
|
|
162
|
+
return { kind: "bool", value: unwrapped, ...NO_SOURCE_POSITION };
|
|
147
163
|
}
|
|
148
164
|
if (typeof unwrapped === "number") {
|
|
149
165
|
if (dialectPort.type === "Int" || dialectPort.type === "Int[]") {
|
|
150
166
|
const v = Math.trunc(unwrapped);
|
|
151
|
-
return { kind: "int", value: v,
|
|
167
|
+
return { kind: "int", value: v, ...NO_SOURCE_POSITION };
|
|
152
168
|
}
|
|
153
|
-
return {
|
|
154
|
-
kind: "float",
|
|
155
|
-
value: unwrapped,
|
|
156
|
-
raw: decimal(unwrapped),
|
|
157
|
-
pos: POS,
|
|
158
|
-
};
|
|
169
|
+
return { kind: "float", value: unwrapped, ...NO_SOURCE_POSITION };
|
|
159
170
|
}
|
|
160
171
|
return null;
|
|
161
172
|
}
|
|
162
|
-
function liftParam(vNode, gipBid,
|
|
173
|
+
function liftParam(vNode, gipBid, name, ctx) {
|
|
163
174
|
const typeName = vNode.valueTypeName ?? "Float";
|
|
164
|
-
const extName = ctx.graphExt.nodes?.[gipBid]?.name;
|
|
165
|
-
const name = extName ? titleToAstName(extName) : `p${paramCounter}`;
|
|
166
175
|
const rawDefault = getData(ctx.graph, vNode.bid, "0");
|
|
167
|
-
const paramTypeName = typeName === "Bool" ? "Float" : typeName;
|
|
168
176
|
return {
|
|
169
177
|
kind: "param",
|
|
170
178
|
name,
|
|
171
|
-
typeName
|
|
179
|
+
typeName,
|
|
172
180
|
value: rawDefault != null
|
|
173
181
|
? decodeScalarValue(rawDefault, typeName)
|
|
174
182
|
: decodeScalarValue(typeName === "Vec3" ? { x: 0, y: 0, z: 0 } : 0, typeName),
|
|
@@ -177,53 +185,50 @@ function liftParam(vNode, gipBid, paramCounter, ctx) {
|
|
|
177
185
|
max: getDataAsNumber(ctx.graph, gipBid, "1"),
|
|
178
186
|
step: getDataAsNumber(ctx.graph, gipBid, "2"),
|
|
179
187
|
},
|
|
180
|
-
|
|
188
|
+
...NO_SOURCE_POSITION,
|
|
181
189
|
};
|
|
182
190
|
}
|
|
183
|
-
function liftConst(vNode,
|
|
191
|
+
function liftConst(vNode, name, ctx) {
|
|
184
192
|
const typeName = vNode.valueTypeName ?? "Float";
|
|
185
193
|
const rawDefault = getData(ctx.graph, vNode.bid, "0");
|
|
186
194
|
return {
|
|
187
195
|
kind: "const",
|
|
188
|
-
name
|
|
189
|
-
typeName
|
|
196
|
+
name,
|
|
197
|
+
typeName,
|
|
190
198
|
value: rawDefault != null
|
|
191
199
|
? decodeScalarValue(rawDefault, typeName)
|
|
192
200
|
: decodeScalarValue(typeName === "Vec3" ? { x: 0, y: 0, z: 0 } : 0, typeName),
|
|
193
|
-
|
|
201
|
+
...NO_SOURCE_POSITION,
|
|
194
202
|
};
|
|
195
203
|
}
|
|
196
204
|
function resolveInputBinding(bid, idx, dialectPort, backendPort, ctx) {
|
|
197
205
|
const connEdge = ctx.incoming.get(bid)?.find((e) => e.toPort === idx);
|
|
198
206
|
if (connEdge) {
|
|
199
207
|
const sourceNode = ctx.classMap.get(connEdge.fromBid);
|
|
200
|
-
|
|
208
|
+
const sourceName = ctx.names.get(connEdge.fromBid);
|
|
209
|
+
if (!sourceNode || !sourceName)
|
|
201
210
|
return null;
|
|
202
|
-
|
|
203
|
-
if (paramName) {
|
|
211
|
+
if (sourceNode.cls === "value") {
|
|
204
212
|
return {
|
|
205
213
|
name: dialectPort.name,
|
|
206
|
-
value: { kind: "ident", name:
|
|
207
|
-
|
|
214
|
+
value: { kind: "ident", name: sourceName, ...NO_SOURCE_POSITION },
|
|
215
|
+
...NO_SOURCE_POSITION,
|
|
208
216
|
};
|
|
209
217
|
}
|
|
210
218
|
if (sourceNode.cls === "dialect" && sourceNode.dialectName) {
|
|
211
219
|
const sourceDialect = ctx.flowirRegistry.getNode(sourceNode.dialectName);
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
pos: POS,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
220
|
+
const outputPort = sourceDialect?.outputs[connEdge.fromPort];
|
|
221
|
+
if (outputPort) {
|
|
222
|
+
return {
|
|
223
|
+
name: dialectPort.name,
|
|
224
|
+
value: {
|
|
225
|
+
kind: "ref",
|
|
226
|
+
nodeId: sourceName,
|
|
227
|
+
port: outputPort.name,
|
|
228
|
+
...NO_SOURCE_POSITION,
|
|
229
|
+
},
|
|
230
|
+
...NO_SOURCE_POSITION,
|
|
231
|
+
};
|
|
227
232
|
}
|
|
228
233
|
}
|
|
229
234
|
return null;
|
|
@@ -234,11 +239,11 @@ function resolveInputBinding(bid, idx, dialectPort, backendPort, ctx) {
|
|
|
234
239
|
const decoded = decodeBackendValue(rawData, backendPort, dialectPort, ctx.flowirRegistry);
|
|
235
240
|
if (!decoded)
|
|
236
241
|
return null;
|
|
237
|
-
return { name: dialectPort.name, value: decoded,
|
|
242
|
+
return { name: dialectPort.name, value: decoded, ...NO_SOURCE_POSITION };
|
|
238
243
|
}
|
|
239
244
|
function liftNodeStmt(bid, cNode, ctx) {
|
|
240
245
|
const dialectName = cNode.dialectName;
|
|
241
|
-
const stmtId = ctx.
|
|
246
|
+
const stmtId = ctx.names.get(bid);
|
|
242
247
|
const dialectNode = ctx.flowirRegistry.getNode(dialectName);
|
|
243
248
|
if (!dialectNode)
|
|
244
249
|
return null;
|
|
@@ -254,7 +259,13 @@ function liftNodeStmt(bid, cNode, ctx) {
|
|
|
254
259
|
if (binding)
|
|
255
260
|
inputs.push(binding);
|
|
256
261
|
}
|
|
257
|
-
return {
|
|
262
|
+
return {
|
|
263
|
+
kind: "node",
|
|
264
|
+
id: stmtId,
|
|
265
|
+
nodeType: dialectName,
|
|
266
|
+
inputs,
|
|
267
|
+
...NO_SOURCE_POSITION,
|
|
268
|
+
};
|
|
258
269
|
}
|
|
259
270
|
function liftOutStmt(oNode, ctx) {
|
|
260
271
|
const geoEdge = ctx.incoming.get(oNode.bid)?.find((e) => e.toPort === 1);
|
|
@@ -264,7 +275,7 @@ function liftOutStmt(oNode, ctx) {
|
|
|
264
275
|
if (!sourceNode || sourceNode.cls !== "dialect" || !sourceNode.dialectName)
|
|
265
276
|
return null;
|
|
266
277
|
const sourceDialect = ctx.flowirRegistry.getNode(sourceNode.dialectName);
|
|
267
|
-
const sourceStmtId = ctx.
|
|
278
|
+
const sourceStmtId = ctx.names.get(geoEdge.fromBid);
|
|
268
279
|
if (!sourceDialect || !sourceStmtId)
|
|
269
280
|
return null;
|
|
270
281
|
const outputPort = sourceDialect.outputs[geoEdge.fromPort];
|
|
@@ -277,20 +288,16 @@ function liftOutStmt(oNode, ctx) {
|
|
|
277
288
|
kind: "ref",
|
|
278
289
|
nodeId: sourceStmtId,
|
|
279
290
|
port: outputPort.name,
|
|
280
|
-
|
|
291
|
+
...NO_SOURCE_POSITION,
|
|
281
292
|
},
|
|
282
|
-
|
|
293
|
+
...NO_SOURCE_POSITION,
|
|
283
294
|
};
|
|
284
295
|
}
|
|
285
296
|
export function lift(graph, graphExt, flowirRegistry, backendRegistry) {
|
|
286
297
|
const reverseMap = new Map();
|
|
287
298
|
for (const name of flowirRegistry.listNodeNames()) {
|
|
288
|
-
const id = flowirRegistry
|
|
289
|
-
if (id == null)
|
|
290
|
-
continue;
|
|
299
|
+
const id = backendTypeId(flowirRegistry, name);
|
|
291
300
|
const outputs = backendRegistry.getSchema(id)?.outputs;
|
|
292
|
-
// Dialect aliases can share a backend ID. Lift the native output type,
|
|
293
|
-
// so vector math is not accidentally narrowed to its scalar alias.
|
|
294
301
|
const matches = flowirRegistry
|
|
295
302
|
.getNode(name)
|
|
296
303
|
?.outputs.every((port, index) => port.type ===
|
|
@@ -313,52 +320,37 @@ export function lift(graph, graphExt, flowirRegistry, backendRegistry) {
|
|
|
313
320
|
const classMap = new Map();
|
|
314
321
|
for (const node of classified)
|
|
315
322
|
classMap.set(node.bid, node);
|
|
323
|
+
const valueNodes = classified.filter((n) => n.cls === "value");
|
|
324
|
+
const paramInputBid = (vNode) => incoming
|
|
325
|
+
.get(vNode.bid)
|
|
326
|
+
?.find((e) => classMap.get(e.fromBid)?.cls === "gip" && e.toPort === 0)
|
|
327
|
+
?.fromBid;
|
|
328
|
+
const dialectBids = toposort(classified.filter((n) => n.cls === "dialect").map((n) => n.bid), incoming);
|
|
316
329
|
const ctx = {
|
|
317
330
|
graph,
|
|
318
|
-
graphExt,
|
|
319
331
|
flowirRegistry,
|
|
320
332
|
backendRegistry,
|
|
321
333
|
incoming,
|
|
322
334
|
classMap,
|
|
323
|
-
|
|
324
|
-
dialectStmtIds: new Map(),
|
|
335
|
+
names: resolveNames(valueNodes, paramInputBid, dialectBids, graphExt),
|
|
325
336
|
};
|
|
326
337
|
const statements = [];
|
|
327
338
|
const nodeIds = {};
|
|
328
|
-
let paramCounter = 0;
|
|
329
|
-
let constCounter = 0;
|
|
330
|
-
const valueNodes = classified.filter((n) => n.cls === "value");
|
|
331
339
|
for (const vNode of valueNodes) {
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
if (
|
|
335
|
-
statements.push(liftParam(vNode,
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
nodeIds[`param:${stmt.name}`] = vNode.bid;
|
|
339
|
-
nodeIds[`param-input:${stmt.name}`] = gipEdge.fromBid;
|
|
340
|
-
paramCounter++;
|
|
340
|
+
const name = ctx.names.get(vNode.bid);
|
|
341
|
+
const inputBid = paramInputBid(vNode);
|
|
342
|
+
if (inputBid) {
|
|
343
|
+
statements.push(liftParam(vNode, inputBid, name, ctx));
|
|
344
|
+
nodeIds[`param:${name}`] = vNode.bid;
|
|
345
|
+
nodeIds[`param-input:${name}`] = inputBid;
|
|
341
346
|
}
|
|
342
347
|
else {
|
|
343
|
-
statements.push(liftConst(vNode,
|
|
344
|
-
const
|
|
345
|
-
ctx.paramBids.set(vNode.bid, stmt.name);
|
|
346
|
-
nodeIds[`const:${stmt.name}`] = vNode.bid;
|
|
347
|
-
constCounter++;
|
|
348
|
+
statements.push(liftConst(vNode, name, ctx));
|
|
349
|
+
nodeIds[`const:${name}`] = vNode.bid;
|
|
348
350
|
}
|
|
349
351
|
}
|
|
350
|
-
const dialectBids
|
|
351
|
-
|
|
352
|
-
.map((n) => n.bid);
|
|
353
|
-
const sortedBids = toposort(dialectBids, incoming);
|
|
354
|
-
let nodeCounter = 0;
|
|
355
|
-
for (const bid of sortedBids) {
|
|
356
|
-
ctx.dialectStmtIds.set(bid, `n${nodeCounter}`);
|
|
357
|
-
nodeCounter++;
|
|
358
|
-
}
|
|
359
|
-
for (const bid of sortedBids) {
|
|
360
|
-
const cNode = classMap.get(bid);
|
|
361
|
-
const stmt = liftNodeStmt(bid, cNode, ctx);
|
|
352
|
+
for (const bid of dialectBids) {
|
|
353
|
+
const stmt = liftNodeStmt(bid, classMap.get(bid), ctx);
|
|
362
354
|
if (stmt) {
|
|
363
355
|
statements.push(stmt);
|
|
364
356
|
nodeIds[`node:${stmt.id}`] = bid;
|