stone-lang 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +52 -0
  2. package/StoneEngine.js +879 -0
  3. package/StoneEngineService.js +1727 -0
  4. package/adapters/FileSystemAdapter.js +230 -0
  5. package/adapters/OutputAdapter.js +208 -0
  6. package/adapters/index.js +6 -0
  7. package/cli/CLIOutputAdapter.js +196 -0
  8. package/cli/DaemonClient.js +349 -0
  9. package/cli/JSONOutputAdapter.js +135 -0
  10. package/cli/ReplSession.js +567 -0
  11. package/cli/ViewerServer.js +590 -0
  12. package/cli/commands/check.js +84 -0
  13. package/cli/commands/daemon.js +189 -0
  14. package/cli/commands/kill.js +66 -0
  15. package/cli/commands/package.js +713 -0
  16. package/cli/commands/ps.js +65 -0
  17. package/cli/commands/run.js +537 -0
  18. package/cli/entry.js +169 -0
  19. package/cli/index.js +14 -0
  20. package/cli/stonec.js +358 -0
  21. package/cli/test-compiler.js +181 -0
  22. package/cli/viewer/index.html +495 -0
  23. package/daemon/IPCServer.js +455 -0
  24. package/daemon/ProcessManager.js +327 -0
  25. package/daemon/ProcessRunner.js +307 -0
  26. package/daemon/daemon.js +398 -0
  27. package/daemon/index.js +16 -0
  28. package/frontend/analysis/index.js +5 -0
  29. package/frontend/analysis/livenessAnalyzer.js +568 -0
  30. package/frontend/analysis/treeShaker.js +265 -0
  31. package/frontend/index.js +20 -0
  32. package/frontend/parsing/astBuilder.js +2196 -0
  33. package/frontend/parsing/index.js +7 -0
  34. package/frontend/parsing/sonParser.js +592 -0
  35. package/frontend/parsing/stoneAstTypes.js +703 -0
  36. package/frontend/parsing/terminal-registry.js +435 -0
  37. package/frontend/parsing/tokenizer.js +692 -0
  38. package/frontend/type-checker/OverloadedFunctionType.js +43 -0
  39. package/frontend/type-checker/TypeEnvironment.js +165 -0
  40. package/frontend/type-checker/bidirectionalInference.js +149 -0
  41. package/frontend/type-checker/index.js +10 -0
  42. package/frontend/type-checker/moduleAnalysis.js +248 -0
  43. package/frontend/type-checker/operatorMappings.js +35 -0
  44. package/frontend/type-checker/overloadResolution.js +605 -0
  45. package/frontend/type-checker/typeChecker.js +452 -0
  46. package/frontend/type-checker/typeCompatibility.js +389 -0
  47. package/frontend/type-checker/visitors/controlFlow.js +483 -0
  48. package/frontend/type-checker/visitors/functions.js +604 -0
  49. package/frontend/type-checker/visitors/index.js +38 -0
  50. package/frontend/type-checker/visitors/literals.js +341 -0
  51. package/frontend/type-checker/visitors/modules.js +159 -0
  52. package/frontend/type-checker/visitors/operators.js +109 -0
  53. package/frontend/type-checker/visitors/statements.js +768 -0
  54. package/frontend/types/index.js +5 -0
  55. package/frontend/types/operatorMap.js +134 -0
  56. package/frontend/types/types.js +2046 -0
  57. package/frontend/utils/errorCollector.js +244 -0
  58. package/frontend/utils/index.js +5 -0
  59. package/frontend/utils/moduleResolver.js +479 -0
  60. package/package.json +50 -0
  61. package/packages/browserCache.js +359 -0
  62. package/packages/fetcher.js +236 -0
  63. package/packages/index.js +130 -0
  64. package/packages/lockfile.js +271 -0
  65. package/packages/manifest.js +291 -0
  66. package/packages/packageResolver.js +356 -0
  67. package/packages/resolver.js +310 -0
  68. package/packages/semver.js +635 -0
