@holoscript/core 2.0.0 → 2.0.2
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/LICENSE +21 -0
- package/dist/chunk-3N67RLQP.cjs +1298 -0
- package/dist/chunk-3N67RLQP.cjs.map +1 -0
- package/dist/chunk-3X2EGU7Z.cjs +52 -0
- package/dist/chunk-3X2EGU7Z.cjs.map +1 -0
- package/dist/chunk-4CV4JOE5.js +24 -0
- package/dist/chunk-4CV4JOE5.js.map +1 -0
- package/dist/chunk-4OHVW4XR.cjs +1027 -0
- package/dist/chunk-4OHVW4XR.cjs.map +1 -0
- package/dist/chunk-CZLDE2OZ.cjs +28 -0
- package/dist/chunk-CZLDE2OZ.cjs.map +1 -0
- package/{src/HoloScriptRuntime.ts → dist/chunk-EU6CZMGJ.js} +437 -794
- package/dist/chunk-EU6CZMGJ.js.map +1 -0
- package/dist/chunk-KWYIVRIH.js +344 -0
- package/dist/chunk-KWYIVRIH.js.map +1 -0
- package/dist/chunk-MCP6D4LT.js +1025 -0
- package/dist/chunk-MCP6D4LT.js.map +1 -0
- package/dist/chunk-SATNCODL.js +45 -0
- package/dist/chunk-SATNCODL.js.map +1 -0
- package/dist/chunk-VMZN4EVR.cjs +347 -0
- package/dist/chunk-VMZN4EVR.cjs.map +1 -0
- package/{src/HoloScriptDebugger.ts → dist/chunk-VYIDLUCV.js} +118 -257
- package/dist/chunk-VYIDLUCV.js.map +1 -0
- package/dist/chunk-WFI4T3XB.cjs +424 -0
- package/dist/chunk-WFI4T3XB.cjs.map +1 -0
- package/dist/debugger.cjs +20 -0
- package/dist/debugger.cjs.map +1 -0
- package/dist/debugger.d.cts +171 -0
- package/dist/debugger.d.ts +171 -0
- package/dist/debugger.js +7 -0
- package/dist/debugger.js.map +1 -0
- package/dist/index.cjs +6006 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2482 -0
- package/dist/index.d.ts +2482 -0
- package/dist/index.js +5926 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.cjs +14 -0
- package/dist/parser.cjs.map +1 -0
- package/dist/parser.d.cts +139 -0
- package/dist/parser.d.ts +139 -0
- package/dist/parser.js +5 -0
- package/dist/parser.js.map +1 -0
- package/dist/runtime.cjs +14 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.cts +180 -0
- package/dist/runtime.d.ts +180 -0
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -0
- package/dist/type-checker.cjs +17 -0
- package/dist/type-checker.cjs.map +1 -0
- package/dist/type-checker.d.cts +105 -0
- package/dist/type-checker.d.ts +105 -0
- package/dist/type-checker.js +4 -0
- package/dist/type-checker.js.map +1 -0
- package/dist/types-D6g4ACjP.d.cts +262 -0
- package/dist/types-D6g4ACjP.d.ts +262 -0
- package/package.json +11 -8
- package/src/HoloScript2DParser.js +0 -227
- package/src/HoloScript2DParser.ts +0 -261
- package/src/HoloScriptCodeParser.js +0 -1102
- package/src/HoloScriptCodeParser.ts +0 -1188
- package/src/HoloScriptDebugger.js +0 -458
- package/src/HoloScriptParser.js +0 -338
- package/src/HoloScriptParser.ts +0 -397
- package/src/HoloScriptPlusParser.js +0 -371
- package/src/HoloScriptPlusParser.ts +0 -543
- package/src/HoloScriptRuntime.js +0 -1399
- package/src/HoloScriptRuntime.test.js +0 -351
- package/src/HoloScriptRuntime.test.ts +0 -436
- package/src/HoloScriptTypeChecker.js +0 -356
- package/src/HoloScriptTypeChecker.ts +0 -475
- package/src/__tests__/GraphicsServices.test.js +0 -357
- package/src/__tests__/GraphicsServices.test.ts +0 -427
- package/src/__tests__/HoloScriptPlusParser.test.js +0 -317
- package/src/__tests__/HoloScriptPlusParser.test.ts +0 -392
- package/src/__tests__/integration.test.js +0 -336
- package/src/__tests__/integration.test.ts +0 -416
- package/src/__tests__/performance.bench.js +0 -218
- package/src/__tests__/performance.bench.ts +0 -262
- package/src/__tests__/type-checker.test.js +0 -60
- package/src/__tests__/type-checker.test.ts +0 -73
- package/src/index.js +0 -217
- package/src/index.ts +0 -426
- package/src/interop/Interoperability.js +0 -413
- package/src/interop/Interoperability.ts +0 -494
- package/src/logger.js +0 -42
- package/src/logger.ts +0 -57
- package/src/parser/EnhancedParser.js +0 -205
- package/src/parser/EnhancedParser.ts +0 -251
- package/src/parser/HoloScriptPlusParser.js +0 -928
- package/src/parser/HoloScriptPlusParser.ts +0 -1089
- package/src/runtime/HoloScriptPlusRuntime.js +0 -674
- package/src/runtime/HoloScriptPlusRuntime.ts +0 -861
- package/src/runtime/PerformanceTelemetry.js +0 -323
- package/src/runtime/PerformanceTelemetry.ts +0 -467
- package/src/runtime/RuntimeOptimization.js +0 -361
- package/src/runtime/RuntimeOptimization.ts +0 -416
- package/src/services/HololandGraphicsPipelineService.js +0 -506
- package/src/services/HololandGraphicsPipelineService.ts +0 -662
- package/src/services/PlatformPerformanceOptimizer.js +0 -356
- package/src/services/PlatformPerformanceOptimizer.ts +0 -503
- package/src/state/ReactiveState.js +0 -427
- package/src/state/ReactiveState.ts +0 -572
- package/src/tools/DeveloperExperience.js +0 -376
- package/src/tools/DeveloperExperience.ts +0 -438
- package/src/traits/AIDriverTrait.js +0 -322
- package/src/traits/AIDriverTrait.test.js +0 -329
- package/src/traits/AIDriverTrait.test.ts +0 -357
- package/src/traits/AIDriverTrait.ts +0 -474
- package/src/traits/LightingTrait.js +0 -313
- package/src/traits/LightingTrait.test.js +0 -410
- package/src/traits/LightingTrait.test.ts +0 -462
- package/src/traits/LightingTrait.ts +0 -505
- package/src/traits/MaterialTrait.js +0 -194
- package/src/traits/MaterialTrait.test.js +0 -286
- package/src/traits/MaterialTrait.test.ts +0 -329
- package/src/traits/MaterialTrait.ts +0 -324
- package/src/traits/RenderingTrait.js +0 -356
- package/src/traits/RenderingTrait.test.js +0 -363
- package/src/traits/RenderingTrait.test.ts +0 -427
- package/src/traits/RenderingTrait.ts +0 -555
- package/src/traits/VRTraitSystem.js +0 -740
- package/src/traits/VRTraitSystem.ts +0 -1040
- package/src/traits/VoiceInputTrait.js +0 -284
- package/src/traits/VoiceInputTrait.test.js +0 -226
- package/src/traits/VoiceInputTrait.test.ts +0 -252
- package/src/traits/VoiceInputTrait.ts +0 -401
- package/src/types/AdvancedTypeSystem.js +0 -226
- package/src/types/AdvancedTypeSystem.ts +0 -494
- package/src/types/HoloScriptPlus.d.ts +0 -853
- package/src/types.js +0 -6
- package/src/types.ts +0 -369
- package/tsconfig.json +0 -23
- package/tsup.config.d.ts +0 -2
- package/tsup.config.js +0 -18
- package/tsup.config.ts +0 -19
|
@@ -1,1188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HoloScript Code Parser
|
|
3
|
-
*
|
|
4
|
-
* Parses HoloScript code strings into AST nodes that can be executed
|
|
5
|
-
* by the HoloScriptRuntime.
|
|
6
|
-
*
|
|
7
|
-
* Syntax Reference:
|
|
8
|
-
* - orb <name> { properties }
|
|
9
|
-
* - function <name>(<params>): <return> { body }
|
|
10
|
-
* - connect <from> to <to> [as <type>]
|
|
11
|
-
* - gate <name> { condition, true_path, false_path }
|
|
12
|
-
* - stream <name> from <source> { transformations }
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { logger } from './logger';
|
|
16
|
-
import type {
|
|
17
|
-
ASTNode,
|
|
18
|
-
OrbNode,
|
|
19
|
-
MethodNode,
|
|
20
|
-
ParameterNode,
|
|
21
|
-
ConnectionNode,
|
|
22
|
-
GateNode,
|
|
23
|
-
StreamNode,
|
|
24
|
-
SpatialPosition,
|
|
25
|
-
HologramProperties,
|
|
26
|
-
ForLoopNode,
|
|
27
|
-
WhileLoopNode,
|
|
28
|
-
ForEachLoopNode,
|
|
29
|
-
ImportNode,
|
|
30
|
-
ExportNode,
|
|
31
|
-
VariableDeclarationNode,
|
|
32
|
-
} from './types';
|
|
33
|
-
|
|
34
|
-
// Security configuration
|
|
35
|
-
const CODE_SECURITY_CONFIG = {
|
|
36
|
-
maxCodeLength: 50000,
|
|
37
|
-
maxBlocks: 100,
|
|
38
|
-
maxNestingDepth: 10,
|
|
39
|
-
suspiciousKeywords: [
|
|
40
|
-
'process', 'require', 'eval', 'import', 'constructor',
|
|
41
|
-
'prototype', '__proto__', 'fs', 'child_process', 'exec', 'spawn',
|
|
42
|
-
],
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export interface ParseResult {
|
|
46
|
-
success: boolean;
|
|
47
|
-
ast: ASTNode[];
|
|
48
|
-
errors: ParseError[];
|
|
49
|
-
warnings: string[];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface ParseError {
|
|
53
|
-
line: number;
|
|
54
|
-
column: number;
|
|
55
|
-
message: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface Token {
|
|
59
|
-
type: 'keyword' | 'identifier' | 'number' | 'string' | 'operator' | 'punctuation' | 'newline';
|
|
60
|
-
value: string;
|
|
61
|
-
line: number;
|
|
62
|
-
column: number;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export class HoloScriptCodeParser {
|
|
66
|
-
private errors: ParseError[] = [];
|
|
67
|
-
private warnings: string[] = [];
|
|
68
|
-
private tokens: Token[] = [];
|
|
69
|
-
private position: number = 0;
|
|
70
|
-
private keywordSet: Set<string>;
|
|
71
|
-
|
|
72
|
-
constructor() {
|
|
73
|
-
// Pre-compute keyword set for O(1) lookup instead of O(n) array search
|
|
74
|
-
this.keywordSet = new Set([
|
|
75
|
-
'orb', 'function', 'connect', 'to', 'as', 'gate', 'stream', 'from', 'through', 'return',
|
|
76
|
-
'if', 'else', 'nexus', 'building', 'pillar', 'foundation',
|
|
77
|
-
'for', 'while', 'forEach', 'in', 'of', 'break', 'continue',
|
|
78
|
-
'import', 'export', 'module', 'use',
|
|
79
|
-
'type', 'interface', 'extends', 'implements',
|
|
80
|
-
'async', 'await', 'spawn', 'parallel',
|
|
81
|
-
'class', 'new', 'this', 'super', 'static', 'private', 'public',
|
|
82
|
-
'try', 'catch', 'finally', 'throw',
|
|
83
|
-
'const', 'let', 'var',
|
|
84
|
-
'animate', 'modify', 'pulse', 'move', 'show', 'hide',
|
|
85
|
-
]);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Parse HoloScript code string into AST
|
|
90
|
-
*/
|
|
91
|
-
parse(code: string): ParseResult {
|
|
92
|
-
this.errors = [];
|
|
93
|
-
this.warnings = [];
|
|
94
|
-
this.tokens = [];
|
|
95
|
-
this.position = 0;
|
|
96
|
-
|
|
97
|
-
// Security: Check code length
|
|
98
|
-
if (code.length > CODE_SECURITY_CONFIG.maxCodeLength) {
|
|
99
|
-
return {
|
|
100
|
-
success: false,
|
|
101
|
-
ast: [],
|
|
102
|
-
errors: [{ line: 0, column: 0, message: `Code exceeds max length (${CODE_SECURITY_CONFIG.maxCodeLength})` }],
|
|
103
|
-
warnings: [],
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Security: Check for suspicious keywords
|
|
108
|
-
for (const keyword of CODE_SECURITY_CONFIG.suspiciousKeywords) {
|
|
109
|
-
if (code.toLowerCase().includes(keyword)) {
|
|
110
|
-
logger.warn('Suspicious keyword detected', { keyword });
|
|
111
|
-
return {
|
|
112
|
-
success: false,
|
|
113
|
-
ast: [],
|
|
114
|
-
errors: [{ line: 0, column: 0, message: `Suspicious keyword detected: ${keyword}` }],
|
|
115
|
-
warnings: [],
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
// Tokenize
|
|
122
|
-
this.tokens = this.tokenize(code);
|
|
123
|
-
|
|
124
|
-
// Parse tokens into AST
|
|
125
|
-
const ast = this.parseProgram();
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
success: this.errors.length === 0,
|
|
129
|
-
ast,
|
|
130
|
-
errors: this.errors,
|
|
131
|
-
warnings: this.warnings,
|
|
132
|
-
};
|
|
133
|
-
} catch (error) {
|
|
134
|
-
return {
|
|
135
|
-
success: false,
|
|
136
|
-
ast: [],
|
|
137
|
-
errors: [{ line: 0, column: 0, message: String(error) }],
|
|
138
|
-
warnings: this.warnings,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Tokenize code into tokens
|
|
145
|
-
*/
|
|
146
|
-
private tokenize(code: string): Token[] {
|
|
147
|
-
const tokens: Token[] = [];
|
|
148
|
-
let line = 1;
|
|
149
|
-
let column = 1;
|
|
150
|
-
let i = 0;
|
|
151
|
-
|
|
152
|
-
while (i < code.length) {
|
|
153
|
-
const char = code[i];
|
|
154
|
-
|
|
155
|
-
// Skip whitespace (except newlines)
|
|
156
|
-
if (char === ' ' || char === '\t' || char === '\r') {
|
|
157
|
-
i++;
|
|
158
|
-
column++;
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Newline
|
|
163
|
-
if (char === '\n') {
|
|
164
|
-
tokens.push({ type: 'newline', value: '\n', line, column });
|
|
165
|
-
line++;
|
|
166
|
-
column = 1;
|
|
167
|
-
i++;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Comments (skip)
|
|
172
|
-
if (char === '/' && code[i + 1] === '/') {
|
|
173
|
-
while (i < code.length && code[i] !== '\n') {
|
|
174
|
-
i++;
|
|
175
|
-
}
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// String
|
|
180
|
-
if (char === '"' || char === "'") {
|
|
181
|
-
const quote = char;
|
|
182
|
-
let str = '';
|
|
183
|
-
const startCol = column;
|
|
184
|
-
i++;
|
|
185
|
-
column++;
|
|
186
|
-
|
|
187
|
-
while (i < code.length && code[i] !== quote) {
|
|
188
|
-
if (code[i] === '\\' && i + 1 < code.length) {
|
|
189
|
-
str += code[i + 1];
|
|
190
|
-
i += 2;
|
|
191
|
-
column += 2;
|
|
192
|
-
} else {
|
|
193
|
-
str += code[i];
|
|
194
|
-
i++;
|
|
195
|
-
column++;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
i++; // Skip closing quote
|
|
200
|
-
column++;
|
|
201
|
-
|
|
202
|
-
tokens.push({ type: 'string', value: str, line, column: startCol });
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Number
|
|
207
|
-
if (/[0-9]/.test(char) || (char === '-' && /[0-9]/.test(code[i + 1]))) {
|
|
208
|
-
let num = '';
|
|
209
|
-
const startCol = column;
|
|
210
|
-
|
|
211
|
-
while (i < code.length && /[0-9.\-]/.test(code[i])) {
|
|
212
|
-
num += code[i];
|
|
213
|
-
i++;
|
|
214
|
-
column++;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
tokens.push({ type: 'number', value: num, line, column: startCol });
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Identifier or keyword
|
|
222
|
-
if (/[a-zA-Z_]/.test(char)) {
|
|
223
|
-
let ident = '';
|
|
224
|
-
const startCol = column;
|
|
225
|
-
|
|
226
|
-
while (i < code.length && /[a-zA-Z0-9_]/.test(code[i])) {
|
|
227
|
-
ident += code[i];
|
|
228
|
-
i++;
|
|
229
|
-
column++;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const isKeyword = this.keywordSet.has(ident.toLowerCase());
|
|
233
|
-
tokens.push({
|
|
234
|
-
type: isKeyword ? 'keyword' : 'identifier',
|
|
235
|
-
value: ident,
|
|
236
|
-
line,
|
|
237
|
-
column: startCol,
|
|
238
|
-
});
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Multi-character operators (must check before single-char)
|
|
243
|
-
const multiCharOps = ['===', '!==', '==', '!=', '>=', '<=', '&&', '||', '++', '--', '+=', '-=', '*=', '/=', '%=', '=>', '->'];
|
|
244
|
-
let foundMultiOp = false;
|
|
245
|
-
for (const op of multiCharOps) {
|
|
246
|
-
if (code.substring(i, i + op.length) === op) {
|
|
247
|
-
tokens.push({ type: 'operator', value: op, line, column });
|
|
248
|
-
i += op.length;
|
|
249
|
-
column += op.length;
|
|
250
|
-
foundMultiOp = true;
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (foundMultiOp) continue;
|
|
255
|
-
|
|
256
|
-
// Operators and punctuation
|
|
257
|
-
const punctuation = ['{', '}', '(', ')', '[', ']', ':', ',', '.', ';', '=', '<', '>', '+', '-', '*', '/', '%', '!', '&', '|', '?'];
|
|
258
|
-
if (punctuation.includes(char)) {
|
|
259
|
-
tokens.push({ type: 'punctuation', value: char, line, column });
|
|
260
|
-
i++;
|
|
261
|
-
column++;
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Unknown character - skip
|
|
266
|
-
i++;
|
|
267
|
-
column++;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return tokens;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Parse program (list of declarations)
|
|
275
|
-
*/
|
|
276
|
-
private parseProgram(): ASTNode[] {
|
|
277
|
-
const nodes: ASTNode[] = [];
|
|
278
|
-
let blockCount = 0;
|
|
279
|
-
|
|
280
|
-
while (this.position < this.tokens.length) {
|
|
281
|
-
// Skip newlines
|
|
282
|
-
while (this.currentToken()?.type === 'newline') {
|
|
283
|
-
this.advance();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (this.position >= this.tokens.length) break;
|
|
287
|
-
|
|
288
|
-
// Security: limit blocks
|
|
289
|
-
blockCount++;
|
|
290
|
-
if (blockCount > CODE_SECURITY_CONFIG.maxBlocks) {
|
|
291
|
-
this.errors.push({ line: 0, column: 0, message: 'Too many blocks in program' });
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const node = this.parseDeclaration();
|
|
296
|
-
if (node) {
|
|
297
|
-
nodes.push(node);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
return nodes;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Parse a single declaration
|
|
306
|
-
*/
|
|
307
|
-
private parseDeclaration(): ASTNode | null {
|
|
308
|
-
const token = this.currentToken();
|
|
309
|
-
if (!token) return null;
|
|
310
|
-
|
|
311
|
-
if (token.type === 'keyword') {
|
|
312
|
-
switch (token.value.toLowerCase()) {
|
|
313
|
-
case 'orb':
|
|
314
|
-
return this.parseOrb();
|
|
315
|
-
case 'function':
|
|
316
|
-
return this.parseFunction();
|
|
317
|
-
case 'connect':
|
|
318
|
-
return this.parseConnection();
|
|
319
|
-
case 'gate':
|
|
320
|
-
case 'if':
|
|
321
|
-
return this.parseGate();
|
|
322
|
-
case 'stream':
|
|
323
|
-
return this.parseStream();
|
|
324
|
-
case 'nexus':
|
|
325
|
-
return this.parseNexus();
|
|
326
|
-
case 'building':
|
|
327
|
-
case 'class':
|
|
328
|
-
return this.parseBuilding();
|
|
329
|
-
// Phase 2: Loop constructs
|
|
330
|
-
case 'for':
|
|
331
|
-
return this.parseForLoop();
|
|
332
|
-
case 'while':
|
|
333
|
-
return this.parseWhileLoop();
|
|
334
|
-
case 'foreach':
|
|
335
|
-
return this.parseForEachLoop();
|
|
336
|
-
// Phase 2: Module system
|
|
337
|
-
case 'import':
|
|
338
|
-
return this.parseImport();
|
|
339
|
-
case 'export':
|
|
340
|
-
return this.parseExport();
|
|
341
|
-
// Phase 2: Variable declarations
|
|
342
|
-
// UI Extensions
|
|
343
|
-
case 'ui2d':
|
|
344
|
-
case 'card':
|
|
345
|
-
case 'metric':
|
|
346
|
-
case 'button':
|
|
347
|
-
case 'row':
|
|
348
|
-
case 'col':
|
|
349
|
-
case 'text':
|
|
350
|
-
return this.parseUIElement();
|
|
351
|
-
case 'const':
|
|
352
|
-
case 'let':
|
|
353
|
-
case 'var':
|
|
354
|
-
return this.parseVariableDeclaration();
|
|
355
|
-
// DSL-first commands (Phase 54)
|
|
356
|
-
case 'animate':
|
|
357
|
-
return this.parseAnimate();
|
|
358
|
-
case 'modify':
|
|
359
|
-
return this.parseModify();
|
|
360
|
-
default:
|
|
361
|
-
this.advance();
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Skip unrecognized tokens
|
|
367
|
-
this.advance();
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Parse for loop: for (init; condition; update) { body }
|
|
373
|
-
*/
|
|
374
|
-
private parseForLoop(): ForLoopNode | null {
|
|
375
|
-
this.expect('keyword', 'for');
|
|
376
|
-
|
|
377
|
-
if (!this.check('punctuation', '(')) {
|
|
378
|
-
this.errors.push({ line: 0, column: 0, message: 'Expected ( after for' });
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
this.advance();
|
|
382
|
-
|
|
383
|
-
// Parse init, condition, update (simplified - collect as strings)
|
|
384
|
-
let init = '', condition = '', update = '';
|
|
385
|
-
let depth = 0;
|
|
386
|
-
|
|
387
|
-
// Parse init (until first ;)
|
|
388
|
-
while (this.position < this.tokens.length) {
|
|
389
|
-
const t = this.currentToken();
|
|
390
|
-
if (!t) break;
|
|
391
|
-
if (t.value === ';' && depth === 0) { this.advance(); break; }
|
|
392
|
-
if (t.value === '(') depth++;
|
|
393
|
-
if (t.value === ')') depth--;
|
|
394
|
-
init += t.value + ' ';
|
|
395
|
-
this.advance();
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Parse condition (until second ;)
|
|
399
|
-
depth = 0;
|
|
400
|
-
while (this.position < this.tokens.length) {
|
|
401
|
-
const t = this.currentToken();
|
|
402
|
-
if (!t) break;
|
|
403
|
-
if (t.value === ';' && depth === 0) { this.advance(); break; }
|
|
404
|
-
if (t.value === '(') depth++;
|
|
405
|
-
if (t.value === ')') depth--;
|
|
406
|
-
condition += t.value + ' ';
|
|
407
|
-
this.advance();
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Parse update (until ))
|
|
411
|
-
depth = 0;
|
|
412
|
-
while (this.position < this.tokens.length) {
|
|
413
|
-
const t = this.currentToken();
|
|
414
|
-
if (!t) break;
|
|
415
|
-
if (t.value === ')' && depth === 0) { this.advance(); break; }
|
|
416
|
-
if (t.value === '(') depth++;
|
|
417
|
-
if (t.value === ')') depth--;
|
|
418
|
-
update += t.value + ' ';
|
|
419
|
-
this.advance();
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Parse body
|
|
423
|
-
const body: ASTNode[] = [];
|
|
424
|
-
if (this.check('punctuation', '{')) {
|
|
425
|
-
this.advance();
|
|
426
|
-
let braceDepth = 1;
|
|
427
|
-
while (braceDepth > 0 && this.position < this.tokens.length) {
|
|
428
|
-
if (this.check('punctuation', '{')) braceDepth++;
|
|
429
|
-
if (this.check('punctuation', '}')) braceDepth--;
|
|
430
|
-
this.advance();
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
type: 'for-loop',
|
|
436
|
-
init: init.trim(),
|
|
437
|
-
condition: condition.trim(),
|
|
438
|
-
update: update.trim(),
|
|
439
|
-
body,
|
|
440
|
-
position: { x: 0, y: 0, z: 0 },
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Parse while loop: while (condition) { body }
|
|
446
|
-
*/
|
|
447
|
-
private parseWhileLoop(): WhileLoopNode | null {
|
|
448
|
-
this.expect('keyword', 'while');
|
|
449
|
-
|
|
450
|
-
let condition = '';
|
|
451
|
-
if (this.check('punctuation', '(')) {
|
|
452
|
-
this.advance();
|
|
453
|
-
let depth = 1;
|
|
454
|
-
while (depth > 0 && this.position < this.tokens.length) {
|
|
455
|
-
const t = this.currentToken();
|
|
456
|
-
if (!t) break;
|
|
457
|
-
if (t.value === '(') depth++;
|
|
458
|
-
if (t.value === ')') { depth--; if (depth === 0) { this.advance(); break; } }
|
|
459
|
-
condition += t.value + ' ';
|
|
460
|
-
this.advance();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Parse body
|
|
465
|
-
if (this.check('punctuation', '{')) {
|
|
466
|
-
this.advance();
|
|
467
|
-
let braceDepth = 1;
|
|
468
|
-
while (braceDepth > 0 && this.position < this.tokens.length) {
|
|
469
|
-
if (this.check('punctuation', '{')) braceDepth++;
|
|
470
|
-
if (this.check('punctuation', '}')) braceDepth--;
|
|
471
|
-
this.advance();
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return {
|
|
476
|
-
type: 'while-loop',
|
|
477
|
-
condition: condition.trim(),
|
|
478
|
-
body: [],
|
|
479
|
-
position: { x: 0, y: 0, z: 0 },
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Parse forEach loop: forEach item in collection { body }
|
|
485
|
-
*/
|
|
486
|
-
private parseForEachLoop(): ForEachLoopNode | null {
|
|
487
|
-
this.expect('keyword', 'forEach');
|
|
488
|
-
|
|
489
|
-
const variable = this.expectIdentifier();
|
|
490
|
-
this.expect('keyword', 'in');
|
|
491
|
-
const collection = this.expectIdentifier();
|
|
492
|
-
|
|
493
|
-
// Parse body
|
|
494
|
-
if (this.check('punctuation', '{')) {
|
|
495
|
-
this.advance();
|
|
496
|
-
let braceDepth = 1;
|
|
497
|
-
while (braceDepth > 0 && this.position < this.tokens.length) {
|
|
498
|
-
if (this.check('punctuation', '{')) braceDepth++;
|
|
499
|
-
if (this.check('punctuation', '}')) braceDepth--;
|
|
500
|
-
this.advance();
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return {
|
|
505
|
-
type: 'foreach-loop',
|
|
506
|
-
variable: variable || 'item',
|
|
507
|
-
collection: collection || 'items',
|
|
508
|
-
body: [],
|
|
509
|
-
position: { x: 0, y: 0, z: 0 },
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/**
|
|
514
|
-
* Parse import: import { x, y } from "module"
|
|
515
|
-
*/
|
|
516
|
-
private parseImport(): ImportNode | null {
|
|
517
|
-
this.expect('keyword', 'import');
|
|
518
|
-
|
|
519
|
-
const imports: string[] = [];
|
|
520
|
-
let modulePath = '';
|
|
521
|
-
let defaultImport: string | undefined;
|
|
522
|
-
|
|
523
|
-
// Check for default import or named imports
|
|
524
|
-
if (this.check('punctuation', '{')) {
|
|
525
|
-
this.advance();
|
|
526
|
-
while (!this.check('punctuation', '}') && this.position < this.tokens.length) {
|
|
527
|
-
const name = this.expectIdentifier();
|
|
528
|
-
if (name) imports.push(name);
|
|
529
|
-
if (this.check('punctuation', ',')) this.advance();
|
|
530
|
-
}
|
|
531
|
-
this.expect('punctuation', '}');
|
|
532
|
-
} else {
|
|
533
|
-
// Default import
|
|
534
|
-
defaultImport = this.expectIdentifier() || undefined;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// from "module"
|
|
538
|
-
if (this.check('keyword', 'from')) {
|
|
539
|
-
this.advance();
|
|
540
|
-
const pathToken = this.currentToken();
|
|
541
|
-
if (pathToken?.type === 'string') {
|
|
542
|
-
modulePath = pathToken.value;
|
|
543
|
-
this.advance();
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
return {
|
|
548
|
-
type: 'import',
|
|
549
|
-
imports,
|
|
550
|
-
defaultImport,
|
|
551
|
-
modulePath,
|
|
552
|
-
position: { x: 0, y: 0, z: 0 },
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/**
|
|
557
|
-
* Parse export: export { x, y } or export function/orb
|
|
558
|
-
*/
|
|
559
|
-
private parseExport(): ExportNode | null {
|
|
560
|
-
this.expect('keyword', 'export');
|
|
561
|
-
|
|
562
|
-
// Check if exporting a declaration
|
|
563
|
-
const next = this.currentToken();
|
|
564
|
-
if (next?.type === 'keyword') {
|
|
565
|
-
const declaration = this.parseDeclaration();
|
|
566
|
-
return {
|
|
567
|
-
type: 'export',
|
|
568
|
-
declaration: declaration || undefined,
|
|
569
|
-
position: { x: 0, y: 0, z: 0 },
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Named exports
|
|
574
|
-
const exports: string[] = [];
|
|
575
|
-
if (this.check('punctuation', '{')) {
|
|
576
|
-
this.advance();
|
|
577
|
-
while (!this.check('punctuation', '}') && this.position < this.tokens.length) {
|
|
578
|
-
const name = this.expectIdentifier();
|
|
579
|
-
if (name) exports.push(name);
|
|
580
|
-
if (this.check('punctuation', ',')) this.advance();
|
|
581
|
-
}
|
|
582
|
-
this.expect('punctuation', '}');
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
return {
|
|
586
|
-
type: 'export',
|
|
587
|
-
exports,
|
|
588
|
-
position: { x: 0, y: 0, z: 0 },
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Parse variable declaration: const/let/var name = value
|
|
594
|
-
*/
|
|
595
|
-
private parseVariableDeclaration(): VariableDeclarationNode | null {
|
|
596
|
-
const kindToken = this.currentToken()?.value.toLowerCase();
|
|
597
|
-
const kind: 'const' | 'let' | 'var' = kindToken === 'let' ? 'let' : kindToken === 'var' ? 'var' : 'const';
|
|
598
|
-
this.advance();
|
|
599
|
-
|
|
600
|
-
const name = this.expectIdentifier();
|
|
601
|
-
if (!name) return null;
|
|
602
|
-
|
|
603
|
-
let dataType: string | undefined;
|
|
604
|
-
if (this.check('punctuation', ':')) {
|
|
605
|
-
this.advance();
|
|
606
|
-
dataType = this.expectIdentifier() || undefined;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
let value: unknown;
|
|
610
|
-
if (this.check('punctuation', '=')) {
|
|
611
|
-
this.advance();
|
|
612
|
-
const valueToken = this.currentToken();
|
|
613
|
-
if (valueToken?.type === 'string') {
|
|
614
|
-
value = valueToken.value;
|
|
615
|
-
this.advance();
|
|
616
|
-
} else if (valueToken?.type === 'number') {
|
|
617
|
-
value = parseFloat(valueToken.value);
|
|
618
|
-
this.advance();
|
|
619
|
-
} else if (valueToken?.type === 'identifier') {
|
|
620
|
-
if (valueToken.value === 'true') value = true;
|
|
621
|
-
else if (valueToken.value === 'false') value = false;
|
|
622
|
-
else value = valueToken.value;
|
|
623
|
-
this.advance();
|
|
624
|
-
} else if (this.check('punctuation', '[')) {
|
|
625
|
-
value = this.parseArray();
|
|
626
|
-
} else if (this.check('punctuation', '{')) {
|
|
627
|
-
value = this.parseObject();
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
return {
|
|
632
|
-
type: 'variable-declaration',
|
|
633
|
-
kind,
|
|
634
|
-
name,
|
|
635
|
-
dataType,
|
|
636
|
-
value,
|
|
637
|
-
position: { x: 0, y: 0, z: 0 },
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
* Parse orb declaration
|
|
643
|
-
*/
|
|
644
|
-
private parseOrb(): OrbNode | null {
|
|
645
|
-
this.expect('keyword', 'orb');
|
|
646
|
-
const name = this.expectIdentifier();
|
|
647
|
-
if (!name) return null;
|
|
648
|
-
|
|
649
|
-
const properties: Record<string, unknown> = {};
|
|
650
|
-
let position: SpatialPosition | undefined;
|
|
651
|
-
let hologram: HologramProperties | undefined;
|
|
652
|
-
|
|
653
|
-
if (this.check('punctuation', '{')) {
|
|
654
|
-
this.advance(); // {
|
|
655
|
-
|
|
656
|
-
while (!this.check('punctuation', '}') && this.position < this.tokens.length) {
|
|
657
|
-
this.skipNewlines();
|
|
658
|
-
if (this.check('punctuation', '}')) break;
|
|
659
|
-
|
|
660
|
-
const prop = this.parseProperty();
|
|
661
|
-
if (prop) {
|
|
662
|
-
// Handle special properties
|
|
663
|
-
if (prop.key === 'position' || prop.key === 'at') {
|
|
664
|
-
position = this.parsePosition(prop.value);
|
|
665
|
-
} else if (prop.key === 'color' || prop.key === 'glow' || prop.key === 'size') {
|
|
666
|
-
hologram = hologram || { shape: 'orb', color: '#00ffff', size: 1, glow: true, interactive: true };
|
|
667
|
-
if (prop.key === 'color') hologram.color = String(prop.value);
|
|
668
|
-
if (prop.key === 'glow') hologram.glow = Boolean(prop.value);
|
|
669
|
-
if (prop.key === 'size') hologram.size = Number(prop.value);
|
|
670
|
-
} else {
|
|
671
|
-
properties[prop.key] = prop.value;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
this.skipNewlines();
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
this.expect('punctuation', '}');
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
return {
|
|
682
|
-
type: 'orb',
|
|
683
|
-
name,
|
|
684
|
-
position: position || { x: 0, y: 0, z: 0 },
|
|
685
|
-
hologram: hologram || { shape: 'orb', color: '#00ffff', size: 1, glow: true, interactive: true },
|
|
686
|
-
properties,
|
|
687
|
-
methods: [],
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Parse function declaration
|
|
693
|
-
*/
|
|
694
|
-
private parseFunction(): MethodNode | null {
|
|
695
|
-
this.expect('keyword', 'function');
|
|
696
|
-
const name = this.expectIdentifier();
|
|
697
|
-
if (!name) return null;
|
|
698
|
-
|
|
699
|
-
const parameters: ParameterNode[] = [];
|
|
700
|
-
let returnType: string | undefined;
|
|
701
|
-
|
|
702
|
-
// Parse parameters
|
|
703
|
-
if (this.check('punctuation', '(')) {
|
|
704
|
-
this.advance(); // (
|
|
705
|
-
|
|
706
|
-
while (!this.check('punctuation', ')') && this.position < this.tokens.length) {
|
|
707
|
-
const paramName = this.expectIdentifier();
|
|
708
|
-
if (!paramName) break;
|
|
709
|
-
|
|
710
|
-
let paramType = 'any';
|
|
711
|
-
if (this.check('punctuation', ':')) {
|
|
712
|
-
this.advance(); // :
|
|
713
|
-
paramType = this.expectIdentifier() || 'any';
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
parameters.push({
|
|
717
|
-
type: 'parameter',
|
|
718
|
-
name: paramName,
|
|
719
|
-
dataType: paramType,
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
if (this.check('punctuation', ',')) {
|
|
723
|
-
this.advance();
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
this.expect('punctuation', ')');
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Parse return type
|
|
731
|
-
if (this.check('punctuation', ':')) {
|
|
732
|
-
this.advance();
|
|
733
|
-
returnType = this.expectIdentifier() || undefined;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// Parse body
|
|
737
|
-
const body: ASTNode[] = [];
|
|
738
|
-
if (this.check('punctuation', '{')) {
|
|
739
|
-
this.advance(); // {
|
|
740
|
-
// Skip body parsing for now - just find closing brace
|
|
741
|
-
let depth = 1;
|
|
742
|
-
while (depth > 0 && this.position < this.tokens.length) {
|
|
743
|
-
if (this.check('punctuation', '{')) depth++;
|
|
744
|
-
if (this.check('punctuation', '}')) depth--;
|
|
745
|
-
this.advance();
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
return {
|
|
750
|
-
type: 'method',
|
|
751
|
-
name,
|
|
752
|
-
parameters,
|
|
753
|
-
body,
|
|
754
|
-
returnType,
|
|
755
|
-
position: { x: 0, y: 0, z: 0 },
|
|
756
|
-
hologram: { shape: 'cube', color: '#ff6b35', size: 1.5, glow: true, interactive: true },
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Parse connection
|
|
762
|
-
*/
|
|
763
|
-
private parseConnection(): ConnectionNode | null {
|
|
764
|
-
this.expect('keyword', 'connect');
|
|
765
|
-
const from = this.expectIdentifier();
|
|
766
|
-
if (!from) return null;
|
|
767
|
-
|
|
768
|
-
this.expect('keyword', 'to');
|
|
769
|
-
const to = this.expectIdentifier();
|
|
770
|
-
if (!to) return null;
|
|
771
|
-
|
|
772
|
-
let dataType = 'any';
|
|
773
|
-
if (this.check('keyword', 'as')) {
|
|
774
|
-
this.advance();
|
|
775
|
-
const typeStr = this.currentToken();
|
|
776
|
-
if (typeStr?.type === 'string' || typeStr?.type === 'identifier') {
|
|
777
|
-
dataType = typeStr.value;
|
|
778
|
-
this.advance();
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
return {
|
|
783
|
-
type: 'connection',
|
|
784
|
-
from,
|
|
785
|
-
to,
|
|
786
|
-
dataType,
|
|
787
|
-
bidirectional: false,
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/**
|
|
792
|
-
* Parse gate (conditional)
|
|
793
|
-
*/
|
|
794
|
-
private parseGate(): GateNode | null {
|
|
795
|
-
this.expect('keyword', 'gate');
|
|
796
|
-
this.expectIdentifier(); // Gate name (consumed but not currently stored)
|
|
797
|
-
|
|
798
|
-
let condition = '';
|
|
799
|
-
if (this.check('punctuation', '(')) {
|
|
800
|
-
this.advance();
|
|
801
|
-
// Parse condition expression
|
|
802
|
-
while (!this.check('punctuation', ')') && this.position < this.tokens.length) {
|
|
803
|
-
const token = this.currentToken();
|
|
804
|
-
if (token) condition += token.value + ' ';
|
|
805
|
-
this.advance();
|
|
806
|
-
}
|
|
807
|
-
this.expect('punctuation', ')');
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
// Parse body if present
|
|
811
|
-
if (this.check('punctuation', '{')) {
|
|
812
|
-
this.advance();
|
|
813
|
-
let depth = 1;
|
|
814
|
-
while (depth > 0 && this.position < this.tokens.length) {
|
|
815
|
-
if (this.check('punctuation', '{')) depth++;
|
|
816
|
-
if (this.check('punctuation', '}')) depth--;
|
|
817
|
-
this.advance();
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
return {
|
|
822
|
-
type: 'gate',
|
|
823
|
-
condition: condition.trim(),
|
|
824
|
-
truePath: [],
|
|
825
|
-
falsePath: [],
|
|
826
|
-
position: { x: 0, y: 0, z: 0 },
|
|
827
|
-
hologram: { shape: 'pyramid', color: '#4ecdc4', size: 1, glow: true, interactive: true },
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
/**
|
|
832
|
-
* Parse stream
|
|
833
|
-
*/
|
|
834
|
-
private parseStream(): StreamNode | null {
|
|
835
|
-
this.expect('keyword', 'stream');
|
|
836
|
-
const name = this.expectIdentifier();
|
|
837
|
-
if (!name) return null;
|
|
838
|
-
|
|
839
|
-
let source = 'unknown';
|
|
840
|
-
if (this.check('keyword', 'from')) {
|
|
841
|
-
this.advance();
|
|
842
|
-
source = this.expectIdentifier() || 'unknown';
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
// Parse body if present
|
|
846
|
-
if (this.check('punctuation', '{')) {
|
|
847
|
-
this.advance();
|
|
848
|
-
let depth = 1;
|
|
849
|
-
while (depth > 0 && this.position < this.tokens.length) {
|
|
850
|
-
if (this.check('punctuation', '{')) depth++;
|
|
851
|
-
if (this.check('punctuation', '}')) depth--;
|
|
852
|
-
this.advance();
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
return {
|
|
857
|
-
type: 'stream',
|
|
858
|
-
name,
|
|
859
|
-
source,
|
|
860
|
-
transformations: [],
|
|
861
|
-
position: { x: 0, y: 0, z: 0 },
|
|
862
|
-
hologram: { shape: 'cylinder', color: '#45b7d1', size: 2, glow: true, interactive: true },
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
/**
|
|
867
|
-
* Parse nexus (multi-agent hub)
|
|
868
|
-
*/
|
|
869
|
-
private parseNexus(): ASTNode | null {
|
|
870
|
-
this.expect('keyword', 'nexus');
|
|
871
|
-
const name = this.expectIdentifier();
|
|
872
|
-
if (!name) return null;
|
|
873
|
-
|
|
874
|
-
if (this.check('punctuation', '{')) {
|
|
875
|
-
this.advance();
|
|
876
|
-
let depth = 1;
|
|
877
|
-
while (depth > 0 && this.position < this.tokens.length) {
|
|
878
|
-
if (this.check('punctuation', '{')) depth++;
|
|
879
|
-
if (this.check('punctuation', '}')) depth--;
|
|
880
|
-
this.advance();
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
return {
|
|
885
|
-
type: 'nexus',
|
|
886
|
-
position: { x: 0, y: 0, z: 0 },
|
|
887
|
-
hologram: { shape: 'sphere', color: '#9b59b6', size: 3, glow: true, interactive: true },
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* Parse building (class-like)
|
|
893
|
-
*/
|
|
894
|
-
private parseBuilding(): ASTNode | null {
|
|
895
|
-
this.expect('keyword', 'building');
|
|
896
|
-
const name = this.expectIdentifier();
|
|
897
|
-
if (!name) return null;
|
|
898
|
-
|
|
899
|
-
if (this.check('punctuation', '{')) {
|
|
900
|
-
this.advance();
|
|
901
|
-
let depth = 1;
|
|
902
|
-
while (depth > 0 && this.position < this.tokens.length) {
|
|
903
|
-
if (this.check('punctuation', '{')) depth++;
|
|
904
|
-
if (this.check('punctuation', '}')) depth--;
|
|
905
|
-
this.advance();
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
return {
|
|
910
|
-
type: 'building',
|
|
911
|
-
position: { x: 0, y: 0, z: 0 },
|
|
912
|
-
hologram: { shape: 'cube', color: '#e74c3c', size: 4, glow: true, interactive: true },
|
|
913
|
-
};
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
/**
|
|
917
|
-
* Parse a property (key: value)
|
|
918
|
-
*/
|
|
919
|
-
private parseProperty(): { key: string; value: unknown } | null {
|
|
920
|
-
const key = this.expectIdentifier();
|
|
921
|
-
if (!key) return null;
|
|
922
|
-
|
|
923
|
-
if (!this.check('punctuation', ':')) {
|
|
924
|
-
return { key, value: true }; // Flag-style property
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
this.advance(); // :
|
|
928
|
-
|
|
929
|
-
const valueToken = this.currentToken();
|
|
930
|
-
if (!valueToken) return null;
|
|
931
|
-
|
|
932
|
-
let value: unknown;
|
|
933
|
-
if (valueToken.type === 'string') {
|
|
934
|
-
value = valueToken.value;
|
|
935
|
-
this.advance();
|
|
936
|
-
} else if (valueToken.type === 'number') {
|
|
937
|
-
value = parseFloat(valueToken.value);
|
|
938
|
-
this.advance();
|
|
939
|
-
} else if (valueToken.type === 'identifier') {
|
|
940
|
-
if (valueToken.value === 'true') value = true;
|
|
941
|
-
else if (valueToken.value === 'false') value = false;
|
|
942
|
-
else value = valueToken.value;
|
|
943
|
-
this.advance();
|
|
944
|
-
} else if (this.check('punctuation', '[')) {
|
|
945
|
-
value = this.parseArray();
|
|
946
|
-
} else if (this.check('punctuation', '{')) {
|
|
947
|
-
value = this.parseObject();
|
|
948
|
-
} else {
|
|
949
|
-
value = valueToken.value;
|
|
950
|
-
this.advance();
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
return { key, value };
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
/**
|
|
957
|
-
* Parse array [...]
|
|
958
|
-
*/
|
|
959
|
-
private parseArray(): unknown[] {
|
|
960
|
-
const arr: unknown[] = [];
|
|
961
|
-
this.expect('punctuation', '[');
|
|
962
|
-
|
|
963
|
-
while (!this.check('punctuation', ']') && this.position < this.tokens.length) {
|
|
964
|
-
const token = this.currentToken();
|
|
965
|
-
if (token?.type === 'string' || token?.type === 'number' || token?.type === 'identifier') {
|
|
966
|
-
if (token.type === 'number') {
|
|
967
|
-
arr.push(parseFloat(token.value));
|
|
968
|
-
} else {
|
|
969
|
-
arr.push(token.value);
|
|
970
|
-
}
|
|
971
|
-
this.advance();
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
if (this.check('punctuation', ',')) {
|
|
975
|
-
this.advance();
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
this.expect('punctuation', ']');
|
|
980
|
-
return arr;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
/**
|
|
984
|
-
* Parse object {...}
|
|
985
|
-
*/
|
|
986
|
-
private parseObject(): Record<string, unknown> {
|
|
987
|
-
const obj: Record<string, unknown> = {};
|
|
988
|
-
this.expect('punctuation', '{');
|
|
989
|
-
|
|
990
|
-
while (!this.check('punctuation', '}') && this.position < this.tokens.length) {
|
|
991
|
-
this.skipNewlines();
|
|
992
|
-
if (this.check('punctuation', '}')) break;
|
|
993
|
-
|
|
994
|
-
const prop = this.parseProperty();
|
|
995
|
-
if (prop) {
|
|
996
|
-
obj[prop.key] = prop.value;
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
this.skipNewlines();
|
|
1000
|
-
if (this.check('punctuation', ',')) {
|
|
1001
|
-
this.advance();
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
this.expect('punctuation', '}');
|
|
1006
|
-
return obj;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
/**
|
|
1010
|
-
* Parse position from value
|
|
1011
|
-
*/
|
|
1012
|
-
private parsePosition(value: unknown): SpatialPosition {
|
|
1013
|
-
if (typeof value === 'object' && value !== null) {
|
|
1014
|
-
const v = value as Record<string, unknown>;
|
|
1015
|
-
return {
|
|
1016
|
-
x: Number(v.x) || 0,
|
|
1017
|
-
y: Number(v.y) || 0,
|
|
1018
|
-
z: Number(v.z) || 0,
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
return { x: 0, y: 0, z: 0 };
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// Helper methods
|
|
1025
|
-
|
|
1026
|
-
private currentToken(): Token | undefined {
|
|
1027
|
-
return this.tokens[this.position];
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
private advance(): Token | undefined {
|
|
1031
|
-
return this.tokens[this.position++];
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
private check(type: string, value?: string): boolean {
|
|
1035
|
-
const token = this.currentToken();
|
|
1036
|
-
if (!token) return false;
|
|
1037
|
-
if (token.type !== type) return false;
|
|
1038
|
-
if (value !== undefined && token.value.toLowerCase() !== value.toLowerCase()) return false;
|
|
1039
|
-
return true;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
private expect(type: string, value?: string): boolean {
|
|
1043
|
-
if (this.check(type, value)) {
|
|
1044
|
-
this.advance();
|
|
1045
|
-
return true;
|
|
1046
|
-
}
|
|
1047
|
-
const token = this.currentToken();
|
|
1048
|
-
this.errors.push({
|
|
1049
|
-
line: token?.line || 0,
|
|
1050
|
-
column: token?.column || 0,
|
|
1051
|
-
message: `Expected ${type}${value ? ` '${value}'` : ''}, got ${token?.type || 'EOF'} '${token?.value || ''}'`,
|
|
1052
|
-
});
|
|
1053
|
-
return false;
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
private expectIdentifier(): string | null {
|
|
1057
|
-
const token = this.currentToken();
|
|
1058
|
-
if (token?.type === 'identifier' || token?.type === 'keyword') {
|
|
1059
|
-
this.advance();
|
|
1060
|
-
return token.value;
|
|
1061
|
-
}
|
|
1062
|
-
this.errors.push({
|
|
1063
|
-
line: token?.line || 0,
|
|
1064
|
-
column: token?.column || 0,
|
|
1065
|
-
message: `Expected identifier, got ${token?.type || 'EOF'}`,
|
|
1066
|
-
});
|
|
1067
|
-
return null;
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
/**
|
|
1071
|
-
* Parse animate command: animate target property: "..." from: 0 to: 1 duration: 1000
|
|
1072
|
-
*/
|
|
1073
|
-
private parseAnimate(): ASTNode | null {
|
|
1074
|
-
this.expect('keyword', 'animate');
|
|
1075
|
-
const target = this.expectIdentifier();
|
|
1076
|
-
if (!target) return null;
|
|
1077
|
-
|
|
1078
|
-
const properties: Record<string, unknown> = {};
|
|
1079
|
-
|
|
1080
|
-
// Parse inline properties
|
|
1081
|
-
while (this.position < this.tokens.length) {
|
|
1082
|
-
this.skipNewlines();
|
|
1083
|
-
const t = this.currentToken();
|
|
1084
|
-
if (!t || t.type === 'newline' || (t.type === 'keyword' && this.keywordSet.has(t.value.toLowerCase()))) break;
|
|
1085
|
-
|
|
1086
|
-
const prop = this.parseProperty();
|
|
1087
|
-
if (prop) {
|
|
1088
|
-
properties[prop.key] = prop.value;
|
|
1089
|
-
} else {
|
|
1090
|
-
break;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
return {
|
|
1095
|
-
type: 'expression-statement',
|
|
1096
|
-
expression: `animate("${target}", ${JSON.stringify(properties)})`,
|
|
1097
|
-
position: { x: 0, y: 0, z: 0 },
|
|
1098
|
-
} as ASTNode;
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
/**
|
|
1102
|
-
* Parse modify command: modify target { prop: value }
|
|
1103
|
-
*/
|
|
1104
|
-
private parseModify(): ASTNode | null {
|
|
1105
|
-
this.expect('keyword', 'modify');
|
|
1106
|
-
const target = this.expectIdentifier();
|
|
1107
|
-
if (!target) return null;
|
|
1108
|
-
|
|
1109
|
-
const properties: Record<string, unknown> = {};
|
|
1110
|
-
|
|
1111
|
-
if (this.check('punctuation', '{')) {
|
|
1112
|
-
this.advance();
|
|
1113
|
-
while (!this.check('punctuation', '}') && this.position < this.tokens.length) {
|
|
1114
|
-
this.skipNewlines();
|
|
1115
|
-
if (this.check('punctuation', '}')) break;
|
|
1116
|
-
|
|
1117
|
-
const prop = this.parseProperty();
|
|
1118
|
-
if (prop) {
|
|
1119
|
-
properties[prop.key] = prop.value;
|
|
1120
|
-
}
|
|
1121
|
-
this.skipNewlines();
|
|
1122
|
-
}
|
|
1123
|
-
this.expect('punctuation', '}');
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
return {
|
|
1127
|
-
type: 'expression-statement',
|
|
1128
|
-
expression: `modify("${target}", ${JSON.stringify(properties)})`,
|
|
1129
|
-
position: { x: 0, y: 0, z: 0 },
|
|
1130
|
-
} as ASTNode;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
/**
|
|
1134
|
-
* Parse UI Element: ui2d dashboard#id { ... }
|
|
1135
|
-
*/
|
|
1136
|
-
private parseUIElement(): ASTNode | null {
|
|
1137
|
-
const typeToken = this.currentToken();
|
|
1138
|
-
if (!typeToken) return null;
|
|
1139
|
-
|
|
1140
|
-
const elementType = typeToken.value;
|
|
1141
|
-
this.advance();
|
|
1142
|
-
|
|
1143
|
-
let elementId = `${elementType}_${Date.now()}`;
|
|
1144
|
-
|
|
1145
|
-
// Check for ID syntax
|
|
1146
|
-
if (this.currentToken()?.type === 'punctuation' && this.currentToken()?.value === '#') {
|
|
1147
|
-
this.advance();
|
|
1148
|
-
const idToken = this.currentToken();
|
|
1149
|
-
if (idToken) {
|
|
1150
|
-
elementId = idToken.value;
|
|
1151
|
-
this.advance();
|
|
1152
|
-
}
|
|
1153
|
-
} else if (this.currentToken()?.type === 'identifier' && this.currentToken()?.value.startsWith('#')) {
|
|
1154
|
-
elementId = this.currentToken()?.value.slice(1) || elementId;
|
|
1155
|
-
this.advance();
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
const properties: Record<string, any> = {};
|
|
1159
|
-
|
|
1160
|
-
if (this.check('punctuation', '{')) {
|
|
1161
|
-
this.advance();
|
|
1162
|
-
while (!this.check('punctuation', '}') && this.position < this.tokens.length) {
|
|
1163
|
-
this.skipNewlines();
|
|
1164
|
-
if (this.check('punctuation', '}')) break;
|
|
1165
|
-
|
|
1166
|
-
const prop = this.parseProperty();
|
|
1167
|
-
if (prop) {
|
|
1168
|
-
properties[prop.key] = prop.value;
|
|
1169
|
-
}
|
|
1170
|
-
this.skipNewlines();
|
|
1171
|
-
}
|
|
1172
|
-
this.expect('punctuation', '}');
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
return {
|
|
1176
|
-
type: 'ui2d',
|
|
1177
|
-
name: elementType,
|
|
1178
|
-
properties: { id: elementId, ...properties },
|
|
1179
|
-
position: { x: 0, y: 0, z: 0 }
|
|
1180
|
-
} as ASTNode;
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
private skipNewlines(): void {
|
|
1184
|
-
while (this.currentToken()?.type === 'newline') {
|
|
1185
|
-
this.advance();
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|