@holoscript/core 1.0.0-alpha.2 ā 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/HoloScript2DParser.js +227 -0
- package/src/HoloScript2DParser.ts +5 -0
- package/src/HoloScriptCodeParser.js +1102 -0
- package/src/HoloScriptCodeParser.ts +145 -20
- package/src/HoloScriptDebugger.js +458 -0
- package/src/HoloScriptParser.js +338 -0
- package/src/HoloScriptPlusParser.js +371 -0
- package/src/HoloScriptPlusParser.ts +543 -0
- package/src/HoloScriptRuntime.js +1399 -0
- package/src/HoloScriptRuntime.test.js +351 -0
- package/src/HoloScriptRuntime.ts +17 -3
- package/src/HoloScriptTypeChecker.js +356 -0
- package/src/__tests__/GraphicsServices.test.js +357 -0
- package/src/__tests__/GraphicsServices.test.ts +427 -0
- package/src/__tests__/HoloScriptPlusParser.test.js +317 -0
- package/src/__tests__/HoloScriptPlusParser.test.ts +392 -0
- package/src/__tests__/integration.test.js +336 -0
- package/src/__tests__/performance.bench.js +218 -0
- package/src/__tests__/type-checker.test.js +60 -0
- package/src/__tests__/type-checker.test.ts +73 -0
- package/src/index.js +217 -0
- package/src/index.ts +158 -18
- package/src/interop/Interoperability.js +413 -0
- package/src/interop/Interoperability.ts +494 -0
- package/src/logger.js +42 -0
- package/src/parser/EnhancedParser.js +205 -0
- package/src/parser/EnhancedParser.ts +251 -0
- package/src/parser/HoloScriptPlusParser.js +928 -0
- package/src/parser/HoloScriptPlusParser.ts +1089 -0
- package/src/runtime/HoloScriptPlusRuntime.js +674 -0
- package/src/runtime/HoloScriptPlusRuntime.ts +861 -0
- package/src/runtime/PerformanceTelemetry.js +323 -0
- package/src/runtime/PerformanceTelemetry.ts +467 -0
- package/src/runtime/RuntimeOptimization.js +361 -0
- package/src/runtime/RuntimeOptimization.ts +416 -0
- package/src/services/HololandGraphicsPipelineService.js +506 -0
- package/src/services/HololandGraphicsPipelineService.ts +662 -0
- package/src/services/PlatformPerformanceOptimizer.js +356 -0
- package/src/services/PlatformPerformanceOptimizer.ts +503 -0
- package/src/state/ReactiveState.js +427 -0
- package/src/state/ReactiveState.ts +572 -0
- package/src/tools/DeveloperExperience.js +376 -0
- package/src/tools/DeveloperExperience.ts +438 -0
- package/src/traits/AIDriverTrait.js +322 -0
- package/src/traits/AIDriverTrait.test.js +329 -0
- package/src/traits/AIDriverTrait.test.ts +357 -0
- package/src/traits/AIDriverTrait.ts +474 -0
- package/src/traits/LightingTrait.js +313 -0
- package/src/traits/LightingTrait.test.js +410 -0
- package/src/traits/LightingTrait.test.ts +462 -0
- package/src/traits/LightingTrait.ts +505 -0
- package/src/traits/MaterialTrait.js +194 -0
- package/src/traits/MaterialTrait.test.js +286 -0
- package/src/traits/MaterialTrait.test.ts +329 -0
- package/src/traits/MaterialTrait.ts +324 -0
- package/src/traits/RenderingTrait.js +356 -0
- package/src/traits/RenderingTrait.test.js +363 -0
- package/src/traits/RenderingTrait.test.ts +427 -0
- package/src/traits/RenderingTrait.ts +555 -0
- package/src/traits/VRTraitSystem.js +740 -0
- package/src/traits/VRTraitSystem.ts +1040 -0
- package/src/traits/VoiceInputTrait.js +284 -0
- package/src/traits/VoiceInputTrait.test.js +226 -0
- package/src/traits/VoiceInputTrait.test.ts +252 -0
- package/src/traits/VoiceInputTrait.ts +401 -0
- package/src/types/AdvancedTypeSystem.js +226 -0
- package/src/types/AdvancedTypeSystem.ts +494 -0
- package/src/types/HoloScriptPlus.d.ts +853 -0
- package/src/types.js +6 -0
- package/src/types.ts +96 -1
- package/tsconfig.json +1 -1
- package/tsup.config.d.ts +2 -0
- package/tsup.config.js +18 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @holoscript/core Developer Experience
|
|
3
|
+
*
|
|
4
|
+
* REPL, better error formatting, interactive mode
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as readline from 'readline';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Enhanced error formatter
|
|
11
|
+
*/
|
|
12
|
+
export class ErrorFormatter {
|
|
13
|
+
/**
|
|
14
|
+
* Format error with source context
|
|
15
|
+
*/
|
|
16
|
+
static formatError(error: any, sourceCode?: string): string {
|
|
17
|
+
const { message, location, suggestion, token } = error;
|
|
18
|
+
|
|
19
|
+
let formatted = `\nā Error: ${message}\n`;
|
|
20
|
+
|
|
21
|
+
if (location) {
|
|
22
|
+
formatted += ` at line ${location.line}, column ${location.column}\n`;
|
|
23
|
+
|
|
24
|
+
if (sourceCode) {
|
|
25
|
+
const lines = sourceCode.split('\n');
|
|
26
|
+
const errorLine = lines[location.line - 1];
|
|
27
|
+
|
|
28
|
+
if (errorLine) {
|
|
29
|
+
formatted += `\n ${location.line} | ${errorLine}\n`;
|
|
30
|
+
formatted += ` | ${' '.repeat(location.column - 1)}^\n`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (suggestion) {
|
|
36
|
+
formatted += `\nš” Suggestion: ${suggestion}\n`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return formatted;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Format multiple errors
|
|
44
|
+
*/
|
|
45
|
+
static formatErrors(errors: any[]): string {
|
|
46
|
+
if (errors.length === 0) return '';
|
|
47
|
+
|
|
48
|
+
let formatted = `\nā Found ${errors.length} error${errors.length !== 1 ? 's' : ''}:\n`;
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < Math.min(errors.length, 5); i++) {
|
|
51
|
+
formatted += this.formatError(errors[i]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (errors.length > 5) {
|
|
55
|
+
formatted += `\n... and ${errors.length - 5} more error${errors.length - 5 !== 1 ? 's' : ''}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return formatted;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Format success message
|
|
63
|
+
*/
|
|
64
|
+
static formatSuccess(message: string, details?: any): string {
|
|
65
|
+
let formatted = `\nā
${message}\n`;
|
|
66
|
+
|
|
67
|
+
if (details) {
|
|
68
|
+
if (typeof details === 'object') {
|
|
69
|
+
formatted += JSON.stringify(details, null, 2) + '\n';
|
|
70
|
+
} else {
|
|
71
|
+
formatted += `${details}\n`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return formatted;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Format help text
|
|
80
|
+
*/
|
|
81
|
+
static formatHelp(): string {
|
|
82
|
+
return `
|
|
83
|
+
HoloScript+ REPL v1.0.0
|
|
84
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
85
|
+
|
|
86
|
+
Commands:
|
|
87
|
+
help - Show this help message
|
|
88
|
+
clear - Clear the screen
|
|
89
|
+
vars - List all variables
|
|
90
|
+
types - List all types
|
|
91
|
+
profile - Show performance profile
|
|
92
|
+
exit - Exit REPL
|
|
93
|
+
|
|
94
|
+
Shortcuts:
|
|
95
|
+
.create orb#name - Create orb
|
|
96
|
+
.position x y z - Set position
|
|
97
|
+
.property key val - Set property
|
|
98
|
+
|
|
99
|
+
Examples:
|
|
100
|
+
> orb#myOrb { position: [0, 0, 0] }
|
|
101
|
+
> myOrb.position = [1, 1, 1]
|
|
102
|
+
> match state { "idle" => { ... } }
|
|
103
|
+
|
|
104
|
+
For more info, visit: https://github.com/brianonbased-dev/holoscript
|
|
105
|
+
`.trim();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Interactive REPL
|
|
111
|
+
*/
|
|
112
|
+
export class HoloScriptREPL {
|
|
113
|
+
private rl: readline.Interface;
|
|
114
|
+
private variables: Map<string, any> = new Map();
|
|
115
|
+
private types: Map<string, any> = new Map();
|
|
116
|
+
private history: string[] = [];
|
|
117
|
+
private parser: any;
|
|
118
|
+
private runtime: any;
|
|
119
|
+
|
|
120
|
+
constructor(parser: any, runtime: any) {
|
|
121
|
+
this.parser = parser;
|
|
122
|
+
this.runtime = runtime;
|
|
123
|
+
|
|
124
|
+
this.rl = readline.createInterface({
|
|
125
|
+
input: process.stdin,
|
|
126
|
+
output: process.stdout,
|
|
127
|
+
history: this.history,
|
|
128
|
+
historySize: 100,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Start REPL
|
|
134
|
+
*/
|
|
135
|
+
async start(): Promise<void> {
|
|
136
|
+
console.log('\nš„½ HoloScript+ REPL v1.0.0');
|
|
137
|
+
console.log('Type "help" for commands, "exit" to quit\n');
|
|
138
|
+
|
|
139
|
+
await this.repl();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Main REPL loop
|
|
144
|
+
*/
|
|
145
|
+
private async repl(): Promise<void> {
|
|
146
|
+
const prompt = () => {
|
|
147
|
+
this.rl.question('> ', async (input) => {
|
|
148
|
+
try {
|
|
149
|
+
const trimmed = input.trim();
|
|
150
|
+
|
|
151
|
+
if (!trimmed) {
|
|
152
|
+
prompt();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle commands
|
|
157
|
+
if (trimmed.startsWith('.')) {
|
|
158
|
+
this.handleCommand(trimmed);
|
|
159
|
+
prompt();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Handle built-in commands
|
|
164
|
+
switch (trimmed.toLowerCase()) {
|
|
165
|
+
case 'help':
|
|
166
|
+
console.log(ErrorFormatter.formatHelp());
|
|
167
|
+
prompt();
|
|
168
|
+
return;
|
|
169
|
+
|
|
170
|
+
case 'clear':
|
|
171
|
+
console.clear();
|
|
172
|
+
prompt();
|
|
173
|
+
return;
|
|
174
|
+
|
|
175
|
+
case 'vars':
|
|
176
|
+
this.showVariables();
|
|
177
|
+
prompt();
|
|
178
|
+
return;
|
|
179
|
+
|
|
180
|
+
case 'types':
|
|
181
|
+
this.showTypes();
|
|
182
|
+
prompt();
|
|
183
|
+
return;
|
|
184
|
+
|
|
185
|
+
case 'profile':
|
|
186
|
+
this.showProfile();
|
|
187
|
+
prompt();
|
|
188
|
+
return;
|
|
189
|
+
|
|
190
|
+
case 'exit':
|
|
191
|
+
this.rl.close();
|
|
192
|
+
console.log('\nš Goodbye!\n');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Parse and execute
|
|
197
|
+
const result = await this.evaluate(trimmed);
|
|
198
|
+
|
|
199
|
+
if (result !== undefined && result !== null) {
|
|
200
|
+
this.displayResult(result);
|
|
201
|
+
}
|
|
202
|
+
} catch (error: any) {
|
|
203
|
+
console.error(ErrorFormatter.formatError(error));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
prompt();
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
prompt();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Handle dot commands
|
|
215
|
+
*/
|
|
216
|
+
private handleCommand(command: string): void {
|
|
217
|
+
const parts = command.slice(1).split(' ');
|
|
218
|
+
const cmd = parts[0];
|
|
219
|
+
|
|
220
|
+
switch (cmd) {
|
|
221
|
+
case 'create':
|
|
222
|
+
console.log(`Creating: ${parts.slice(1).join(' ')}`);
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case 'position':
|
|
226
|
+
console.log(`Position: x=${parts[1]}, y=${parts[2]}, z=${parts[3]}`);
|
|
227
|
+
break;
|
|
228
|
+
|
|
229
|
+
case 'property':
|
|
230
|
+
console.log(`Property: ${parts[1]} = ${parts[2]}`);
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
default:
|
|
234
|
+
console.log(`Unknown command: .${cmd}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Evaluate HoloScript code
|
|
240
|
+
*/
|
|
241
|
+
private async evaluate(code: string): Promise<any> {
|
|
242
|
+
try {
|
|
243
|
+
const parseResult = this.parser.parse(code);
|
|
244
|
+
|
|
245
|
+
if (parseResult.errors && parseResult.errors.length > 0) {
|
|
246
|
+
throw {
|
|
247
|
+
message: 'Parse error',
|
|
248
|
+
errors: parseResult.errors,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const result = await this.runtime.execute(parseResult.ast);
|
|
253
|
+
return result;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Show variables
|
|
261
|
+
*/
|
|
262
|
+
private showVariables(): void {
|
|
263
|
+
if (this.variables.size === 0) {
|
|
264
|
+
console.log('No variables defined');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
console.log('\nš¦ Variables:');
|
|
269
|
+
for (const [name, value] of this.variables) {
|
|
270
|
+
console.log(` ${name}: ${this.formatValue(value)}`);
|
|
271
|
+
}
|
|
272
|
+
console.log();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Show types
|
|
277
|
+
*/
|
|
278
|
+
private showTypes(): void {
|
|
279
|
+
if (this.types.size === 0) {
|
|
280
|
+
console.log('No custom types defined');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.log('\nš·ļø Types:');
|
|
285
|
+
for (const [name, type] of this.types) {
|
|
286
|
+
console.log(` ${name}: ${JSON.stringify(type)}`);
|
|
287
|
+
}
|
|
288
|
+
console.log();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Show performance profile
|
|
293
|
+
*/
|
|
294
|
+
private showProfile(): void {
|
|
295
|
+
console.log('\nā±ļø Performance Profile:');
|
|
296
|
+
console.log(' (Profiling data would be displayed here)');
|
|
297
|
+
console.log();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Display result
|
|
302
|
+
*/
|
|
303
|
+
private displayResult(result: any): void {
|
|
304
|
+
const formatted = this.formatValue(result);
|
|
305
|
+
console.log(`=> ${formatted}`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Format value for display
|
|
310
|
+
*/
|
|
311
|
+
private formatValue(value: any): string {
|
|
312
|
+
if (value === null) return 'null';
|
|
313
|
+
if (value === undefined) return 'undefined';
|
|
314
|
+
if (typeof value === 'string') return `"${value}"`;
|
|
315
|
+
if (typeof value === 'number') return `${value}`;
|
|
316
|
+
if (typeof value === 'boolean') return `${value}`;
|
|
317
|
+
if (Array.isArray(value)) {
|
|
318
|
+
return `[ ${value.map((v) => this.formatValue(v)).join(', ')} ]`;
|
|
319
|
+
}
|
|
320
|
+
if (typeof value === 'object') {
|
|
321
|
+
const pairs = Object.entries(value)
|
|
322
|
+
.map(([k, v]) => `${k}: ${this.formatValue(v)}`)
|
|
323
|
+
.join(', ');
|
|
324
|
+
return `{ ${pairs} }`;
|
|
325
|
+
}
|
|
326
|
+
return String(value);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Create and start REPL
|
|
332
|
+
*/
|
|
333
|
+
export async function startREPL(parser: any, runtime: any): Promise<void> {
|
|
334
|
+
const repl = new HoloScriptREPL(parser, runtime);
|
|
335
|
+
await repl.start();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Hot reload watcher for development
|
|
340
|
+
*/
|
|
341
|
+
export class HotReloadWatcher {
|
|
342
|
+
private watchers: Map<string, any> = new Map();
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Watch file for changes
|
|
346
|
+
*/
|
|
347
|
+
watch(filePath: string, callback: () => Promise<void>): void {
|
|
348
|
+
// Using basic fs.watch (production would use chokidar)
|
|
349
|
+
const fs = require('fs');
|
|
350
|
+
|
|
351
|
+
fs.watchFile(filePath, { interval: 1000 }, async () => {
|
|
352
|
+
try {
|
|
353
|
+
console.log(`\nš Reloading: ${filePath}`);
|
|
354
|
+
await callback();
|
|
355
|
+
console.log('ā
Reload complete\n');
|
|
356
|
+
} catch (error: any) {
|
|
357
|
+
console.error(ErrorFormatter.formatError(error));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
this.watchers.set(filePath, true);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Stop watching
|
|
366
|
+
*/
|
|
367
|
+
unwatch(filePath: string): void {
|
|
368
|
+
const fs = require('fs');
|
|
369
|
+
fs.unwatchFile(filePath);
|
|
370
|
+
this.watchers.delete(filePath);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Stop all watchers
|
|
375
|
+
*/
|
|
376
|
+
clear(): void {
|
|
377
|
+
const fs = require('fs');
|
|
378
|
+
for (const filePath of this.watchers.keys()) {
|
|
379
|
+
fs.unwatchFile(filePath);
|
|
380
|
+
}
|
|
381
|
+
this.watchers.clear();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Source map generator
|
|
387
|
+
*/
|
|
388
|
+
export class SourceMapGenerator {
|
|
389
|
+
private mappings: Array<{
|
|
390
|
+
generatedLine: number;
|
|
391
|
+
generatedColumn: number;
|
|
392
|
+
sourceLine: number;
|
|
393
|
+
sourceColumn: number;
|
|
394
|
+
name?: string;
|
|
395
|
+
}> = [];
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Add mapping
|
|
399
|
+
*/
|
|
400
|
+
addMapping(
|
|
401
|
+
generatedLine: number,
|
|
402
|
+
generatedColumn: number,
|
|
403
|
+
sourceLine: number,
|
|
404
|
+
sourceColumn: number,
|
|
405
|
+
name?: string
|
|
406
|
+
): void {
|
|
407
|
+
this.mappings.push({
|
|
408
|
+
generatedLine,
|
|
409
|
+
generatedColumn,
|
|
410
|
+
sourceLine,
|
|
411
|
+
sourceColumn,
|
|
412
|
+
name,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Generate source map
|
|
418
|
+
*/
|
|
419
|
+
generate(sourceFile: string, generatedFile: string): any {
|
|
420
|
+
return {
|
|
421
|
+
version: 3,
|
|
422
|
+
file: generatedFile,
|
|
423
|
+
sourceRoot: '',
|
|
424
|
+
sources: [sourceFile],
|
|
425
|
+
sourcesContent: [],
|
|
426
|
+
mappings: this.encodeMappings(),
|
|
427
|
+
names: [],
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Encode mappings in VLQ format
|
|
433
|
+
*/
|
|
434
|
+
private encodeMappings(): string {
|
|
435
|
+
// Simplified VLQ encoding
|
|
436
|
+
return this.mappings.map((m) => `${m.generatedLine}:${m.generatedColumn}->${m.sourceLine}:${m.sourceColumn}`).join(';');
|
|
437
|
+
}
|
|
438
|
+
}
|