@@ -0,0 +1,567 @@
1
+ /**
2
+ * Stone REPL Session
3
+ *
4
+ * Interactive Read-Eval-Print Loop for Stone language.
5
+ * Maintains state between commands and supports meta-commands.
6
+ *
7
+ * Usage:
8
+ * $ stone
9
+ * Stone v1.0.0
10
+ * Type "help" for commands, "exit" to quit.
11
+ *
12
+ * stone> print("hello")
13
+ * hello
14
+ * stone> x = 5
15
+ * stone> x * 2
16
+ * 10
17
+ */
18
+
19
+ import readline from 'readline';
20
+ import fs from 'fs';
21
+ import path from 'path';
22
+ import { StoneEngine } from '../StoneEngine.js';
23
+ import { CLIOutputAdapter } from './CLIOutputAdapter.js';
24
+ import { ReplVM } from '../backends/js-vm/vm/index.js';
25
+ import { Lexer } from '../frontend/parsing/tokenizer.js';
26
+ import { Parser } from '../frontend/parsing/astBuilder.js';
27
+ import { Compiler } from '../backends/js-vm/compiler.js';
28
+
29
+ // ANSI colors
30
+ const colors = {
31
+ reset: '\x1b[0m',
32
+ bold: '\x1b[1m',
33
+ dim: '\x1b[2m',
34
+ red: '\x1b[31m',
35
+ green: '\x1b[32m',
36
+ yellow: '\x1b[33m',
37
+ blue: '\x1b[34m',
38
+ cyan: '\x1b[36m',
39
+ magenta: '\x1b[35m',
40
+ };
41
+
42
+ const c = (color, text) => `${colors[color]}${text}${colors.reset}`;
43
+
44
+ /**
45
+ * Stone REPL Session
46
+ */
47
+ export class ReplSession {
48
+ constructor(options = {}) {
49
+ this.options = options;
50
+ this.version = options.version || '1.0.0';
51
+ this.prompt = options.prompt || 'stone> ';
52
+ this.continuationPrompt = options.continuationPrompt || ' ... ';
53
+
54
+ // Multiline input state
55
+ this.history = [];
56
+ this.multilineBuffer = '';
57
+ this.bracketDepth = 0;
58
+ this.parenDepth = 0;
59
+
60
+ // Working directory for file operations
61
+ this.cwd = process.cwd();
62
+
63
+ // Create output adapter
64
+ this.adapter = new CLIOutputAdapter({
65
+ colorize: true,
66
+ quiet: false,
67
+ });
68
+
69
+ // Create ReplVM for persistent state execution
70
+ this.replVM = new ReplVM({
71
+ outputAdapter: this.adapter,
72
+ });
73
+
74
+ // Create engine for file operations (run command)
75
+ this.engine = new StoneEngine({
76
+ outputAdapter: this.adapter,
77
+ });
78
+
79
+ // Readline interface
80
+ this.rl = null;
81
+ this.running = false;
82
+ }
83
+
84
+ /**
85
+ * Start the REPL session
86
+ */
87
+ async start() {
88
+ this.running = true;
89
+
90
+ // Print banner
91
+ this.printBanner();
92
+
93
+ // Create readline interface
94
+ this.rl = readline.createInterface({
95
+ input: process.stdin,
96
+ output: process.stdout,
97
+ prompt: this.prompt,
98
+ historySize: 1000,
99
+ });
100
+
101
+ // Handle line input
102
+ this.rl.on('line', async (line) => {
103
+ await this.handleLine(line);
104
+ if (this.running) {
105
+ this.rl.prompt();
106
+ }
107
+ });
108
+
109
+ // Handle close (Ctrl+D)
110
+ this.rl.on('close', () => {
111
+ this.exit();
112
+ });
113
+
114
+ // Handle SIGINT (Ctrl+C)
115
+ this.rl.on('SIGINT', () => {
116
+ if (this.multilineBuffer) {
117
+ // Cancel multiline input
118
+ this.multilineBuffer = '';
119
+ this.bracketDepth = 0;
120
+ this.parenDepth = 0;
121
+ console.log('\n' + c('dim', '(input cancelled)'));
122
+ this.rl.setPrompt(this.prompt);
123
+ this.rl.prompt();
124
+ } else {
125
+ console.log('\n' + c('dim', 'Use "exit" or Ctrl+D to quit'));
126
+ this.rl.prompt();
127
+ }
128
+ });
129
+
130
+ // Start prompting
131
+ this.rl.prompt();
132
+ }
133
+
134
+ /**
135
+ * Print welcome banner
136
+ */
137
+ printBanner() {
138
+ console.log('');
139
+ console.log(c('bold', `Stone v${this.version}`));
140
+ console.log(c('dim', 'Type "help" for commands, "exit" to quit.'));
141
+ console.log('');
142
+ }
143
+
144
+ /**
145
+ * Handle a line of input
146
+ */
147
+ async handleLine(line) {
148
+ const trimmed = line.trim();
149
+
150
+ // Handle empty input
151
+ if (!trimmed && !this.multilineBuffer) {
152
+ return;
153
+ }
154
+
155
+ // Check for meta-commands (only if not in multiline mode)
156
+ if (!this.multilineBuffer && trimmed.startsWith(':')) {
157
+ await this.handleMetaCommand(trimmed.slice(1));
158
+ return;
159
+ }
160
+
161
+ // Check for simple commands without colon prefix
162
+ if (!this.multilineBuffer) {
163
+ const firstWord = trimmed.split(/\s+/)[0].toLowerCase();
164
+ if (['help', 'exit', 'quit', 'clear', 'reset', 'vars', 'pwd', 'cd'].includes(firstWord)) {
165
+ await this.handleMetaCommand(trimmed);
166
+ return;
167
+ }
168
+ // Handle 'run' command
169
+ if (firstWord === 'run') {
170
+ await this.handleMetaCommand(trimmed);
171
+ return;
172
+ }
173
+ }
174
+
175
+ // Accumulate multiline input
176
+ this.multilineBuffer += (this.multilineBuffer ? '\n' : '') + line;
177
+
178
+ // Update bracket tracking
179
+ this.updateBracketDepth(line);
180
+
181
+ // Check if input is complete
182
+ if (this.isInputComplete()) {
183
+ const code = this.multilineBuffer;
184
+ this.multilineBuffer = '';
185
+ this.bracketDepth = 0;
186
+ this.parenDepth = 0;
187
+ this.rl.setPrompt(this.prompt);
188
+
189
+ await this.executeCode(code);
190
+ } else {
191
+ // Continue multiline input
192
+ this.rl.setPrompt(this.continuationPrompt);
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Update bracket depth tracking
198
+ */
199
+ updateBracketDepth(line) {
200
+ // Simple bracket counting (doesn't handle strings/comments perfectly)
201
+ for (const char of line) {
202
+ if (char === '{' || char === '[') this.bracketDepth++;
203
+ if (char === '}' || char === ']') this.bracketDepth--;
204
+ if (char === '(') this.parenDepth++;
205
+ if (char === ')') this.parenDepth--;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Check if multiline input is complete
211
+ */
212
+ isInputComplete() {
213
+ // If brackets are balanced, input is complete
214
+ return this.bracketDepth <= 0 && this.parenDepth <= 0;
215
+ }
216
+
217
+ /**
218
+ * Handle meta-commands
219
+ */
220
+ async handleMetaCommand(cmd) {
221
+ const parts = cmd.trim().split(/\s+/);
222
+ const command = parts[0].toLowerCase();
223
+ const args = parts.slice(1);
224
+
225
+ switch (command) {
226
+ case 'help':
227
+ case 'h':
228
+ case '?':
229
+ this.printHelp();
230
+ break;
231
+
232
+ case 'exit':
233
+ case 'quit':
234
+ case 'q':
235
+ this.exit();
236
+ break;
237
+
238
+ case 'clear':
239
+ case 'cls':
240
+ console.clear();
241
+ break;
242
+
243
+ case 'reset':
244
+ this.replVM.reset();
245
+ console.log(c('dim', 'State cleared'));
246
+ break;
247
+
248
+ case 'vars':
249
+ case 'variables':
250
+ this.printVariables();
251
+ break;
252
+
253
+ case 'run':
254
+ case 'r':
255
+ if (args.length === 0) {
256
+ console.log(c('red', 'Usage: run <filename.stn>'));
257
+ } else {
258
+ await this.runFile(args[0]);
259
+ }
260
+ break;
261
+
262
+ case 'load':
263
+ case 'l':
264
+ if (args.length === 0) {
265
+ console.log(c('red', 'Usage: load <filename.stn>'));
266
+ } else {
267
+ await this.loadFile(args[0]);
268
+ }
269
+ break;
270
+
271
+ case 'pwd':
272
+ console.log(this.cwd);
273
+ break;
274
+
275
+ case 'cd':
276
+ if (args.length === 0) {
277
+ this.cwd = process.cwd();
278
+ } else {
279
+ const newPath = path.resolve(this.cwd, args[0]);
280
+ if (fs.existsSync(newPath) && fs.statSync(newPath).isDirectory()) {
281
+ this.cwd = newPath;
282
+ } else {
283
+ console.log(c('red', `Directory not found: ${args[0]}`));
284
+ }
285
+ }
286
+ break;
287
+
288
+ case 'ls':
289
+ case 'dir':
290
+ try {
291
+ const files = fs.readdirSync(this.cwd);
292
+ const stoneFiles = files.filter(f => f.endsWith('.stn'));
293
+ const otherFiles = files.filter(f => !f.endsWith('.stn'));
294
+
295
+ if (stoneFiles.length > 0) {
296
+ console.log(c('cyan', 'Stone files:'));
297
+ stoneFiles.forEach(f => console.log(' ' + c('green', f)));
298
+ }
299
+ if (otherFiles.length > 0) {
300
+ console.log(c('dim', 'Other:'));
301
+ otherFiles.slice(0, 10).forEach(f => console.log(' ' + f));
302
+ if (otherFiles.length > 10) {
303
+ console.log(c('dim', ` ... and ${otherFiles.length - 10} more`));
304
+ }
305
+ }
306
+ } catch (e) {
307
+ console.log(c('red', `Error: ${e.message}`));
308
+ }
309
+ break;
310
+
311
+ default:
312
+ console.log(c('red', `Unknown command: ${command}`));
313
+ console.log(c('dim', 'Type "help" for available commands'));
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Execute Stone code using ReplVM with incremental compilation
319
+ */
320
+ async executeCode(code) {
321
+ try {
322
+ // 1. Parse the code
323
+ const lexer = new Lexer(code, '<repl>');
324
+ const tokens = lexer.tokenize();
325
+ const parser = new Parser(tokens, '<repl>');
326
+ const ast = parser.parse();
327
+
328
+ // 2. Compile with incremental compilation (preserving existing state)
329
+ const compiler = new Compiler({
330
+ existingLocals: this.replVM.getExistingLocals(),
331
+ existingFunctions: this.replVM.getExistingFunctions(),
332
+ });
333
+ const compiled = compiler.compile(ast);
334
+
335
+ if (!compiled.success) {
336
+ const errorMsg = compiled.errors.map(e => e.message).join('\n');
337
+ console.log(c('red', `Compile error: ${errorMsg}`));
338
+ return;
339
+ }
340
+
341
+ // 3. Execute with ReplVM (maintains state between commands)
342
+ const result = this.replVM.executeCommand(compiled);
343
+
344
+ if (result.success) {
345
+ // Print result if it's an expression (not assignment/statement)
346
+ if (result.result !== undefined && result.result !== null) {
347
+ // Don't print if it was just printed by the script
348
+ const isPrintStatement = code.trim().startsWith('print(');
349
+ if (!isPrintStatement) {
350
+ this.printValue(result.result);
351
+ }
352
+ }
353
+ } else {
354
+ console.log(c('red', `Error: ${result.error}`));
355
+ }
356
+ } catch (error) {
357
+ console.log(c('red', `Error: ${error.message}`));
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Print a value with formatting
363
+ */
364
+ printValue(value) {
365
+ if (value === undefined || value === null) {
366
+ return;
367
+ }
368
+
369
+ if (typeof value === 'number') {
370
+ console.log(c('yellow', String(value)));
371
+ } else if (typeof value === 'string') {
372
+ console.log(c('green', `"${value}"`));
373
+ } else if (typeof value === 'boolean') {
374
+ console.log(c('magenta', String(value)));
375
+ } else if (Array.isArray(value)) {
376
+ console.log(c('cyan', JSON.stringify(value)));
377
+ } else if (value && value._type === 'StoneArray') {
378
+ console.log(c('cyan', value.toString()));
379
+ } else if (typeof value === 'object') {
380
+ console.log(c('cyan', JSON.stringify(value, null, 2)));
381
+ } else {
382
+ console.log(String(value));
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Run a Stone file
388
+ */
389
+ async runFile(filename) {
390
+ const fullPath = path.resolve(this.cwd, filename);
391
+
392
+ if (!fs.existsSync(fullPath)) {
393
+ console.log(c('red', `File not found: ${filename}`));
394
+ return;
395
+ }
396
+
397
+ try {
398
+ const source = fs.readFileSync(fullPath, 'utf8');
399
+ console.log(c('dim', `Running ${filename}...`));
400
+ console.log('');
401
+
402
+ // Get current REPL variables to pass to file execution
403
+ const currentVars = {};
404
+ for (const name of this.replVM.getVariableNames()) {
405
+ currentVars[name] = this.replVM.getVariable(name);
406
+ }
407
+
408
+ const result = await this.engine.execute(source, {
409
+ filename: fullPath,
410
+ currentPath: path.dirname(fullPath),
411
+ variables: currentVars,
412
+ });
413
+
414
+ if (result.success) {
415
+ console.log('');
416
+ console.log(c('green', `Completed in ${result.duration || 0}ms`));
417
+ } else {
418
+ console.log(c('red', `Execution failed: ${result.error}`));
419
+ }
420
+ } catch (error) {
421
+ console.log(c('red', `Error: ${error.message}`));
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Load a file into the current context (execute and import variables)
427
+ */
428
+ async loadFile(filename) {
429
+ const fullPath = path.resolve(this.cwd, filename);
430
+
431
+ if (!fs.existsSync(fullPath)) {
432
+ console.log(c('red', `File not found: ${filename}`));
433
+ return;
434
+ }
435
+
436
+ try {
437
+ const source = fs.readFileSync(fullPath, 'utf8');
438
+ console.log(c('dim', `Loading ${filename}...`));
439
+
440
+ // Track variable count before loading
441
+ const varCountBefore = this.replVM.getVariableNames().length;
442
+
443
+ // Parse and compile with incremental compilation
444
+ const lexer = new Lexer(source, fullPath);
445
+ const tokens = lexer.tokenize();
446
+ const parser = new Parser(tokens, fullPath);
447
+ const ast = parser.parse();
448
+
449
+ const compiler = new Compiler({
450
+ existingLocals: this.replVM.getExistingLocals(),
451
+ existingFunctions: this.replVM.getExistingFunctions(),
452
+ });
453
+ const compiled = compiler.compile(ast);
454
+
455
+ if (!compiled.success) {
456
+ const errorMsg = compiled.errors.map(e => e.message).join('\n');
457
+ console.log(c('red', `Compile error: ${errorMsg}`));
458
+ return;
459
+ }
460
+
461
+ // Execute through ReplVM to load variables into current context
462
+ const result = this.replVM.executeCommand(compiled);
463
+
464
+ if (result.success) {
465
+ const varCountAfter = this.replVM.getVariableNames().length;
466
+ const newVarCount = varCountAfter - varCountBefore;
467
+ console.log(c('green', `Loaded ${newVarCount >= 0 ? newVarCount : varCountAfter} variable(s)`));
468
+ } else {
469
+ console.log(c('red', `Load failed: ${result.error}`));
470
+ }
471
+ } catch (error) {
472
+ console.log(c('red', `Error: ${error.message}`));
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Print available variables
478
+ */
479
+ printVariables() {
480
+ const vars = this.replVM.getVariableNames();
481
+
482
+ if (vars.length === 0) {
483
+ console.log(c('dim', 'No variables defined'));
484
+ return;
485
+ }
486
+
487
+ console.log(c('bold', 'Variables:'));
488
+ for (const name of vars) {
489
+ const value = this.replVM.getVariable(name);
490
+ const type = typeof value;
491
+ const preview = this.getValuePreview(value);
492
+ console.log(` ${c('cyan', name)}: ${c('dim', type)} = ${preview}`);
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Get a short preview of a value
498
+ */
499
+ getValuePreview(value) {
500
+ if (value === undefined) return c('dim', 'undefined');
501
+ if (value === null) return c('dim', 'null');
502
+ if (typeof value === 'number') return c('yellow', String(value));
503
+ if (typeof value === 'string') {
504
+ if (value.length > 30) {
505
+ return c('green', `"${value.slice(0, 30)}..."`);
506
+ }
507
+ return c('green', `"${value}"`);
508
+ }
509
+ if (typeof value === 'boolean') return c('magenta', String(value));
510
+ if (typeof value === 'function') return c('blue', '[function]');
511
+ if (Array.isArray(value)) {
512
+ return c('cyan', `[${value.length} items]`);
513
+ }
514
+ if (value && value._type === 'StoneArray') {
515
+ return c('cyan', `StoneArray(${value.shape.join('x')})`);
516
+ }
517
+ if (typeof value === 'object') {
518
+ return c('cyan', `{${Object.keys(value).length} keys}`);
519
+ }
520
+ return String(value);
521
+ }
522
+
523
+ /**
524
+ * Print help
525
+ */
526
+ printHelp() {
527
+ console.log('');
528
+ console.log(c('bold', 'Stone REPL Commands:'));
529
+ console.log('');
530
+ console.log(' ' + c('cyan', 'help, ?') + ' Show this help');
531
+ console.log(' ' + c('cyan', 'exit, quit') + ' Exit the REPL');
532
+ console.log(' ' + c('cyan', 'clear') + ' Clear the screen');
533
+ console.log(' ' + c('cyan', 'reset') + ' Clear all variables');
534
+ console.log(' ' + c('cyan', 'vars') + ' Show defined variables');
535
+ console.log('');
536
+ console.log(c('bold', 'File Commands:'));
537
+ console.log('');
538
+ console.log(' ' + c('cyan', 'run <file>') + ' Execute a Stone script');
539
+ console.log(' ' + c('cyan', 'load <file>') + ' Load script into current context');
540
+ console.log(' ' + c('cyan', 'ls, dir') + ' List files in current directory');
541
+ console.log(' ' + c('cyan', 'cd <dir>') + ' Change directory');
542
+ console.log(' ' + c('cyan', 'pwd') + ' Print working directory');
543
+ console.log('');
544
+ console.log(c('bold', 'Keyboard:'));
545
+ console.log('');
546
+ console.log(' ' + c('cyan', 'Ctrl+C') + ' Cancel current input');
547
+ console.log(' ' + c('cyan', 'Ctrl+D') + ' Exit the REPL');
548
+ console.log(' ' + c('cyan', 'Up/Down') + ' Navigate history');
549
+ console.log('');
550
+ }
551
+
552
+ /**
553
+ * Exit the REPL
554
+ */
555
+ exit() {
556
+ if (!this.running) return; // Already exiting
557
+ this.running = false;
558
+ console.log('');
559
+ console.log(c('dim', 'Goodbye!'));
560
+ if (this.rl) {
561
+ this.rl.close();
562
+ }
563
+ process.exit(0);
564
+ }
565
+ }
566
+
567
+ export default ReplSession;