@holoscript/core 2.0.1 → 2.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.
Files changed (137) hide show
  1. package/dist/chunk-2XXE34KS.js +344 -0
  2. package/dist/chunk-2XXE34KS.js.map +1 -0
  3. package/dist/chunk-3X2EGU7Z.cjs +52 -0
  4. package/dist/chunk-3X2EGU7Z.cjs.map +1 -0
  5. package/dist/chunk-AFFVFO4D.js +1689 -0
  6. package/dist/chunk-AFFVFO4D.js.map +1 -0
  7. package/dist/chunk-DGUM43GV.js +10 -0
  8. package/dist/chunk-DGUM43GV.js.map +1 -0
  9. package/{src/HoloScriptDebugger.ts → dist/chunk-DOY73HDH.js} +118 -257
  10. package/dist/chunk-DOY73HDH.js.map +1 -0
  11. package/dist/chunk-JEQ2X3Z6.cjs +12 -0
  12. package/dist/chunk-JEQ2X3Z6.cjs.map +1 -0
  13. package/dist/chunk-L6VLNVKP.cjs +1691 -0
  14. package/dist/chunk-L6VLNVKP.cjs.map +1 -0
  15. package/dist/chunk-MFNO57XL.cjs +347 -0
  16. package/dist/chunk-MFNO57XL.cjs.map +1 -0
  17. package/dist/chunk-R75MREOS.cjs +424 -0
  18. package/dist/chunk-R75MREOS.cjs.map +1 -0
  19. package/dist/chunk-SATNCODL.js +45 -0
  20. package/dist/chunk-SATNCODL.js.map +1 -0
  21. package/dist/chunk-T57ZL7KR.cjs +1281 -0
  22. package/dist/chunk-T57ZL7KR.cjs.map +1 -0
  23. package/dist/chunk-U72GEJZT.js +1279 -0
  24. package/dist/chunk-U72GEJZT.js.map +1 -0
  25. package/dist/debugger.cjs +20 -0
  26. package/dist/debugger.cjs.map +1 -0
  27. package/dist/debugger.d.cts +171 -0
  28. package/dist/debugger.d.ts +171 -0
  29. package/dist/debugger.js +7 -0
  30. package/dist/debugger.js.map +1 -0
  31. package/dist/index.cjs +6803 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +4093 -0
  34. package/dist/index.d.ts +4093 -0
  35. package/dist/index.js +6715 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/parser.cjs +14 -0
  38. package/dist/parser.cjs.map +1 -0
  39. package/dist/parser.d.cts +172 -0
  40. package/dist/parser.d.ts +172 -0
  41. package/dist/parser.js +5 -0
  42. package/dist/parser.js.map +1 -0
  43. package/dist/runtime.cjs +14 -0
  44. package/dist/runtime.cjs.map +1 -0
  45. package/dist/runtime.d.cts +200 -0
  46. package/dist/runtime.d.ts +200 -0
  47. package/dist/runtime.js +5 -0
  48. package/dist/runtime.js.map +1 -0
  49. package/dist/type-checker.cjs +17 -0
  50. package/dist/type-checker.cjs.map +1 -0
  51. package/dist/type-checker.d.cts +105 -0
  52. package/dist/type-checker.d.ts +105 -0
  53. package/dist/type-checker.js +4 -0
  54. package/dist/type-checker.js.map +1 -0
  55. package/dist/types-4h8cbtF_.d.cts +329 -0
  56. package/dist/types-4h8cbtF_.d.ts +329 -0
  57. package/package.json +17 -13
  58. package/src/HoloScript2DParser.js +0 -227
  59. package/src/HoloScript2DParser.ts +0 -261
  60. package/src/HoloScriptCodeParser.js +0 -1102
  61. package/src/HoloScriptCodeParser.ts +0 -1188
  62. package/src/HoloScriptDebugger.js +0 -458
  63. package/src/HoloScriptParser.js +0 -338
  64. package/src/HoloScriptParser.ts +0 -397
  65. package/src/HoloScriptPlusParser.js +0 -371
  66. package/src/HoloScriptPlusParser.ts +0 -543
  67. package/src/HoloScriptRuntime.js +0 -1399
  68. package/src/HoloScriptRuntime.test.js +0 -351
  69. package/src/HoloScriptRuntime.test.ts +0 -436
  70. package/src/HoloScriptRuntime.ts +0 -1653
  71. package/src/HoloScriptTypeChecker.js +0 -356
  72. package/src/HoloScriptTypeChecker.ts +0 -475
  73. package/src/__tests__/GraphicsServices.test.js +0 -357
  74. package/src/__tests__/GraphicsServices.test.ts +0 -427
  75. package/src/__tests__/HoloScriptPlusParser.test.js +0 -317
  76. package/src/__tests__/HoloScriptPlusParser.test.ts +0 -392
  77. package/src/__tests__/integration.test.js +0 -336
  78. package/src/__tests__/integration.test.ts +0 -416
  79. package/src/__tests__/performance.bench.js +0 -218
  80. package/src/__tests__/performance.bench.ts +0 -262
  81. package/src/__tests__/type-checker.test.js +0 -60
  82. package/src/__tests__/type-checker.test.ts +0 -73
  83. package/src/index.js +0 -217
  84. package/src/index.ts +0 -426
  85. package/src/interop/Interoperability.js +0 -413
  86. package/src/interop/Interoperability.ts +0 -494
  87. package/src/logger.js +0 -42
  88. package/src/logger.ts +0 -57
  89. package/src/parser/EnhancedParser.js +0 -205
  90. package/src/parser/EnhancedParser.ts +0 -251
  91. package/src/parser/HoloScriptPlusParser.js +0 -928
  92. package/src/parser/HoloScriptPlusParser.ts +0 -1089
  93. package/src/runtime/HoloScriptPlusRuntime.js +0 -674
  94. package/src/runtime/HoloScriptPlusRuntime.ts +0 -861
  95. package/src/runtime/PerformanceTelemetry.js +0 -323
  96. package/src/runtime/PerformanceTelemetry.ts +0 -467
  97. package/src/runtime/RuntimeOptimization.js +0 -361
  98. package/src/runtime/RuntimeOptimization.ts +0 -416
  99. package/src/services/HololandGraphicsPipelineService.js +0 -506
  100. package/src/services/HololandGraphicsPipelineService.ts +0 -662
  101. package/src/services/PlatformPerformanceOptimizer.js +0 -356
  102. package/src/services/PlatformPerformanceOptimizer.ts +0 -503
  103. package/src/state/ReactiveState.js +0 -427
  104. package/src/state/ReactiveState.ts +0 -572
  105. package/src/tools/DeveloperExperience.js +0 -376
  106. package/src/tools/DeveloperExperience.ts +0 -438
  107. package/src/traits/AIDriverTrait.js +0 -322
  108. package/src/traits/AIDriverTrait.test.js +0 -329
  109. package/src/traits/AIDriverTrait.test.ts +0 -357
  110. package/src/traits/AIDriverTrait.ts +0 -474
  111. package/src/traits/LightingTrait.js +0 -313
  112. package/src/traits/LightingTrait.test.js +0 -410
  113. package/src/traits/LightingTrait.test.ts +0 -462
  114. package/src/traits/LightingTrait.ts +0 -505
  115. package/src/traits/MaterialTrait.js +0 -194
  116. package/src/traits/MaterialTrait.test.js +0 -286
  117. package/src/traits/MaterialTrait.test.ts +0 -329
  118. package/src/traits/MaterialTrait.ts +0 -324
  119. package/src/traits/RenderingTrait.js +0 -356
  120. package/src/traits/RenderingTrait.test.js +0 -363
  121. package/src/traits/RenderingTrait.test.ts +0 -427
  122. package/src/traits/RenderingTrait.ts +0 -555
  123. package/src/traits/VRTraitSystem.js +0 -740
  124. package/src/traits/VRTraitSystem.ts +0 -1040
  125. package/src/traits/VoiceInputTrait.js +0 -284
  126. package/src/traits/VoiceInputTrait.test.js +0 -226
  127. package/src/traits/VoiceInputTrait.test.ts +0 -252
  128. package/src/traits/VoiceInputTrait.ts +0 -401
  129. package/src/types/AdvancedTypeSystem.js +0 -226
  130. package/src/types/AdvancedTypeSystem.ts +0 -494
  131. package/src/types/HoloScriptPlus.d.ts +0 -853
  132. package/src/types.js +0 -6
  133. package/src/types.ts +0 -369
  134. package/tsconfig.json +0 -23
  135. package/tsup.config.d.ts +0 -2
  136. package/tsup.config.js +0 -18
  137. package/tsup.config.ts +0 -19
