@nrutils/node-red-utils 0.0.0-stage → 0.2.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +190 -0
  3. package/README.md +187 -2
  4. package/index.js +50 -0
  5. package/node-red-base/NodeRedNodeBase.js +143 -0
  6. package/node-red-base/README.md +215 -0
  7. package/node-red-editorcomms/EditorCommsBackend.js +155 -0
  8. package/node-red-editorcomms/EditorCommsFrontend.js +105 -0
  9. package/node-red-editorcomms/README.md +164 -0
  10. package/node-red-gpu/GpuBuffer.js +29 -0
  11. package/node-red-gpu/Kernel.js +35 -0
  12. package/node-red-gpu/README.md +220 -0
  13. package/node-red-gpu/backends/CpuBackend.js +109 -0
  14. package/node-red-gpu/backends/VulkanBackend.js +232 -0
  15. package/node-red-gpu/errors.js +48 -0
  16. package/node-red-gpu/index.js +6 -0
  17. package/node-red-gpu/log.js +18 -0
  18. package/node-red-gpu/native/binding.gyp +14 -0
  19. package/node-red-gpu/native/src/gpu_addon.cpp +1066 -0
  20. package/node-red-gpu/nodeRedGpuMain.js +39 -0
  21. package/node-red-gpu/nodeRedGpuWorker.js +242 -0
  22. package/node-red-gpu/transpiler/emitGlsl.js +497 -0
  23. package/node-red-gpu/transpiler/glslNames.js +60 -0
  24. package/node-red-gpu/transpiler/index.js +81 -0
  25. package/node-red-gpu/transpiler/inferTypes.js +302 -0
  26. package/node-red-gpu/transpiler/validate.js +461 -0
  27. package/node-red-popup/NodeRedPopupBackend.js +69 -0
  28. package/node-red-popup/NodeRedPopupFrontend.js +508 -0
  29. package/node-red-popup/README.md +113 -0
  30. package/node-red-pubsub-bus/NodeRedPubsubBus.js +617 -0
  31. package/node-red-pubsub-bus/NodeRedPubsubBusWorkerProxy.js +239 -0
  32. package/node-red-pubsub-bus/README.md +201 -0
  33. package/node-red-resourceendpoint/NodeRedResourceEndpoint.js +205 -0
  34. package/node-red-resourceendpoint/README.md +119 -0
  35. package/node-red-singleton/NodeRedSingletonBackend.js +30 -0
  36. package/node-red-singleton/NodeRedSingletonFrontend.js +54 -0
  37. package/node-red-singleton/README.md +72 -0
  38. package/node-red-worker/NodeRedWorker.js +443 -0
  39. package/node-red-worker/NodeRedWorkerRuntime.js +659 -0
  40. package/node-red-worker/README.md +273 -0
  41. package/node-red-worker/workerRuntimeLoader.js +55 -0
  42. 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 };