@nrutils/node-red-utils 0.0.0-stage → 0.1.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 +15 -0
- package/LICENSE +190 -0
- package/README.md +181 -2
- package/index.js +50 -0
- package/node-red-base/NodeRedNodeBase.js +143 -0
- package/node-red-base/README.md +215 -0
- package/node-red-editorcomms/EditorCommsBackend.js +155 -0
- package/node-red-editorcomms/EditorCommsFrontend.js +105 -0
- package/node-red-editorcomms/README.md +164 -0
- package/node-red-gpu/GpuBuffer.js +29 -0
- package/node-red-gpu/Kernel.js +35 -0
- package/node-red-gpu/README.md +220 -0
- package/node-red-gpu/backends/CpuBackend.js +109 -0
- package/node-red-gpu/backends/VulkanBackend.js +232 -0
- package/node-red-gpu/errors.js +48 -0
- package/node-red-gpu/index.js +6 -0
- package/node-red-gpu/log.js +18 -0
- package/node-red-gpu/native/binding.gyp +14 -0
- package/node-red-gpu/native/src/gpu_addon.cpp +1066 -0
- package/node-red-gpu/nodeRedGpuMain.js +39 -0
- package/node-red-gpu/nodeRedGpuWorker.js +242 -0
- package/node-red-gpu/transpiler/emitGlsl.js +497 -0
- package/node-red-gpu/transpiler/glslNames.js +60 -0
- package/node-red-gpu/transpiler/index.js +81 -0
- package/node-red-gpu/transpiler/inferTypes.js +302 -0
- package/node-red-gpu/transpiler/validate.js +461 -0
- package/node-red-popup/NodeRedPopupBackend.js +69 -0
- package/node-red-popup/NodeRedPopupFrontend.js +414 -0
- package/node-red-popup/README.md +110 -0
- package/node-red-pubsub-bus/NodeRedPubsubBus.js +617 -0
- package/node-red-pubsub-bus/NodeRedPubsubBusWorkerProxy.js +239 -0
- package/node-red-pubsub-bus/README.md +201 -0
- package/node-red-resourceendpoint/NodeRedResourceEndpoint.js +205 -0
- package/node-red-resourceendpoint/README.md +119 -0
- package/node-red-singleton/NodeRedSingletonBackend.js +30 -0
- package/node-red-singleton/NodeRedSingletonFrontend.js +54 -0
- package/node-red-singleton/README.md +72 -0
- package/node-red-worker/NodeRedWorker.js +438 -0
- package/node-red-worker/NodeRedWorkerRuntime.js +651 -0
- package/node-red-worker/README.md +265 -0
- package/node-red-worker/workerRuntimeLoader.js +55 -0
- package/package.json +70 -5
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Type inference pass for JS→GLSL 450 compute shader transpiler.
|
|
4
|
+
// Walks the AST and annotates each expression node with node._glslType.
|
|
5
|
+
// Types: 'uint' | 'int' | 'float' | 'bool'
|
|
6
|
+
|
|
7
|
+
const DTYPE_MAP = {
|
|
8
|
+
u8: 'uint', u16: 'uint', u32: 'uint',
|
|
9
|
+
i8: 'int', i16: 'int', i32: 'int',
|
|
10
|
+
f32: 'float', f64: 'float',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const MATH_FUNCTIONS = new Set([
|
|
14
|
+
'abs', 'ceil', 'floor', 'round', 'sqrt', 'inversesqrt',
|
|
15
|
+
'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
|
|
16
|
+
'exp', 'exp2', 'log', 'log2', 'pow',
|
|
17
|
+
'min', 'max', 'clamp', 'mix', 'step', 'smoothstep',
|
|
18
|
+
'fract', 'mod', 'sign', 'length', 'distance', 'dot', 'cross',
|
|
19
|
+
'normalize', 'reflect', 'refract',
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const COMPARISON_OPS = new Set(['==', '!=', '<', '>', '<=', '>=']);
|
|
23
|
+
const LOGICAL_OPS = new Set(['&&', '||']);
|
|
24
|
+
const BITWISE_OPS = new Set(['&', '|', '^', '<<', '>>']);
|
|
25
|
+
|
|
26
|
+
function inferTypes(ast, paramNames, options) {
|
|
27
|
+
const scope = new Map();
|
|
28
|
+
|
|
29
|
+
// Pre-populate scope with buffer parameter types
|
|
30
|
+
const buffers = options.buffers || [];
|
|
31
|
+
for (const buf of buffers) {
|
|
32
|
+
scope.set(buf.name, DTYPE_MAP[buf.dtype] || 'float');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Walk the AST body (array of statements)
|
|
36
|
+
const stmts = Array.isArray(ast) ? ast : (ast.body || []);
|
|
37
|
+
for (const stmt of stmts) {
|
|
38
|
+
inferStmt(stmt);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Statements ──────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
function inferStmt(node) {
|
|
44
|
+
if (!node) return;
|
|
45
|
+
switch (node.type) {
|
|
46
|
+
case 'VariableDeclaration':
|
|
47
|
+
for (const decl of node.declarations) inferVarDeclarator(decl);
|
|
48
|
+
break;
|
|
49
|
+
case 'ExpressionStatement':
|
|
50
|
+
inferExpr(node.expression);
|
|
51
|
+
break;
|
|
52
|
+
case 'ReturnStatement':
|
|
53
|
+
if (node.argument) inferExpr(node.argument);
|
|
54
|
+
break;
|
|
55
|
+
case 'IfStatement':
|
|
56
|
+
inferExpr(node.test);
|
|
57
|
+
inferStmt(node.consequent);
|
|
58
|
+
if (node.alternate) inferStmt(node.alternate);
|
|
59
|
+
break;
|
|
60
|
+
case 'BlockStatement':
|
|
61
|
+
for (const s of node.body) inferStmt(s);
|
|
62
|
+
break;
|
|
63
|
+
case 'ForStatement':
|
|
64
|
+
if (node.init) inferStmt(node.init);
|
|
65
|
+
if (node.test) inferExpr(node.test);
|
|
66
|
+
if (node.update) inferExpr(node.update);
|
|
67
|
+
inferStmt(node.body);
|
|
68
|
+
break;
|
|
69
|
+
case 'WhileStatement':
|
|
70
|
+
inferExpr(node.test);
|
|
71
|
+
inferStmt(node.body);
|
|
72
|
+
break;
|
|
73
|
+
case 'BreakStatement':
|
|
74
|
+
case 'ContinueStatement':
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
// Unknown statement — try as expression
|
|
78
|
+
if (node.expression) inferExpr(node.expression);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function inferVarDeclarator(decl) {
|
|
83
|
+
let t = 'float';
|
|
84
|
+
if (decl.init) {
|
|
85
|
+
inferExpr(decl.init);
|
|
86
|
+
t = decl.init._glslType || 'float';
|
|
87
|
+
}
|
|
88
|
+
if (decl.id && decl.id.name) {
|
|
89
|
+
scope.set(decl.id.name, t);
|
|
90
|
+
}
|
|
91
|
+
decl._glslType = t;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Expressions ─────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
function inferExpr(node) {
|
|
97
|
+
if (!node) return;
|
|
98
|
+
node._glslType = resolveExpr(node);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolveExpr(node) {
|
|
102
|
+
if (!node) return 'float';
|
|
103
|
+
switch (node.type) {
|
|
104
|
+
|
|
105
|
+
// Literals
|
|
106
|
+
case 'Literal': {
|
|
107
|
+
const v = node.value;
|
|
108
|
+
if (typeof v === 'boolean') return 'bool';
|
|
109
|
+
if (typeof v === 'number') {
|
|
110
|
+
// Float if has decimal or exponent in raw source
|
|
111
|
+
const raw = node.raw || String(v);
|
|
112
|
+
if (raw.includes('.') || /e/i.test(raw)) return 'float';
|
|
113
|
+
return 'uint'; // integer literals default to uint (v1 rule)
|
|
114
|
+
}
|
|
115
|
+
return 'float';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Identifiers
|
|
119
|
+
case 'Identifier': {
|
|
120
|
+
const t = scope.get(node.name);
|
|
121
|
+
return t || 'float';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Member expressions: this.thread.x/y/z, this.constants.NAME, buf[i]
|
|
125
|
+
case 'MemberExpression': {
|
|
126
|
+
return resolveMember(node);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Assignment
|
|
130
|
+
case 'AssignmentExpression': {
|
|
131
|
+
inferExpr(node.right);
|
|
132
|
+
const t = node.right._glslType || 'float';
|
|
133
|
+
if (node.left.type === 'Identifier') {
|
|
134
|
+
scope.set(node.left.name, t);
|
|
135
|
+
}
|
|
136
|
+
inferExpr(node.left);
|
|
137
|
+
return t;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Binary
|
|
141
|
+
case 'BinaryExpression': {
|
|
142
|
+
inferExpr(node.left);
|
|
143
|
+
inferExpr(node.right);
|
|
144
|
+
const op = node.operator;
|
|
145
|
+
if (COMPARISON_OPS.has(op)) return 'bool';
|
|
146
|
+
if (BITWISE_OPS.has(op)) return 'uint';
|
|
147
|
+
// Arithmetic: promote if mixed
|
|
148
|
+
const lt = node.left._glslType || 'float';
|
|
149
|
+
const rt = node.right._glslType || 'float';
|
|
150
|
+
if (lt === 'float' || rt === 'float') return 'float';
|
|
151
|
+
if (lt === 'int' || rt === 'int') return 'int';
|
|
152
|
+
return 'uint';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Logical
|
|
156
|
+
case 'LogicalExpression': {
|
|
157
|
+
inferExpr(node.left);
|
|
158
|
+
inferExpr(node.right);
|
|
159
|
+
return 'bool';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Unary
|
|
163
|
+
case 'UnaryExpression': {
|
|
164
|
+
inferExpr(node.argument);
|
|
165
|
+
const op = node.operator;
|
|
166
|
+
if (op === '!') return 'bool';
|
|
167
|
+
if (op === '~') return 'uint';
|
|
168
|
+
if (op === '-') {
|
|
169
|
+
const at = node.argument._glslType || 'float';
|
|
170
|
+
return at === 'uint' ? 'int' : at;
|
|
171
|
+
}
|
|
172
|
+
return node.argument._glslType || 'float';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Update (++/--)
|
|
176
|
+
case 'UpdateExpression': {
|
|
177
|
+
inferExpr(node.argument);
|
|
178
|
+
return node.argument._glslType || 'uint';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Conditional / ternary
|
|
182
|
+
case 'ConditionalExpression': {
|
|
183
|
+
inferExpr(node.test);
|
|
184
|
+
inferExpr(node.consequent);
|
|
185
|
+
inferExpr(node.alternate);
|
|
186
|
+
return node.consequent._glslType || 'float';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Call expressions (Math.xxx, bare math funcs)
|
|
190
|
+
case 'CallExpression': {
|
|
191
|
+
for (const arg of node.arguments) inferExpr(arg);
|
|
192
|
+
return resolveCall(node);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Sequence expression — type of last
|
|
196
|
+
case 'SequenceExpression': {
|
|
197
|
+
for (const e of node.expressions) inferExpr(e);
|
|
198
|
+
return node.expressions[node.expressions.length - 1]?._glslType || 'float';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
default:
|
|
202
|
+
return 'float';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function resolveMember(node) {
|
|
207
|
+
// this.thread.x / this.thread.y / this.thread.z / localX / localY / localZ → uint
|
|
208
|
+
if (
|
|
209
|
+
node.object?.type === 'MemberExpression' &&
|
|
210
|
+
node.object.object?.type === 'ThisExpression' &&
|
|
211
|
+
node.object.property?.name === 'thread'
|
|
212
|
+
) {
|
|
213
|
+
const prop = node.property?.name;
|
|
214
|
+
if (prop === 'localX' || prop === 'localY' || prop === 'localZ') {
|
|
215
|
+
node._glslType = 'uint';
|
|
216
|
+
return 'uint';
|
|
217
|
+
}
|
|
218
|
+
return 'uint';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// this.constants.NAME → look up from options.constants
|
|
222
|
+
if (
|
|
223
|
+
node.object?.type === 'MemberExpression' &&
|
|
224
|
+
node.object.object?.type === 'ThisExpression' &&
|
|
225
|
+
node.object.property?.name === 'constants'
|
|
226
|
+
) {
|
|
227
|
+
const cName = node.property?.name;
|
|
228
|
+
if (cName && options.constants) {
|
|
229
|
+
const dtype = options.constants[cName];
|
|
230
|
+
if (dtype) return DTYPE_MAP[dtype] || 'float';
|
|
231
|
+
}
|
|
232
|
+
return 'float';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// bufferName[index] — computed member access
|
|
236
|
+
if (node.computed) {
|
|
237
|
+
const objName = node.object?.name;
|
|
238
|
+
if (objName) {
|
|
239
|
+
const buf = buffers.find(b => b.name === objName);
|
|
240
|
+
if (buf) {
|
|
241
|
+
if (node.index) inferExpr(node.index);
|
|
242
|
+
inferExpr(node.property); // Acorn uses property for computed
|
|
243
|
+
return DTYPE_MAP[buf.dtype] || 'float';
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Fallback: infer the object
|
|
249
|
+
inferExpr(node.object);
|
|
250
|
+
return node.object?._glslType || 'float';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function resolveCall(node) {
|
|
254
|
+
const callee = node.callee;
|
|
255
|
+
// barrier() — void return
|
|
256
|
+
if (callee.type === 'Identifier' && callee.name === 'barrier') {
|
|
257
|
+
return 'void';
|
|
258
|
+
}
|
|
259
|
+
// Type cast calls return their named type
|
|
260
|
+
if (callee.type === 'Identifier') {
|
|
261
|
+
const castTypes = { 'float': 'float', 'int': 'int', 'uint': 'uint' };
|
|
262
|
+
if (castTypes[callee.name]) {
|
|
263
|
+
node._glslType = castTypes[callee.name];
|
|
264
|
+
// Still infer the argument
|
|
265
|
+
if (node.arguments[0]) resolveExpr(node.arguments[0]);
|
|
266
|
+
return node._glslType;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Atomic operations return the previous value (uint for u32 buffers, int for i32)
|
|
270
|
+
if (callee.type === 'Identifier') {
|
|
271
|
+
const atomicFuncs = new Set([
|
|
272
|
+
'atomicAdd', 'atomicMin', 'atomicMax',
|
|
273
|
+
'atomicOr', 'atomicAnd', 'atomicXor',
|
|
274
|
+
'atomicExchange', 'atomicCompSwap',
|
|
275
|
+
]);
|
|
276
|
+
if (atomicFuncs.has(callee.name)) {
|
|
277
|
+
// Arguments are already inferred by the CallExpression case above
|
|
278
|
+
// Return type matches the buffer element type
|
|
279
|
+
const bufArg = node.arguments[0];
|
|
280
|
+
if (bufArg && bufArg.type === 'Identifier') {
|
|
281
|
+
const bufType = scope.get(bufArg.name);
|
|
282
|
+
if (bufType) { node._glslType = bufType; return bufType; }
|
|
283
|
+
}
|
|
284
|
+
node._glslType = 'uint';
|
|
285
|
+
return 'uint';
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Math.xxx(...) or bare identifier matching a math function name
|
|
289
|
+
if (
|
|
290
|
+
callee.type === 'MemberExpression' &&
|
|
291
|
+
callee.object?.name === 'Math'
|
|
292
|
+
) {
|
|
293
|
+
return 'float';
|
|
294
|
+
}
|
|
295
|
+
if (callee.type === 'Identifier' && MATH_FUNCTIONS.has(callee.name)) {
|
|
296
|
+
return 'float';
|
|
297
|
+
}
|
|
298
|
+
return 'float';
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
module.exports = { inferTypes };
|