@@ -1,1653 +0,0 @@
1
- /**
2
- * HoloScript Runtime Engine
3
- *
4
- * Executes HoloScript AST in VR environment with spatial computation.
5
- * Supports:
6
- * - Orb creation and manipulation
7
- * - Function definition and invocation with arguments
8
- * - Connections and reactive data flow
9
- * - Gates (conditionals)
10
- * - Streams (data pipelines)
11
- * - 2D UI elements
12
- * - Built-in commands (show, hide, animate, pulse)
13
- * - Expression evaluation
14
- * - Event system
15
- */
16
-
17
- import { logger } from './logger';
18
- import type {
19
- ASTNode,
20
- OrbNode,
21
- MethodNode,
22
- ConnectionNode,
23
- GateNode,
24
- StreamNode,
25
- SpatialPosition,
26
- HologramProperties,
27
- HologramShape,
28
- RuntimeContext,
29
- ExecutionResult,
30
- ParticleSystem,
31
- TransformationNode,
32
- UI2DNode,
33
- } from './types';
34
- import type { ImportLoader } from './types';
35
-
36
- const RUNTIME_SECURITY_LIMITS = {
37
- maxExecutionDepth: 50,
38
- maxTotalNodes: 1000,
39
- maxExecutionTimeMs: 5000,
40
- maxParticlesPerSystem: 1000,
41
- maxStringLength: 10000,
42
- maxCallStackDepth: 100,
43
- };
44
-
45
- /**
46
- * Event handler type
47
- */
48
- type EventHandler = (data?: unknown) => void | Promise<void>;
49
-
50
- /**
51
- * Scope for variable resolution
52
- */
53
- interface Scope {
54
- variables: Map<string, unknown>;
55
- parent?: Scope;
56
- }
57
-
58
- /**
59
- * Animation state
60
- */
61
- interface Animation {
62
- target: string;
63
- property: string;
64
- from: number;
65
- to: number;
66
- duration: number;
67
- startTime: number;
68
- easing: string;
69
- loop?: boolean;
70
- yoyo?: boolean;
71
- }
72
-
73
- /**
74
- * UI Element state
75
- */
76
- interface UIElementState {
77
- type: string;
78
- name: string;
79
- properties: Record<string, unknown>;
80
- value?: unknown;
81
- visible: boolean;
82
- enabled: boolean;
83
- }
84
-
85
- export class HoloScriptRuntime {
86
- private context: RuntimeContext;
87
- private particleSystems: Map<string, ParticleSystem> = new Map();
88
- private executionHistory: ExecutionResult[] = [];
89
- private startTime: number = 0;
90
- private nodeCount: number = 0;
91
-
92
- // Enhanced runtime state
93
- private currentScope: Scope;
94
- private callStack: string[] = [];
95
- private eventHandlers: Map<string, EventHandler[]> = new Map();
96
- private animations: Map<string, Animation> = new Map();
97
- private uiElements: Map<string, UIElementState> = new Map();
98
- private builtinFunctions: Map<string, (args: unknown[]) => unknown>;
99
-
100
- constructor(_importLoader?: ImportLoader) {
101
- this.context = this.createEmptyContext();
102
- this.currentScope = { variables: this.context.variables };
103
- this.builtinFunctions = this.initBuiltins();
104
- }
105
-
106
- /**
107
- * Initialize built-in functions
108
- */
109
- private initBuiltins(): Map<string, (args: unknown[]) => unknown> {
110
- const builtins = new Map<string, (args: unknown[]) => unknown>();
111
-
112
- // Display commands
113
- builtins.set('show', (args) => {
114
- const target = String(args[0]);
115
- const element = this.uiElements.get(target);
116
- if (element) element.visible = true;
117
- const hologram = this.context.hologramState.get(target);
118
- if (hologram) {
119
- this.createParticleEffect(`${target}_show`, { x: 0, y: 0, z: 0 }, hologram.color, 15);
120
- }
121
- logger.info('show', { target });
122
- return { shown: target };
123
- });
124
-
125
- builtins.set('hide', (args) => {
126
- const target = String(args[0]);
127
- const element = this.uiElements.get(target);
128
- if (element) element.visible = false;
129
- logger.info('hide', { target });
130
- return { hidden: target };
131
- });
132
-
133
- // Animation commands
134
- builtins.set('pulse', (args) => {
135
- const target = String(args[0]);
136
- const options = (args[1] as Record<string, unknown>) || {};
137
- const duration = Number(options.duration) || 1000;
138
- const color = String(options.color || '#ffffff');
139
-
140
- const position = this.context.spatialMemory.get(target) || { x: 0, y: 0, z: 0 };
141
- this.createParticleEffect(`${target}_pulse`, position, color, 30);
142
-
143
- return { pulsing: target, duration };
144
- });
145
-
146
- builtins.set('animate', (args) => {
147
- const target = String(args[0]);
148
- const options = (args[1] as Record<string, unknown>) || {};
149
-
150
- const animation: Animation = {
151
- target,
152
- property: String(options.property || 'position.y'),
153
- from: Number(options.from || 0),
154
- to: Number(options.to || 1),
155
- duration: Number(options.duration || 1000),
156
- startTime: Date.now(),
157
- easing: String(options.easing || 'linear'),
158
- loop: Boolean(options.loop),
159
- yoyo: Boolean(options.yoyo),
160
- };
161
-
162
- this.animations.set(`${target}_${animation.property}`, animation);
163
- return { animating: target, animation };
164
- });
165
-
166
- // Spatial commands
167
- builtins.set('spawn', (args) => {
168
- const target = String(args[0]);
169
- const position = (args[1] as SpatialPosition) || { x: 0, y: 0, z: 0 };
170
-
171
- this.context.spatialMemory.set(target, position);
172
- this.createParticleEffect(`${target}_spawn`, position, '#00ff00', 25);
173
-
174
- return { spawned: target, at: position };
175
- });
176
-
177
- builtins.set('move', (args) => {
178
- const target = String(args[0]);
179
- const position = (args[1] as SpatialPosition) || { x: 0, y: 0, z: 0 };
180
-
181
- const current = this.context.spatialMemory.get(target);
182
- if (current) {
183
- this.context.spatialMemory.set(target, position);
184
- this.createConnectionStream(target, `${target}_dest`, current, position, 'move');
185
- }
186
-
187
- return { moved: target, to: position };
188
- });
189
-
190
- // Data commands
191
- builtins.set('set', (args) => {
192
- const target = String(args[0]);
193
- const value = args[1];
194
- this.setVariable(target, value);
195
- return { set: target, value };
196
- });
197
-
198
- builtins.set('get', (args) => {
199
- const target = String(args[0]);
200
- return this.getVariable(target);
201
- });
202
-
203
- // Math functions
204
- builtins.set('add', (args) => Number(args[0]) + Number(args[1]));
205
- builtins.set('subtract', (args) => Number(args[0]) - Number(args[1]));
206
- builtins.set('multiply', (args) => Number(args[0]) * Number(args[1]));
207
- builtins.set('divide', (args) => Number(args[1]) !== 0 ? Number(args[0]) / Number(args[1]) : 0);
208
- builtins.set('mod', (args) => Number(args[0]) % Number(args[1]));
209
- builtins.set('abs', (args) => Math.abs(Number(args[0])));
210
- builtins.set('floor', (args) => Math.floor(Number(args[0])));
211
- builtins.set('ceil', (args) => Math.ceil(Number(args[0])));
212
- builtins.set('round', (args) => Math.round(Number(args[0])));
213
- builtins.set('min', (args) => Math.min(...args.map(Number)));
214
- builtins.set('max', (args) => Math.max(...args.map(Number)));
215
- builtins.set('random', () => Math.random());
216
-
217
- // String functions
218
- builtins.set('concat', (args) => args.map(String).join(''));
219
- builtins.set('length', (args) => {
220
- const val = args[0];
221
- if (typeof val === 'string') return val.length;
222
- if (Array.isArray(val)) return val.length;
223
- return 0;
224
- });
225
- builtins.set('substring', (args) => String(args[0]).substring(Number(args[1]), Number(args[2])));
226
- builtins.set('uppercase', (args) => String(args[0]).toUpperCase());
227
- builtins.set('lowercase', (args) => String(args[0]).toLowerCase());
228
-
229
- // Array functions
230
- builtins.set('push', (args) => {
231
- const arr = args[0];
232
- if (Array.isArray(arr)) {
233
- arr.push(args[1]);
234
- return arr;
235
- }
236
- return [args[0], args[1]];
237
- });
238
- builtins.set('pop', (args) => {
239
- const arr = args[0];
240
- if (Array.isArray(arr)) return arr.pop();
241
- return undefined;
242
- });
243
- builtins.set('at', (args) => {
244
- const arr = args[0];
245
- const index = Number(args[1]);
246
- if (Array.isArray(arr)) return arr[index];
247
- return undefined;
248
- });
249
-
250
- // Console/Debug
251
- builtins.set('log', (args) => {
252
- logger.info('HoloScript log', { args });
253
- return args[0];
254
- });
255
- builtins.set('print', (args) => {
256
- const message = args.map(String).join(' ');
257
- logger.info('print', { message });
258
- return message;
259
- });
260
-
261
- // Type checking
262
- builtins.set('typeof', (args) => typeof args[0]);
263
- builtins.set('isArray', (args) => Array.isArray(args[0]));
264
- builtins.set('isNumber', (args) => typeof args[0] === 'number' && !isNaN(args[0] as number));
265
- builtins.set('isString', (args) => typeof args[0] === 'string');
266
-
267
- return builtins;
268
- }
269
-
270
- /**
271
- * Execute a single AST node
272
- */
273
- async executeNode(node: ASTNode): Promise<ExecutionResult> {
274
- const startTime = Date.now();
275
-
276
- try {
277
- this.context.executionStack.push(node);
278
-
279
- let result: ExecutionResult;
280
-
281
- switch (node.type) {
282
- case 'orb':
283
- result = await this.executeOrb(node as OrbNode);
284
- break;
285
- case 'method':
286
- case 'function':
287
- result = await this.executeFunction(node as MethodNode);
288
- break;
289
- case 'connection':
290
- result = await this.executeConnection(node as ConnectionNode);
291
- break;
292
- case 'gate':
293
- result = await this.executeGate(node as GateNode);
294
- break;
295
- case 'stream':
296
- result = await this.executeStream(node as StreamNode);
297
- break;
298
- case 'execute':
299
- case 'call':
300
- result = await this.executeCall(node as ASTNode & { target?: string; args?: unknown[] });
301
- break;
302
- case 'debug':
303
- result = await this.executeDebug(node);
304
- break;
305
- case 'visualize':
306
- result = await this.executeVisualize(node);
307
- break;
308
- case '2d-element':
309
- result = await this.executeUIElement(node as unknown as UI2DNode);
310
- break;
311
- case 'nexus':
312
- case 'building':
313
- result = await this.executeStructure(node);
314
- break;
315
- case 'assignment':
316
- result = await this.executeAssignment(node as ASTNode & { name: string; value: unknown });
317
- break;
318
- case 'return':
319
- result = await this.executeReturn(node as ASTNode & { value: unknown });
320
- break;
321
- case 'generic':
322
- result = await this.executeGeneric(node);
323
- break;
324
- case 'expression-statement':
325
- result = await this.executeCall(node);
326
- break;
327
- default:
328
- result = {
329
- success: false,
330
- error: `Unknown node type: ${node.type}`,
331
- executionTime: Date.now() - startTime,
332
- };
333
- }
334
-
335
- result.executionTime = Date.now() - startTime;
336
- this.executionHistory.push(result);
337
- this.context.executionStack.pop();
338
-
339
- return result;
340
- } catch (error) {
341
- const execTime = Date.now() - startTime;
342
- const errorResult: ExecutionResult = {
343
- success: false,
344
- error: error instanceof Error ? error.message : String(error),
345
- executionTime: execTime,
346
- };
347
-
348
- this.executionHistory.push(errorResult);
349
- this.context.executionStack.pop();
350
-
351
- return errorResult;
352
- }
353
- }
354
-
355
- /**
356
- * Execute multiple nodes in sequence
357
- */
358
- async executeProgram(nodes: ASTNode[], depth: number = 0): Promise<ExecutionResult[]> {
359
- if (depth === 0) {
360
- this.startTime = Date.now();
361
- this.nodeCount = 0;
362
- }
363
-
364
- if (depth > RUNTIME_SECURITY_LIMITS.maxExecutionDepth) {
365
- logger.error('Max execution depth exceeded', { depth });
366
- return [{
367
- success: false,
368
- error: `Max execution depth exceeded (${RUNTIME_SECURITY_LIMITS.maxExecutionDepth})`,
369
- executionTime: 0,
370
- }];
371
- }
372
-
373
- const results: ExecutionResult[] = [];
374
-
375
- for (const node of nodes) {
376
- this.nodeCount++;
377
- if (this.nodeCount > RUNTIME_SECURITY_LIMITS.maxTotalNodes) {
378
- logger.error('Max total nodes exceeded', { count: this.nodeCount });
379
- results.push({
380
- success: false,
381
- error: 'Max total nodes exceeded',
382
- executionTime: Date.now() - this.startTime,
383
- });
384
- break;
385
- }
386
-
387
- if (Date.now() - this.startTime > RUNTIME_SECURITY_LIMITS.maxExecutionTimeMs) {
388
- logger.error('Execution timeout', { duration: Date.now() - this.startTime });
389
- results.push({
390
- success: false,
391
- error: 'Execution timeout',
392
- executionTime: Date.now() - this.startTime,
393
- });
394
- break;
395
- }
396
-
397
- const result = await this.executeNode(node);
398
- results.push(result);
399
-
400
- // Stop on error (except visualize) or return statement
401
- if (!result.success && node.type !== 'visualize') {
402
- break;
403
- }
404
- if (node.type === 'return') {
405
- break;
406
- }
407
- }
408
-
409
- return results;
410
- }
411
-
412
- /**
413
- * Call a function with arguments
414
- */
415
- async callFunction(name: string, args: unknown[] = []): Promise<ExecutionResult> {
416
- // Check built-in functions first
417
- const builtin = this.builtinFunctions.get(name);
418
- if (builtin) {
419
- try {
420
- const result = builtin(args);
421
- return {
422
- success: true,
423
- output: result,
424
- };
425
- } catch (error) {
426
- return {
427
- success: false,
428
- error: `Built-in function ${name} failed: ${error}`,
429
- };
430
- }
431
- }
432
-
433
- // Check user-defined functions
434
- const func = this.context.functions.get(name);
435
- if (!func) {
436
- return {
437
- success: false,
438
- error: `Function '${name}' not found`,
439
- };
440
- }
441
-
442
- // Check call stack depth
443
- if (this.callStack.length >= RUNTIME_SECURITY_LIMITS.maxCallStackDepth) {
444
- return {
445
- success: false,
446
- error: `Max call stack depth exceeded (${RUNTIME_SECURITY_LIMITS.maxCallStackDepth})`,
447
- };
448
- }
449
-
450
- // Create new scope
451
- const parentScope = this.currentScope;
452
- this.currentScope = {
453
- variables: new Map(),
454
- parent: parentScope,
455
- };
456
-
457
- // Bind parameters
458
- func.parameters.forEach((param, index) => {
459
- const value = index < args.length ? args[index] : param.defaultValue;
460
- this.currentScope.variables.set(param.name, value);
461
- });
462
-
463
- // Push to call stack
464
- this.callStack.push(name);
465
-
466
- // Execute function body
467
- let returnValue: unknown = undefined;
468
- try {
469
- const results = await this.executeProgram(func.body, this.callStack.length);
470
- const lastResult = results[results.length - 1];
471
-
472
- if (lastResult?.output !== undefined) {
473
- returnValue = lastResult.output;
474
- }
475
-
476
- // Visual effect
477
- this.createExecutionEffect(name, func.position || { x: 0, y: 0, z: 0 });
478
-
479
- return {
480
- success: results.every(r => r.success),
481
- output: returnValue,
482
- hologram: func.hologram,
483
- spatialPosition: func.position,
484
- };
485
- } finally {
486
- // Restore scope
487
- this.currentScope = parentScope;
488
- this.callStack.pop();
489
- }
490
- }
491
-
492
- /**
493
- * Set a variable in current scope
494
- */
495
- setVariable(name: string, value: unknown): void {
496
- // Handle property access (e.g., "obj.prop")
497
- if (name.includes('.')) {
498
- const parts = name.split('.');
499
- const objName = parts[0];
500
- const propPath = parts.slice(1);
501
-
502
- let obj = this.getVariable(objName);
503
- if (obj === undefined || typeof obj !== 'object' || obj === null) {
504
- obj = {};
505
- this.currentScope.variables.set(objName, obj);
506
- }
507
-
508
- let current = obj as Record<string, unknown>;
509
- for (let i = 0; i < propPath.length - 1; i++) {
510
- if (current[propPath[i]] === undefined || typeof current[propPath[i]] !== 'object') {
511
- current[propPath[i]] = {};
512
- }
513
- current = current[propPath[i]] as Record<string, unknown>;
514
- }
515
- current[propPath[propPath.length - 1]] = value;
516
- } else {
517
- this.currentScope.variables.set(name, value);
518
- }
519
- }
520
-
521
- /**
522
- * Get a variable from scope chain
523
- */
524
- getVariable(name: string): unknown {
525
- // Handle property access (e.g., "obj.prop")
526
- if (name.includes('.')) {
527
- const parts = name.split('.');
528
- let value = this.getVariable(parts[0]);
529
-
530
- for (let i = 1; i < parts.length && value !== undefined; i++) {
531
- if (typeof value === 'object' && value !== null) {
532
- value = (value as Record<string, unknown>)[parts[i]];
533
- } else {
534
- return undefined;
535
- }
536
- }
537
- return value;
538
- }
539
-
540
- // Walk scope chain
541
- let scope: Scope | undefined = this.currentScope;
542
- while (scope) {
543
- if (scope.variables.has(name)) {
544
- return scope.variables.get(name);
545
- }
546
- scope = scope.parent;
547
- }
548
-
549
- // Check context variables
550
- if (this.context.variables.has(name)) {
551
- return this.context.variables.get(name);
552
- }
553
-
554
- // Fallback to functions map (for imported functions)
555
- if (this.context.functions.has(name)) {
556
- return this.context.functions.get(name);
557
- }
558
-
559
- return undefined;
560
- }
561
-
562
- /**
563
- * Evaluate an expression
564
- */
565
- evaluateExpression(expr: string): unknown {
566
- if (!expr || typeof expr !== 'string') return expr;
567
-
568
- expr = expr.trim();
569
-
570
- // Security check
571
- const suspicious = ['eval', 'process', 'require', '__proto__', 'constructor', 'Function'];
572
- if (suspicious.some(kw => expr.toLowerCase().includes(kw))) {
573
- logger.warn('Suspicious expression blocked', { expr });
574
- return undefined;
575
- }
576
-
577
- // String literal
578
- if ((expr.startsWith('"') && expr.endsWith('"')) || (expr.startsWith("'") && expr.endsWith("'"))) {
579
- return expr.slice(1, -1);
580
- }
581
-
582
- // Number literal
583
- if (/^-?\d+(\.\d+)?$/.test(expr)) {
584
- return parseFloat(expr);
585
- }
586
-
587
- // Boolean literal
588
- if (expr === 'true') return true;
589
- if (expr === 'false') return false;
590
- if (expr === 'null') return null;
591
- if (expr === 'undefined') return undefined;
592
-
593
- // Array literal [a, b, c]
594
- if (expr.startsWith('[') && expr.endsWith(']')) {
595
- const inner = expr.slice(1, -1);
596
- if (!inner.trim()) return [];
597
- const elements = this.splitByComma(inner);
598
- return elements.map(e => this.evaluateExpression(e.trim()));
599
- }
600
-
601
- // Object literal {a: 1, b: 2}
602
- if (expr.startsWith('{') && expr.endsWith('}')) {
603
- const inner = expr.slice(1, -1);
604
- if (!inner.trim()) return {};
605
- const pairs = this.splitByComma(inner);
606
- const obj: Record<string, unknown> = {};
607
- for (const pair of pairs) {
608
- const colonIndex = pair.indexOf(':');
609
- if (colonIndex > 0) {
610
- const key = pair.slice(0, colonIndex).trim();
611
- const value = pair.slice(colonIndex + 1).trim();
612
- obj[key] = this.evaluateExpression(value);
613
- }
614
- }
615
- return obj;
616
- }
617
-
618
- // Function call: name(args)
619
- const funcMatch = expr.match(/^(\w+)\s*\((.*)?\)$/);
620
- if (funcMatch) {
621
- const [, funcName, argsStr] = funcMatch;
622
- const args = argsStr ? this.splitByComma(argsStr).map(a => this.evaluateExpression(a.trim())) : [];
623
-
624
- // Check builtins
625
- const builtin = this.builtinFunctions.get(funcName);
626
- if (builtin) {
627
- return builtin(args);
628
- }
629
-
630
- // Check user functions (but don't execute - just reference)
631
- if (this.context.functions.has(funcName)) {
632
- // For async execution, return a promise marker
633
- return { __holoCall: funcName, args };
634
- }
635
-
636
- return undefined;
637
- }
638
-
639
- // Binary operations: a + b, a - b, etc.
640
- const binaryOps = [
641
- { pattern: /(.+)\s*\+\s*(.+)/, op: (a: unknown, b: unknown) => (typeof a === 'string' || typeof b === 'string') ? String(a) + String(b) : Number(a) + Number(b) },
642
- { pattern: /(.+)\s*-\s*(.+)/, op: (a: unknown, b: unknown) => Number(a) - Number(b) },
643
- { pattern: /(.+)\s*\*\s*(.+)/, op: (a: unknown, b: unknown) => Number(a) * Number(b) },
644
- { pattern: /(.+)\s*\/\s*(.+)/, op: (a: unknown, b: unknown) => Number(b) !== 0 ? Number(a) / Number(b) : 0 },
645
- { pattern: /(.+)\s*%\s*(.+)/, op: (a: unknown, b: unknown) => Number(a) % Number(b) },
646
- ];
647
-
648
- for (const { pattern, op } of binaryOps) {
649
- const match = expr.match(pattern);
650
- if (match) {
651
- const left = this.evaluateExpression(match[1]);
652
- const right = this.evaluateExpression(match[2]);
653
- return op(left, right);
654
- }
655
- }
656
-
657
- // Variable reference
658
- return this.getVariable(expr);
659
- }
660
-
661
- /**
662
- * Split string by comma, respecting nesting
663
- */
664
- private splitByComma(str: string): string[] {
665
- const parts: string[] = [];
666
- let current = '';
667
- let depth = 0;
668
- let inString = false;
669
- let stringChar = '';
670
-
671
- for (let i = 0; i < str.length; i++) {
672
- const char = str[i];
673
-
674
- if (!inString && (char === '"' || char === "'")) {
675
- inString = true;
676
- stringChar = char;
677
- } else if (inString && char === stringChar && str[i - 1] !== '\\') {
678
- inString = false;
679
- }
680
-
681
- if (!inString) {
682
- if (char === '(' || char === '[' || char === '{') depth++;
683
- if (char === ')' || char === ']' || char === '}') depth--;
684
-
685
- if (char === ',' && depth === 0) {
686
- parts.push(current.trim());
687
- current = '';
688
- continue;
689
- }
690
- }
691
-
692
- current += char;
693
- }
694
-
695
- if (current.trim()) {
696
- parts.push(current.trim());
697
- }
698
-
699
- return parts;
700
- }
701
-
702
- // ============================================================================
703
- // Node Executors
704
- // ============================================================================
705
-
706
- private async executeOrb(node: OrbNode): Promise<ExecutionResult> {
707
- if (node.position) {
708
- this.context.spatialMemory.set(node.name, node.position);
709
- }
710
-
711
- // Create orb object with reactive properties
712
- const orbData = {
713
- __type: 'orb',
714
- name: node.name,
715
- properties: { ...node.properties },
716
- position: node.position || { x: 0, y: 0, z: 0 },
717
- hologram: node.hologram,
718
- created: Date.now(),
719
- // Methods bound to this orb
720
- show: () => this.builtinFunctions.get('show')!([node.name]),
721
- hide: () => this.builtinFunctions.get('hide')!([node.name]),
722
- pulse: (opts?: Record<string, unknown>) => this.builtinFunctions.get('pulse')!([node.name, opts]),
723
- };
724
-
725
- this.context.variables.set(node.name, orbData);
726
-
727
- if (node.hologram) {
728
- this.context.hologramState.set(node.name, node.hologram);
729
- }
730
-
731
- this.createParticleEffect(`${node.name}_creation`, node.position || { x: 0, y: 0, z: 0 }, '#00ffff', 20);
732
-
733
- logger.info('Orb created', { name: node.name, properties: Object.keys(node.properties) });
734
-
735
- return {
736
- success: true,
737
- output: orbData,
738
- hologram: node.hologram,
739
- spatialPosition: node.position,
740
- };
741
- }
742
-
743
- private async executeFunction(node: MethodNode): Promise<ExecutionResult> {
744
- this.context.functions.set(node.name, node);
745
-
746
- const hologram: HologramProperties = {
747
- shape: 'cube',
748
- color: '#ff6b35',
749
- size: 1.5,
750
- glow: true,
751
- interactive: true,
752
- ...node.hologram,
753
- };
754
-
755
- this.context.hologramState.set(node.name, hologram);
756
-
757
- logger.info('Function defined', { name: node.name, params: node.parameters.map(p => p.name) });
758
-
759
- return {
760
- success: true,
761
- output: `Function '${node.name}' defined with ${node.parameters.length} parameter(s)`,
762
- hologram,
763
- spatialPosition: node.position,
764
- };
765
- }
766
-
767
- private async executeConnection(node: ConnectionNode): Promise<ExecutionResult> {
768
- this.context.connections.push(node);
769
-
770
- const fromPos = this.context.spatialMemory.get(node.from);
771
- const toPos = this.context.spatialMemory.get(node.to);
772
-
773
- if (fromPos && toPos) {
774
- this.createConnectionStream(node.from, node.to, fromPos, toPos, node.dataType);
775
- }
776
-
777
- // Set up reactive binding if bidirectional
778
- if (node.bidirectional) {
779
- // When 'from' changes, update 'to'
780
- this.on(`${node.from}.changed`, async (data) => {
781
- this.setVariable(node.to, data);
782
- this.emit(`${node.to}.changed`, data);
783
- });
784
- // When 'to' changes, update 'from'
785
- this.on(`${node.to}.changed`, async (data) => {
786
- this.setVariable(node.from, data);
787
- this.emit(`${node.from}.changed`, data);
788
- });
789
- }
790
-
791
- logger.info('Connection created', { from: node.from, to: node.to, dataType: node.dataType });
792
-
793
- return {
794
- success: true,
795
- output: `Connected '${node.from}' to '${node.to}' (${node.dataType})`,
796
- hologram: {
797
- shape: 'cylinder',
798
- color: this.getDataTypeColor(node.dataType),
799
- size: 0.1,
800
- glow: true,
801
- interactive: false,
802
- },
803
- };
804
- }
805
-
806
- private async executeGate(node: GateNode): Promise<ExecutionResult> {
807
- try {
808
- const condition = this.evaluateCondition(node.condition);
809
- const path = condition ? node.truePath : node.falsePath;
810
-
811
- logger.info('Gate evaluation', { condition: node.condition, result: condition });
812
-
813
- if (path.length > 0) {
814
- await this.executeProgram(path, this.callStack.length + 1);
815
- }
816
-
817
- return {
818
- success: true,
819
- output: `Gate: took ${condition ? 'true' : 'false'} path`,
820
- hologram: {
821
- shape: 'pyramid',
822
- color: condition ? '#00ff00' : '#ff0000',
823
- size: 1,
824
- glow: true,
825
- interactive: true,
826
- },
827
- };
828
- } catch (error) {
829
- return {
830
- success: false,
831
- error: `Gate execution failed: ${error}`,
832
- };
833
- }
834
- }
835
-
836
- private async executeStream(node: StreamNode): Promise<ExecutionResult> {
837
- let data = this.getVariable(node.source);
838
-
839
- logger.info('Stream processing', { name: node.name, source: node.source, transforms: node.transformations.length });
840
-
841
- for (const transform of node.transformations) {
842
- data = await this.applyTransformation(data, transform);
843
- }
844
-
845
- this.setVariable(`${node.name}_result`, data);
846
- this.createFlowingStream(node.name, node.position || { x: 0, y: 0, z: 0 }, data);
847
-
848
- return {
849
- success: true,
850
- output: `Stream '${node.name}' processed ${Array.isArray(data) ? data.length : 1} item(s)`,
851
- hologram: node.hologram,
852
- spatialPosition: node.position,
853
- };
854
- }
855
-
856
- private async executeCall(node: ASTNode & { target?: string; args?: unknown[] }): Promise<ExecutionResult> {
857
- const funcName = node.target || '';
858
- const args = node.args || [];
859
-
860
- return this.callFunction(funcName, args);
861
- }
862
-
863
- private async executeDebug(node: ASTNode & { target?: string }): Promise<ExecutionResult> {
864
- const debugInfo = {
865
- variables: Object.fromEntries(this.currentScope.variables),
866
- contextVariables: Object.fromEntries(this.context.variables),
867
- functions: Array.from(this.context.functions.keys()),
868
- connections: this.context.connections.length,
869
- callStack: [...this.callStack],
870
- uiElements: Array.from(this.uiElements.keys()),
871
- animations: Array.from(this.animations.keys()),
872
- executionHistory: this.executionHistory.slice(-10),
873
- };
874
-
875
- const debugOrb: HologramProperties = {
876
- shape: 'pyramid',
877
- color: '#ff1493',
878
- size: 0.8,
879
- glow: true,
880
- interactive: true,
881
- };
882
-
883
- this.context.hologramState.set(`debug_${node.target || 'program'}`, debugOrb);
884
-
885
- logger.info('Debug info', debugInfo);
886
-
887
- return {
888
- success: true,
889
- output: debugInfo,
890
- hologram: debugOrb,
891
- };
892
- }
893
-
894
- private async executeVisualize(node: ASTNode & { target?: string }): Promise<ExecutionResult> {
895
- const target = node.target || '';
896
- const data = this.getVariable(target);
897
-
898
- if (data === undefined) {
899
- return {
900
- success: false,
901
- error: `No data found for '${target}'`,
902
- };
903
- }
904
-
905
- const visHologram: HologramProperties = {
906
- shape: 'cylinder',
907
- color: '#32cd32',
908
- size: 1.5,
909
- glow: true,
910
- interactive: true,
911
- };
912
-
913
- this.createDataVisualization(target, data, node.position || { x: 0, y: 0, z: 0 });
914
-
915
- return {
916
- success: true,
917
- output: { visualizing: target, data },
918
- hologram: visHologram,
919
- };
920
- }
921
-
922
- private async executeUIElement(node: UI2DNode): Promise<ExecutionResult> {
923
- const element: UIElementState = {
924
- type: node.elementType,
925
- name: node.name,
926
- properties: { ...node.properties },
927
- visible: true,
928
- enabled: true,
929
- };
930
-
931
- // Set initial value based on element type
932
- if (node.elementType === 'textinput') {
933
- element.value = node.properties.value || '';
934
- } else if (node.elementType === 'slider') {
935
- element.value = node.properties.value || node.properties.min || 0;
936
- } else if (node.elementType === 'toggle') {
937
- element.value = node.properties.checked || false;
938
- }
939
-
940
- this.uiElements.set(node.name, element);
941
-
942
- // Register event handlers
943
- if (node.events) {
944
- for (const [eventName, handlerName] of Object.entries(node.events)) {
945
- this.on(`${node.name}.${eventName}`, async () => {
946
- await this.callFunction(handlerName);
947
- });
948
- }
949
- }
950
-
951
- logger.info('UI element created', { type: node.elementType, name: node.name });
952
-
953
- return {
954
- success: true,
955
- output: element,
956
- };
957
- }
958
-
959
- /**
960
- * Execute generic voice commands
961
- * Handles commands like: show, hide, animate, pulse, create
962
- */
963
- private async executeGeneric(_node: ASTNode): Promise<ExecutionResult> {
964
- const genericNode = _node as any;
965
- const command = String(genericNode.command || '').trim().toLowerCase();
966
- const tokens = command.split(/\s+/);
967
- const action = tokens[0];
968
- const target = tokens[1];
969
-
970
- logger.info('Executing generic command', { command, action, target });
971
-
972
- try {
973
- let result: any;
974
-
975
- switch (action) {
976
- case 'show':
977
- result = await this.executeShowCommand(target, genericNode);
978
- break;
979
- case 'hide':
980
- result = await this.executeHideCommand(target, genericNode);
981
- break;
982
- case 'create':
983
- case 'summon':
984
- result = await this.executeCreateCommand(tokens.slice(1), genericNode);
985
- break;
986
- case 'animate':
987
- result = await this.executeAnimateCommand(target, tokens.slice(2), genericNode);
988
- break;
989
- case 'pulse':
990
- result = await this.executePulseCommand(target, tokens.slice(2), genericNode);
991
- break;
992
- case 'move':
993
- result = await this.executeMoveCommand(target, tokens.slice(2), genericNode);
994
- break;
995
- case 'delete':
996
- case 'remove':
997
- result = await this.executeDeleteCommand(target, genericNode);
998
- break;
999
- default:
1000
- // Default: create visual representation of the generic command
1001
- logger.warn('Unknown voice command action', { action, command });
1002
- result = {
1003
- executed: false,
1004
- message: `Unknown command: ${action}`,
1005
- };
1006
- }
1007
-
1008
- return {
1009
- success: true,
1010
- output: result,
1011
- };
1012
- } catch (error) {
1013
- return {
1014
- success: false,
1015
- error: `Generic command execution failed: ${String(error)}`,
1016
- };
1017
- }
1018
- }
1019
-
1020
- /**
1021
- * Execute 'show' command
1022
- */
1023
- private async executeShowCommand(target: string, _node: any): Promise<any> {
1024
- // Create or show orb for this target
1025
- const hologram = _node.hologram || {
1026
- shape: 'orb',
1027
- color: '#00ffff',
1028
- size: 0.8,
1029
- glow: true,
1030
- interactive: true,
1031
- };
1032
-
1033
- const position = _node.position || { x: 0, y: 0, z: 0 };
1034
- this.context.spatialMemory.set(target, position);
1035
- this.createParticleEffect(`${target}_show`, position, hologram.color, 15);
1036
-
1037
- logger.info('Show command executed', { target, position });
1038
-
1039
- return {
1040
- showed: target,
1041
- hologram,
1042
- position,
1043
- };
1044
- }
1045
-
1046
- /**
1047
- * Execute 'hide' command
1048
- */
1049
- private async executeHideCommand(target: string, _node: any): Promise<any> {
1050
- const position = this.context.spatialMemory.get(target) || { x: 0, y: 0, z: 0 };
1051
- this.createParticleEffect(`${target}_hide`, position, '#ff0000', 10);
1052
-
1053
- logger.info('Hide command executed', { target });
1054
-
1055
- return {
1056
- hidden: target,
1057
- };
1058
- }
1059
-
1060
- /**
1061
- * Execute 'create' command
1062
- */
1063
- private async executeCreateCommand(tokens: string[], _node: any): Promise<any> {
1064
- if (tokens.length < 2) {
1065
- return { error: 'Create command requires shape and name' };
1066
- }
1067
-
1068
- const shape = tokens[0];
1069
- const name = tokens[1];
1070
- const position = _node.position || { x: 0, y: 0, z: 0 };
1071
-
1072
- const hologram: HologramProperties = {
1073
- shape: shape as HologramShape,
1074
- color: _node.hologram?.color || '#00ffff',
1075
- size: _node.hologram?.size || 1,
1076
- glow: _node.hologram?.glow !== false,
1077
- interactive: _node.hologram?.interactive !== false,
1078
- };
1079
-
1080
- this.context.spatialMemory.set(name, position);
1081
- this.createParticleEffect(`${name}_create`, position, hologram.color, 20);
1082
-
1083
- logger.info('Create command executed', { shape, name, position });
1084
-
1085
- return {
1086
- created: name,
1087
- shape,
1088
- hologram,
1089
- position,
1090
- };
1091
- }
1092
-
1093
- /**
1094
- * Execute 'animate' command
1095
- */
1096
- private async executeAnimateCommand(target: string, tokens: string[], _node: any): Promise<any> {
1097
- const property = tokens[0] || 'position.y';
1098
- const duration = parseInt(tokens[1] || '1000', 10);
1099
-
1100
- const animation: Animation = {
1101
- target,
1102
- property,
1103
- from: 0,
1104
- to: 1,
1105
- duration,
1106
- startTime: Date.now(),
1107
- easing: 'ease-in-out',
1108
- };
1109
-
1110
- this.animations.set(`${target}_${property}`, animation);
1111
-
1112
- logger.info('Animate command executed', { target, property, duration });
1113
-
1114
- return {
1115
- animating: target,
1116
- animation,
1117
- };
1118
- }
1119
-
1120
- /**
1121
- * Execute 'pulse' command
1122
- */
1123
- private async executePulseCommand(target: string, tokens: string[], _node: any): Promise<any> {
1124
- const duration = parseInt(tokens[0] || '500', 10);
1125
- const position = this.context.spatialMemory.get(target) || { x: 0, y: 0, z: 0 };
1126
-
1127
- // Create pulsing particle effect
1128
- this.createParticleEffect(`${target}_pulse`, position, '#ffff00', 30);
1129
-
1130
- // Create animation for scale
1131
- const animation: Animation = {
1132
- target,
1133
- property: 'scale',
1134
- from: 1,
1135
- to: 1.5,
1136
- duration,
1137
- startTime: Date.now(),
1138
- easing: 'sine',
1139
- yoyo: true,
1140
- loop: true,
1141
- };
1142
-
1143
- this.animations.set(`${target}_pulse`, animation);
1144
-
1145
- logger.info('Pulse command executed', { target, duration });
1146
-
1147
- return {
1148
- pulsing: target,
1149
- duration,
1150
- };
1151
- }
1152
-
1153
- /**
1154
- * Execute 'move' command
1155
- */
1156
- private async executeMoveCommand(target: string, tokens: string[], _node: any): Promise<any> {
1157
- const x = parseFloat(tokens[0] || '0');
1158
- const y = parseFloat(tokens[1] || '0');
1159
- const z = parseFloat(tokens[2] || '0');
1160
- const position: SpatialPosition = { x, y, z };
1161
-
1162
- const current = this.context.spatialMemory.get(target);
1163
- if (current) {
1164
- this.context.spatialMemory.set(target, position);
1165
- this.createConnectionStream(target, `${target}_move`, current, position, 'movement');
1166
- } else {
1167
- this.context.spatialMemory.set(target, position);
1168
- }
1169
-
1170
- logger.info('Move command executed', { target, position });
1171
-
1172
- return {
1173
- moved: target,
1174
- to: position,
1175
- };
1176
- }
1177
-
1178
- /**
1179
- * Execute 'delete' command
1180
- */
1181
- private async executeDeleteCommand(target: string, _node: any): Promise<any> {
1182
- const position = this.context.spatialMemory.get(target);
1183
- if (position) {
1184
- this.createParticleEffect(`${target}_delete`, position, '#ff0000', 15);
1185
- this.context.spatialMemory.delete(target);
1186
- }
1187
-
1188
- logger.info('Delete command executed', { target });
1189
-
1190
- return {
1191
- deleted: target,
1192
- };
1193
- }
1194
-
1195
- private async executeStructure(node: ASTNode): Promise<ExecutionResult> {
1196
- // Handle nexus, building, and other structural elements
1197
- const hologram: HologramProperties = node.hologram || {
1198
- shape: node.type === 'nexus' ? 'sphere' : 'cube',
1199
- color: node.type === 'nexus' ? '#9b59b6' : '#e74c3c',
1200
- size: node.type === 'nexus' ? 3 : 4,
1201
- glow: true,
1202
- interactive: true,
1203
- };
1204
-
1205
- return {
1206
- success: true,
1207
- output: { type: node.type, created: true },
1208
- hologram,
1209
- spatialPosition: node.position,
1210
- };
1211
- }
1212
-
1213
- private async executeAssignment(node: ASTNode & { name: string; value: unknown }): Promise<ExecutionResult> {
1214
- const value = this.evaluateExpression(String(node.value));
1215
- this.setVariable(node.name, value);
1216
-
1217
- return {
1218
- success: true,
1219
- output: { assigned: node.name, value },
1220
- };
1221
- }
1222
-
1223
- private async executeReturn(node: ASTNode & { value: unknown }): Promise<ExecutionResult> {
1224
- const value = this.evaluateExpression(String(node.value));
1225
-
1226
- return {
1227
- success: true,
1228
- output: value,
1229
- };
1230
- }
1231
-
1232
- // ============================================================================
1233
- // Condition Evaluation
1234
- // ============================================================================
1235
-
1236
- private evaluateCondition(condition: string): boolean {
1237
- if (!condition) return false;
1238
-
1239
- const suspiciousKeywords = ['eval', 'process', 'require', '__proto__', 'constructor'];
1240
- if (suspiciousKeywords.some(kw => condition.toLowerCase().includes(kw))) {
1241
- logger.warn('Suspicious condition blocked', { condition });
1242
- return false;
1243
- }
1244
-
1245
- try {
1246
- // Boolean literals
1247
- if (condition.trim().toLowerCase() === 'true') return true;
1248
- if (condition.trim().toLowerCase() === 'false') return false;
1249
-
1250
- // Comparison operators
1251
- const comparisonPatterns: Array<{ regex: RegExp; logical?: string }> = [
1252
- { regex: /^(.+?)\s*(===|!==)\s*(.+)$/ },
1253
- { regex: /^(.+?)\s*(==|!=|>=|<=|>|<)\s*(.+)$/ },
1254
- { regex: /^(.+?)\s*(&&)\s*(.+)$/, logical: 'and' },
1255
- { regex: /^(.+?)\s*(\|\|)\s*(.+)$/, logical: 'or' },
1256
- ];
1257
-
1258
- for (const { regex, logical } of comparisonPatterns) {
1259
- const match = condition.match(regex);
1260
- if (match) {
1261
- const [, leftExpr, operator, rightExpr] = match;
1262
- const left = this.evaluateExpression(leftExpr.trim());
1263
- const right = this.evaluateExpression(rightExpr.trim());
1264
-
1265
- if (logical === 'and') return Boolean(left) && Boolean(right);
1266
- if (logical === 'or') return Boolean(left) || Boolean(right);
1267
-
1268
- switch (operator) {
1269
- case '===': return left === right;
1270
- case '!==': return left !== right;
1271
- case '==': return left == right;
1272
- case '!=': return left != right;
1273
- case '>=': return Number(left) >= Number(right);
1274
- case '<=': return Number(left) <= Number(right);
1275
- case '>': return Number(left) > Number(right);
1276
- case '<': return Number(left) < Number(right);
1277
- }
1278
- }
1279
- }
1280
-
1281
- // Negation
1282
- if (condition.startsWith('!')) {
1283
- return !this.evaluateCondition(condition.slice(1).trim());
1284
- }
1285
-
1286
- // Variable truthiness
1287
- const value = this.evaluateExpression(condition.trim());
1288
- return Boolean(value);
1289
- } catch (error) {
1290
- logger.error('Condition evaluation error', { condition, error });
1291
- return false;
1292
- }
1293
- }
1294
-
1295
- // ============================================================================
1296
- // Transformation
1297
- // ============================================================================
1298
-
1299
- private async applyTransformation(data: unknown, transform: TransformationNode): Promise<unknown> {
1300
- const params = transform.parameters || {};
1301
-
1302
- switch (transform.operation) {
1303
- case 'filter': {
1304
- if (!Array.isArray(data)) return data;
1305
- const predicate = params.predicate as string;
1306
- if (predicate) {
1307
- return data.filter(item => {
1308
- this.setVariable('_item', item);
1309
- return this.evaluateCondition(predicate);
1310
- });
1311
- }
1312
- return data.filter(item => item !== null && item !== undefined);
1313
- }
1314
-
1315
- case 'map': {
1316
- if (!Array.isArray(data)) return data;
1317
- const mapper = params.mapper as string;
1318
- if (mapper) {
1319
- return data.map(item => {
1320
- this.setVariable('_item', item);
1321
- return this.evaluateExpression(mapper);
1322
- });
1323
- }
1324
- return data.map(item => ({ value: item, processed: true }));
1325
- }
1326
-
1327
- case 'reduce': {
1328
- if (!Array.isArray(data)) return data;
1329
- const initial = params.initial ?? 0;
1330
- const reducer = params.reducer as string;
1331
- if (reducer) {
1332
- return data.reduce((acc, item) => {
1333
- this.setVariable('_acc', acc);
1334
- this.setVariable('_item', item);
1335
- return this.evaluateExpression(reducer);
1336
- }, initial);
1337
- }
1338
- return data.reduce((acc, item) => acc + (typeof item === 'number' ? item : 0), 0);
1339
- }
1340
-
1341
- case 'sort': {
1342
- if (!Array.isArray(data)) return data;
1343
- const key = params.key as string;
1344
- const desc = params.descending as boolean;
1345
- const sorted = [...data].sort((a, b) => {
1346
- const aVal = key ? (a as Record<string, unknown>)[key] : a;
1347
- const bVal = key ? (b as Record<string, unknown>)[key] : b;
1348
- if (aVal < bVal) return desc ? 1 : -1;
1349
- if (aVal > bVal) return desc ? -1 : 1;
1350
- return 0;
1351
- });
1352
- return sorted;
1353
- }
1354
-
1355
- case 'sum':
1356
- return Array.isArray(data) ? data.reduce((sum, item) => sum + (typeof item === 'number' ? item : 0), 0) : data;
1357
-
1358
- case 'count':
1359
- return Array.isArray(data) ? data.length : 1;
1360
-
1361
- case 'unique':
1362
- return Array.isArray(data) ? [...new Set(data)] : data;
1363
-
1364
- case 'flatten':
1365
- return Array.isArray(data) ? data.flat() : data;
1366
-
1367
- case 'reverse':
1368
- return Array.isArray(data) ? [...data].reverse() : data;
1369
-
1370
- case 'take': {
1371
- if (!Array.isArray(data)) return data;
1372
- const count = Number(params.count) || 10;
1373
- return data.slice(0, count);
1374
- }
1375
-
1376
- case 'skip': {
1377
- if (!Array.isArray(data)) return data;
1378
- const count = Number(params.count) || 0;
1379
- return data.slice(count);
1380
- }
1381
-
1382
- default:
1383
- logger.warn('Unknown transformation', { operation: transform.operation });
1384
- return data;
1385
- }
1386
- }
1387
-
1388
- // ============================================================================
1389
- // Event System
1390
- // ============================================================================
1391
-
1392
- /**
1393
- * Register event handler
1394
- */
1395
- on(event: string, handler: EventHandler): void {
1396
- const handlers = this.eventHandlers.get(event) || [];
1397
- handlers.push(handler);
1398
- this.eventHandlers.set(event, handlers);
1399
- }
1400
-
1401
- /**
1402
- * Remove event handler
1403
- */
1404
- off(event: string, handler?: EventHandler): void {
1405
- if (!handler) {
1406
- this.eventHandlers.delete(event);
1407
- } else {
1408
- const handlers = this.eventHandlers.get(event) || [];
1409
- this.eventHandlers.set(event, handlers.filter(h => h !== handler));
1410
- }
1411
- }
1412
-
1413
- /**
1414
- * Emit event
1415
- */
1416
- async emit(event: string, data?: unknown): Promise<void> {
1417
- const handlers = this.eventHandlers.get(event) || [];
1418
- for (const handler of handlers) {
1419
- try {
1420
- await handler(data);
1421
- } catch (error) {
1422
- logger.error('Event handler error', { event, error });
1423
- }
1424
- }
1425
- }
1426
-
1427
- /**
1428
- * Trigger UI event
1429
- */
1430
- async triggerUIEvent(elementName: string, eventType: string, data?: unknown): Promise<void> {
1431
- const element = this.uiElements.get(elementName);
1432
- if (!element) {
1433
- logger.warn('UI element not found', { elementName });
1434
- return;
1435
- }
1436
-
1437
- // Update element state based on event
1438
- if (eventType === 'change' && data !== undefined) {
1439
- element.value = data;
1440
- }
1441
-
1442
- await this.emit(`${elementName}.${eventType}`, data);
1443
- }
1444
-
1445
- // ============================================================================
1446
- // Animation System
1447
- // ============================================================================
1448
-
1449
- /**
1450
- * Update all animations
1451
- */
1452
- updateAnimations(): void {
1453
- const now = Date.now();
1454
-
1455
- for (const [key, anim] of this.animations) {
1456
- const elapsed = now - anim.startTime;
1457
- let progress = Math.min(elapsed / anim.duration, 1);
1458
-
1459
- // Apply easing
1460
- progress = this.applyEasing(progress, anim.easing);
1461
-
1462
- // Calculate current value
1463
- let currentValue = anim.from + (anim.to - anim.from) * progress;
1464
-
1465
- // Handle yoyo
1466
- if (anim.yoyo && progress >= 1) {
1467
- anim.startTime = now;
1468
- [anim.from, anim.to] = [anim.to, anim.from];
1469
- }
1470
-
1471
- // Update the property
1472
- this.setVariable(`${anim.target}.${anim.property}`, currentValue);
1473
-
1474
- // Remove completed non-looping animations
1475
- if (progress >= 1 && !anim.loop && !anim.yoyo) {
1476
- this.animations.delete(key);
1477
- } else if (progress >= 1 && anim.loop) {
1478
- anim.startTime = now;
1479
- }
1480
- }
1481
- }
1482
-
1483
- private applyEasing(t: number, easing: string): number {
1484
- switch (easing) {
1485
- case 'easeIn':
1486
- return t * t;
1487
- case 'easeOut':
1488
- return t * (2 - t);
1489
- case 'easeInOut':
1490
- return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
1491
- case 'easeInQuad':
1492
- return t * t;
1493
- case 'easeOutQuad':
1494
- return t * (2 - t);
1495
- case 'easeInOutQuad':
1496
- return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
1497
- case 'linear':
1498
- default:
1499
- return t;
1500
- }
1501
- }
1502
-
1503
- // ============================================================================
1504
- // Particle Effects
1505
- // ============================================================================
1506
-
1507
- private createParticleEffect(name: string, position: SpatialPosition, color: string, count: number): void {
1508
- const limitedCount = Math.min(count, RUNTIME_SECURITY_LIMITS.maxParticlesPerSystem);
1509
- const particles: SpatialPosition[] = [];
1510
-
1511
- for (let i = 0; i < limitedCount; i++) {
1512
- particles.push({
1513
- x: position.x + (Math.random() - 0.5) * 2,
1514
- y: position.y + (Math.random() - 0.5) * 2,
1515
- z: position.z + (Math.random() - 0.5) * 2,
1516
- });
1517
- }
1518
-
1519
- this.particleSystems.set(name, {
1520
- particles,
1521
- color,
1522
- lifetime: 3000,
1523
- speed: 0.01,
1524
- });
1525
- }
1526
-
1527
- private createConnectionStream(from: string, to: string, fromPos: SpatialPosition, toPos: SpatialPosition, dataType: string): void {
1528
- const streamName = `connection_${from}_${to}`;
1529
- const particles: SpatialPosition[] = [];
1530
- const steps = 20;
1531
-
1532
- for (let i = 0; i <= steps; i++) {
1533
- const t = i / steps;
1534
- particles.push({
1535
- x: fromPos.x + (toPos.x - fromPos.x) * t,
1536
- y: fromPos.y + (toPos.y - fromPos.y) * t,
1537
- z: fromPos.z + (toPos.z - fromPos.z) * t,
1538
- });
1539
- }
1540
-
1541
- this.particleSystems.set(streamName, {
1542
- particles,
1543
- color: this.getDataTypeColor(dataType),
1544
- lifetime: 5000,
1545
- speed: 0.02,
1546
- });
1547
- }
1548
-
1549
- private createFlowingStream(name: string, position: SpatialPosition, data: unknown): void {
1550
- const count = Array.isArray(data) ? Math.min(data.length, 50) : 10;
1551
- this.createParticleEffect(`${name}_flow`, position, '#45b7d1', count);
1552
- }
1553
-
1554
- private createExecutionEffect(name: string, position: SpatialPosition): void {
1555
- this.createParticleEffect(`${name}_execution`, position, '#ff4500', 30);
1556
- }
1557
-
1558
- private createDataVisualization(name: string, data: unknown, position: SpatialPosition): void {
1559
- let count = 10;
1560
- if (Array.isArray(data)) {
1561
- count = Math.min(data.length, 100);
1562
- } else if (typeof data === 'object' && data !== null) {
1563
- count = Math.min(Object.keys(data).length * 5, 50);
1564
- }
1565
- this.createParticleEffect(`${name}_visualization`, position, '#32cd32', count);
1566
- }
1567
-
1568
- private getDataTypeColor(dataType: string): string {
1569
- const colors: Record<string, string> = {
1570
- 'string': '#ff6b35',
1571
- 'number': '#4ecdc4',
1572
- 'boolean': '#45b7d1',
1573
- 'object': '#96ceb4',
1574
- 'array': '#ffeaa7',
1575
- 'any': '#dda0dd',
1576
- 'move': '#ff69b4',
1577
- };
1578
- return colors[dataType] || '#ffffff';
1579
- }
1580
-
1581
- // ============================================================================
1582
- // Public API
1583
- // ============================================================================
1584
-
1585
- getParticleSystems(): Map<string, ParticleSystem> {
1586
- return new Map(this.particleSystems);
1587
- }
1588
-
1589
- updateParticles(deltaTime: number): void {
1590
- for (const [name, system] of this.particleSystems) {
1591
- system.lifetime -= deltaTime;
1592
- system.particles.forEach(particle => {
1593
- particle.x += (Math.random() - 0.5) * system.speed;
1594
- particle.y += (Math.random() - 0.5) * system.speed;
1595
- particle.z += (Math.random() - 0.5) * system.speed;
1596
- });
1597
- if (system.lifetime <= 0) {
1598
- this.particleSystems.delete(name);
1599
- }
1600
- }
1601
- }
1602
-
1603
- getContext(): RuntimeContext {
1604
- return { ...this.context };
1605
- }
1606
-
1607
- getUIElements(): Map<string, UIElementState> {
1608
- return new Map(this.uiElements);
1609
- }
1610
-
1611
- getUIElement(name: string): UIElementState | undefined {
1612
- return this.uiElements.get(name);
1613
- }
1614
-
1615
- getAnimations(): Map<string, Animation> {
1616
- return new Map(this.animations);
1617
- }
1618
-
1619
- reset(): void {
1620
- this.context = this.createEmptyContext();
1621
- this.currentScope = { variables: this.context.variables };
1622
- this.callStack = [];
1623
- this.particleSystems.clear();
1624
- this.executionHistory = [];
1625
- this.eventHandlers.clear();
1626
- this.animations.clear();
1627
- this.uiElements.clear();
1628
- }
1629
-
1630
- private createEmptyContext(): RuntimeContext {
1631
- return {
1632
- variables: new Map(),
1633
- functions: new Map(),
1634
- exports: new Map(),
1635
- connections: [],
1636
- spatialMemory: new Map(),
1637
- hologramState: new Map(),
1638
- executionStack: [],
1639
- };
1640
- }
1641
-
1642
- getExecutionHistory(): ExecutionResult[] {
1643
- return [...this.executionHistory];
1644
- }
1645
-
1646
- getHologramStates(): Map<string, HologramProperties> {
1647
- return new Map(this.context.hologramState);
1648
- }
1649
-
1650
- getCallStack(): string[] {
1651
- return [...this.callStack];
1652
- }
1653
- }