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,568 @@
1
+ /**
2
+ * Liveness Analyzer for Stone
3
+ *
4
+ * Performs backward dataflow analysis to determine the last use of each variable.
5
+ * Stone's semantics enable precise analysis:
6
+ * - Immutable bindings → values never change after definition
7
+ * - No shadowing → each name = one value in scope
8
+ * - Whole-program analysis → we can see all uses at compile time
9
+ *
10
+ * Output is backend-agnostic, reusable for:
11
+ * - JS VM: CLEAR_LOCAL opcode emission
12
+ * - WASM codegen: Linear memory management
13
+ * - C codegen: Stack allocation decisions
14
+ *
15
+ * @example
16
+ * const analyzer = new LivenessAnalyzer();
17
+ * const info = analyzer.analyze(ast);
18
+ * // info.bindings: Map of name → binding metadata
19
+ * // info.isLastUse(name, node): Check if node is the last use of variable
20
+ */
21
+
22
+ /**
23
+ * @typedef {Object} BindingInfo
24
+ * @property {string} name - Variable name
25
+ * @property {string} scope - Scope identifier (e.g., "function:main", "block:1")
26
+ * @property {Object} definedAt - Location where the binding is defined
27
+ * @property {Object|null} lastUseAt - Location of the last use (null if never used)
28
+ * @property {Array} uses - Array of all use locations
29
+ * @property {boolean} escapes - Whether the variable escapes via closure
30
+ * @property {boolean} canStackAlloc - Whether safe for stack allocation
31
+ * @property {string} type - Type of value if known
32
+ */
33
+
34
+ /**
35
+ * @typedef {Object} LivenessInfo
36
+ * @property {Map<string, BindingInfo>} bindings - Map of binding key to binding info
37
+ * @property {function(string, Object): boolean} isLastUse - Check if a node is the last use
38
+ * @property {function(Object): string|null} getLastUseName - Get variable name if this is its last use
39
+ */
40
+
41
+ export class LivenessAnalyzer {
42
+ constructor() {
43
+ this.bindings = new Map(); // key -> BindingInfo
44
+ this.currentScope = [];
45
+ this.scopeCounter = 0;
46
+ this.lastUseMap = new Map(); // nodeId -> { name, scopeKey }
47
+ }
48
+
49
+ /**
50
+ * Generate a unique key for a binding
51
+ * @param {string} name - Variable name
52
+ * @param {string} scope - Scope identifier
53
+ * @returns {string} Unique key
54
+ */
55
+ _bindingKey(name, scope) {
56
+ return `${scope}:${name}`;
57
+ }
58
+
59
+ /**
60
+ * Get current scope identifier
61
+ * @returns {string}
62
+ */
63
+ _currentScopeId() {
64
+ return this.currentScope.length > 0
65
+ ? this.currentScope[this.currentScope.length - 1]
66
+ : 'global';
67
+ }
68
+
69
+ /**
70
+ * Generate a node ID for tracking last uses
71
+ * @param {Object} node - AST node
72
+ * @returns {string}
73
+ */
74
+ _nodeId(node) {
75
+ if (!node) return 'unknown';
76
+ const loc = node.location || node.loc || {};
77
+ return `${loc.start?.line || 0}:${loc.start?.column || 0}:${node.type || 'node'}`;
78
+ }
79
+
80
+ /**
81
+ * Enter a new scope
82
+ * @param {string} type - Scope type (function, block, loop, etc.)
83
+ * @param {string} name - Optional scope name
84
+ */
85
+ _enterScope(type, name = '') {
86
+ const scopeId = `${type}:${name || this.scopeCounter++}`;
87
+ this.currentScope.push(scopeId);
88
+ }
89
+
90
+ /**
91
+ * Exit current scope
92
+ */
93
+ _exitScope() {
94
+ this.currentScope.pop();
95
+ }
96
+
97
+ /**
98
+ * Record a binding definition
99
+ * @param {string} name - Variable name
100
+ * @param {Object} node - AST node where it's defined
101
+ * @param {string} type - Optional type annotation
102
+ */
103
+ _recordDefinition(name, node, type = 'any') {
104
+ const scope = this._currentScopeId();
105
+ const key = this._bindingKey(name, scope);
106
+
107
+ const loc = node.location || node.loc || {};
108
+ this.bindings.set(key, {
109
+ name,
110
+ scope,
111
+ definedAt: {
112
+ line: loc.start?.line || 0,
113
+ column: loc.start?.column || 0,
114
+ },
115
+ lastUseAt: null,
116
+ uses: [],
117
+ escapes: false,
118
+ canStackAlloc: true,
119
+ type,
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Record a variable use
125
+ * @param {string} name - Variable name
126
+ * @param {Object} node - AST node where it's used
127
+ */
128
+ _recordUse(name, node) {
129
+ // Find the binding in current or enclosing scopes
130
+ let key = null;
131
+ for (let i = this.currentScope.length - 1; i >= 0; i--) {
132
+ const tryKey = this._bindingKey(name, this.currentScope[i]);
133
+ if (this.bindings.has(tryKey)) {
134
+ key = tryKey;
135
+ break;
136
+ }
137
+ }
138
+
139
+ // Check global scope
140
+ if (!key) {
141
+ const globalKey = this._bindingKey(name, 'global');
142
+ if (this.bindings.has(globalKey)) {
143
+ key = globalKey;
144
+ }
145
+ }
146
+
147
+ if (!key) return; // Unknown variable (might be builtin or external)
148
+
149
+ const binding = this.bindings.get(key);
150
+ const loc = node.location || node.loc || {};
151
+ const useInfo = {
152
+ line: loc.start?.line || 0,
153
+ column: loc.start?.column || 0,
154
+ };
155
+
156
+ binding.uses.push(useInfo);
157
+ binding.lastUseAt = useInfo; // Will be overwritten by later uses
158
+
159
+ // Track for isLastUse lookup
160
+ const nodeId = this._nodeId(node);
161
+ this.lastUseMap.set(nodeId, { name, scopeKey: key, binding });
162
+ }
163
+
164
+ /**
165
+ * Mark a binding as escaping (captured by closure)
166
+ * @param {string} name - Variable name
167
+ */
168
+ _markEscapes(name) {
169
+ // Find the binding in enclosing scopes
170
+ for (let i = this.currentScope.length - 1; i >= 0; i--) {
171
+ const key = this._bindingKey(name, this.currentScope[i]);
172
+ if (this.bindings.has(key)) {
173
+ const binding = this.bindings.get(key);
174
+ binding.escapes = true;
175
+ binding.canStackAlloc = false;
176
+ return;
177
+ }
178
+ }
179
+
180
+ // Check global scope
181
+ const globalKey = this._bindingKey(name, 'global');
182
+ if (this.bindings.has(globalKey)) {
183
+ const binding = this.bindings.get(globalKey);
184
+ binding.escapes = true;
185
+ binding.canStackAlloc = false;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Analyze an AST and return liveness information
191
+ * @param {Object} ast - The AST to analyze
192
+ * @returns {LivenessInfo} Liveness information
193
+ */
194
+ analyze(ast) {
195
+ // Reset state
196
+ this.bindings.clear();
197
+ this.currentScope = [];
198
+ this.scopeCounter = 0;
199
+ this.lastUseMap.clear();
200
+
201
+ // Phase 1: Collect all definitions and uses
202
+ this._collectDefinitionsAndUses(ast);
203
+
204
+ // Phase 2: Finalize last-use information
205
+ // The lastUseMap now contains all uses, but we need to keep only the actual last uses
206
+ const finalLastUseMap = new Map();
207
+ for (const binding of this.bindings.values()) {
208
+ if (binding.uses.length > 0) {
209
+ // The last use is the one with the highest line/column
210
+ const lastUse = binding.uses[binding.uses.length - 1];
211
+ binding.lastUseAt = lastUse;
212
+ }
213
+ }
214
+
215
+ // Update lastUseMap to only include actual last uses
216
+ for (const [nodeId, info] of this.lastUseMap) {
217
+ const binding = info.binding;
218
+ if (binding && binding.uses.length > 0) {
219
+ const lastUse = binding.lastUseAt;
220
+ const nodeUse = this._nodeIdToLocation(nodeId);
221
+ if (nodeUse.line === lastUse.line && nodeUse.column === lastUse.column) {
222
+ finalLastUseMap.set(nodeId, info);
223
+ }
224
+ }
225
+ }
226
+ this.lastUseMap = finalLastUseMap;
227
+
228
+ // Create the result object
229
+ const bindings = new Map(this.bindings);
230
+ const lastUseMap = new Map(this.lastUseMap);
231
+
232
+ return {
233
+ bindings,
234
+
235
+ /**
236
+ * Check if a node is the last use of a variable
237
+ * @param {string} name - Variable name
238
+ * @param {Object} node - AST node to check
239
+ * @returns {boolean}
240
+ */
241
+ isLastUse: (name, node) => {
242
+ const nodeId = this._nodeId(node);
243
+ const info = lastUseMap.get(nodeId);
244
+ if (!info) return false;
245
+ if (info.name !== name) return false;
246
+
247
+ // Don't clear if the variable escapes
248
+ const binding = info.binding;
249
+ if (binding && binding.escapes) return false;
250
+
251
+ return true;
252
+ },
253
+
254
+ /**
255
+ * Get the variable name if this node is its last use
256
+ * @param {Object} node - AST node to check
257
+ * @returns {string|null} Variable name or null
258
+ */
259
+ getLastUseName: (node) => {
260
+ const nodeId = this._nodeId(node);
261
+ const info = lastUseMap.get(nodeId);
262
+ if (!info) return null;
263
+
264
+ // Don't clear if the variable escapes
265
+ const binding = info.binding;
266
+ if (binding && binding.escapes) return null;
267
+
268
+ return info.name;
269
+ },
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Parse a node ID back to a location
275
+ * @param {string} nodeId
276
+ * @returns {{line: number, column: number}}
277
+ */
278
+ _nodeIdToLocation(nodeId) {
279
+ const parts = nodeId.split(':');
280
+ return {
281
+ line: parseInt(parts[0], 10) || 0,
282
+ column: parseInt(parts[1], 10) || 0,
283
+ };
284
+ }
285
+
286
+ /**
287
+ * Traverse the AST and collect all definitions and uses
288
+ * @param {Object} node - AST node
289
+ */
290
+ _collectDefinitionsAndUses(node) {
291
+ if (!node || typeof node !== 'object') return;
292
+
293
+ switch (node.type) {
294
+ case 'Program':
295
+ this._enterScope('global', 'main');
296
+ if (node.body) {
297
+ for (const stmt of node.body) {
298
+ this._collectDefinitionsAndUses(stmt);
299
+ }
300
+ }
301
+ this._exitScope();
302
+ break;
303
+
304
+ case 'BindingStatement':
305
+ case 'Declaration':
306
+ // Record the definition
307
+ if (node.name) {
308
+ const typeStr = node.typeAnnotation?.name || 'any';
309
+ this._recordDefinition(node.name, node, typeStr);
310
+ }
311
+ // Analyze the value expression
312
+ if (node.value) {
313
+ this._collectDefinitionsAndUses(node.value);
314
+ }
315
+ break;
316
+
317
+ case 'FunctionDeclaration':
318
+ case 'FunctionExpression':
319
+ case 'ArrowFunction':
320
+ // Record function name if it exists
321
+ if (node.name) {
322
+ this._recordDefinition(node.name, node, 'function');
323
+ }
324
+
325
+ // Enter function scope
326
+ this._enterScope('function', node.name || '');
327
+
328
+ // Record parameters as definitions
329
+ if (node.params) {
330
+ for (const param of node.params) {
331
+ const paramName = typeof param === 'string' ? param : param.name;
332
+ if (paramName) {
333
+ const typeStr = param.typeAnnotation?.name || 'any';
334
+ this._recordDefinition(paramName, param.node || node, typeStr);
335
+ }
336
+ }
337
+ }
338
+
339
+ // Check for captured variables (closures)
340
+ this._checkForClosures(node.body);
341
+
342
+ // Analyze function body
343
+ if (node.body) {
344
+ this._collectDefinitionsAndUses(node.body);
345
+ }
346
+
347
+ this._exitScope();
348
+ break;
349
+
350
+ case 'BlockExpression':
351
+ case 'Block':
352
+ this._enterScope('block');
353
+ if (node.body) {
354
+ for (const stmt of node.body) {
355
+ this._collectDefinitionsAndUses(stmt);
356
+ }
357
+ }
358
+ if (node.result) {
359
+ this._collectDefinitionsAndUses(node.result);
360
+ }
361
+ this._exitScope();
362
+ break;
363
+
364
+ case 'IfExpression':
365
+ case 'ConditionalExpression':
366
+ this._collectDefinitionsAndUses(node.condition || node.test);
367
+ this._collectDefinitionsAndUses(node.consequent || node.then);
368
+ this._collectDefinitionsAndUses(node.alternate || node.else);
369
+ break;
370
+
371
+ case 'ForExpression':
372
+ case 'ForLoop':
373
+ this._enterScope('loop');
374
+ // Loop variable
375
+ if (node.variable) {
376
+ this._recordDefinition(node.variable, node, 'num');
377
+ }
378
+ // Initial state
379
+ if (node.init) {
380
+ this._collectDefinitionsAndUses(node.init);
381
+ }
382
+ // Iterable
383
+ if (node.iterable) {
384
+ this._collectDefinitionsAndUses(node.iterable);
385
+ }
386
+ // Body
387
+ if (node.body) {
388
+ this._collectDefinitionsAndUses(node.body);
389
+ }
390
+ this._exitScope();
391
+ break;
392
+
393
+ case 'WhileLoop':
394
+ this._enterScope('loop');
395
+ if (node.condition) {
396
+ this._collectDefinitionsAndUses(node.condition);
397
+ }
398
+ if (node.body) {
399
+ this._collectDefinitionsAndUses(node.body);
400
+ }
401
+ this._exitScope();
402
+ break;
403
+
404
+ case 'Identifier':
405
+ // This is a variable use
406
+ if (node.name) {
407
+ this._recordUse(node.name, node);
408
+ }
409
+ break;
410
+
411
+ case 'CallExpression':
412
+ case 'Call':
413
+ // Analyze callee and arguments
414
+ this._collectDefinitionsAndUses(node.callee || node.func);
415
+ if (node.arguments || node.args) {
416
+ for (const arg of node.arguments || node.args) {
417
+ this._collectDefinitionsAndUses(arg);
418
+ }
419
+ }
420
+ break;
421
+
422
+ case 'BinaryExpression':
423
+ case 'BinaryOp':
424
+ this._collectDefinitionsAndUses(node.left);
425
+ this._collectDefinitionsAndUses(node.right);
426
+ break;
427
+
428
+ case 'UnaryExpression':
429
+ case 'UnaryOp':
430
+ this._collectDefinitionsAndUses(node.argument || node.operand);
431
+ break;
432
+
433
+ case 'MemberExpression':
434
+ case 'PropertyAccess':
435
+ this._collectDefinitionsAndUses(node.object);
436
+ // Don't record property name as a use
437
+ break;
438
+
439
+ case 'IndexExpression':
440
+ case 'IndexAccess':
441
+ this._collectDefinitionsAndUses(node.object || node.array);
442
+ this._collectDefinitionsAndUses(node.index);
443
+ break;
444
+
445
+ case 'ArrayLiteral':
446
+ case 'ArrayExpression':
447
+ if (node.elements) {
448
+ for (const elem of node.elements) {
449
+ this._collectDefinitionsAndUses(elem);
450
+ }
451
+ }
452
+ break;
453
+
454
+ case 'ObjectLiteral':
455
+ case 'ObjectExpression':
456
+ case 'RecordLiteral':
457
+ if (node.properties || node.fields) {
458
+ for (const prop of node.properties || node.fields) {
459
+ // Only analyze value, not key
460
+ this._collectDefinitionsAndUses(prop.value);
461
+ }
462
+ }
463
+ break;
464
+
465
+ case 'ReturnStatement':
466
+ if (node.value) {
467
+ this._collectDefinitionsAndUses(node.value);
468
+ }
469
+ break;
470
+
471
+ case 'ExpressionStatement':
472
+ this._collectDefinitionsAndUses(node.expression);
473
+ break;
474
+
475
+ default:
476
+ // Generic traversal for unknown node types
477
+ for (const key of Object.keys(node)) {
478
+ if (key === 'location' || key === 'loc' || key === 'type') continue;
479
+ const value = node[key];
480
+ if (Array.isArray(value)) {
481
+ for (const item of value) {
482
+ if (item && typeof item === 'object') {
483
+ this._collectDefinitionsAndUses(item);
484
+ }
485
+ }
486
+ } else if (value && typeof value === 'object') {
487
+ this._collectDefinitionsAndUses(value);
488
+ }
489
+ }
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Check for variables that are captured by closures
495
+ * @param {Object} node - AST node to check
496
+ */
497
+ _checkForClosures(node) {
498
+ if (!node || typeof node !== 'object') return;
499
+
500
+ // If we find a function inside, check for captured variables
501
+ if (node.type === 'FunctionExpression' ||
502
+ node.type === 'FunctionDeclaration' ||
503
+ node.type === 'ArrowFunction') {
504
+ // Collect all identifiers used in this function
505
+ const usedIds = this._collectIdentifiers(node.body);
506
+
507
+ // Mark any that are from outer scopes as escaping
508
+ for (const name of usedIds) {
509
+ // Check if it's defined in an outer scope (not in this function's params)
510
+ const isParam = node.params?.some(p =>
511
+ (typeof p === 'string' ? p : p.name) === name
512
+ );
513
+ if (!isParam) {
514
+ this._markEscapes(name);
515
+ }
516
+ }
517
+ }
518
+
519
+ // Continue checking child nodes
520
+ for (const key of Object.keys(node)) {
521
+ if (key === 'location' || key === 'loc' || key === 'type') continue;
522
+ const value = node[key];
523
+ if (Array.isArray(value)) {
524
+ for (const item of value) {
525
+ if (item && typeof item === 'object') {
526
+ this._checkForClosures(item);
527
+ }
528
+ }
529
+ } else if (value && typeof value === 'object') {
530
+ this._checkForClosures(value);
531
+ }
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Collect all identifier names used in a subtree
537
+ * @param {Object} node - AST node
538
+ * @returns {Set<string>} Set of identifier names
539
+ */
540
+ _collectIdentifiers(node) {
541
+ const identifiers = new Set();
542
+
543
+ const visit = (n) => {
544
+ if (!n || typeof n !== 'object') return;
545
+
546
+ if (n.type === 'Identifier' && n.name) {
547
+ identifiers.add(n.name);
548
+ }
549
+
550
+ for (const key of Object.keys(n)) {
551
+ if (key === 'location' || key === 'loc' || key === 'type') continue;
552
+ const value = n[key];
553
+ if (Array.isArray(value)) {
554
+ for (const item of value) {
555
+ visit(item);
556
+ }
557
+ } else if (value && typeof value === 'object') {
558
+ visit(value);
559
+ }
560
+ }
561
+ };
562
+
563
+ visit(node);
564
+ return identifiers;
565
+ }
566
+ }
567
+
568
+ export default LivenessAnalyzer;