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,452 @@
1
+ /**
2
+ * Stone Type Checker
3
+ *
4
+ * Performs compile-time type checking on Stone AST.
5
+ *
6
+ * Design principles:
7
+ * - Types are ALWAYS optional (no errors for missing annotations)
8
+ * - Compile-time errors when annotated types don't match inferred types
9
+ * - Infers types from usage context
10
+ * - Stores inferred types on AST nodes for IDE tooling
11
+ */
12
+
13
+ import {
14
+ FunctionType,
15
+ RecordType,
16
+ ArrayType,
17
+ TypeVariable,
18
+ INT,
19
+ FLOAT,
20
+ BOOL,
21
+ STRING,
22
+ UNIT,
23
+ NUM,
24
+ freshTypeVar,
25
+ flattenNestedArrayType,
26
+ } from "../types/types.js";
27
+ import { OVERLOADS } from "../../backends/js-vm/primitives/overloads.js";
28
+
29
+ // Import modular components
30
+ import { TypeEnvironment } from "./TypeEnvironment.js";
31
+ import { OverloadedFunctionType } from "./OverloadedFunctionType.js";
32
+ import { typeCompatibilityMethods } from "./typeCompatibility.js";
33
+ import { bidirectionalInferenceMethods } from "./bidirectionalInference.js";
34
+ import { overloadResolutionMethods } from "./overloadResolution.js";
35
+ import { moduleAnalysisMethods } from "./moduleAnalysis.js";
36
+ import { registerVisitors } from "./visitors/index.js";
37
+
38
+ // Re-export for external consumers
39
+ export { OverloadedFunctionType };
40
+
41
+ /**
42
+ * Stone Type Checker
43
+ *
44
+ * Performs compile-time type checking on Stone AST using Hindley-Milner style
45
+ * type inference with structural typing.
46
+ *
47
+ * Design principles:
48
+ * - Types are always optional (no errors for missing annotations)
49
+ * - Compile-time errors when annotated types don't match inferred types
50
+ * - Infers types from usage context
51
+ * - Stores inferred types on AST nodes (node.inferredType) for tooling and optimization
52
+ *
53
+ * Type system features:
54
+ * - Primitive types: num, bool, string, unit, complex
55
+ * - Compound types: arrays (with rank), records, functions
56
+ * - Type variables for polymorphism
57
+ * - Overloaded functions with signature matching
58
+ * - Module import type analysis
59
+ *
60
+ * @example
61
+ * const checker = new TypeChecker({ moduleResolver, parser });
62
+ * const result = checker.check(ast);
63
+ * if (!result.success) {
64
+ * console.error(result.errors);
65
+ * }
66
+ */
67
+ export class TypeChecker {
68
+ /**
69
+ * Create a new TypeChecker instance
70
+ *
71
+ * @param {Object} [options={}] - Configuration options
72
+ * @param {ModuleResolver} [options.moduleResolver] - Module resolver for import type analysis
73
+ * @param {Function} [options.parser] - Parser function for analyzing module source
74
+ */
75
+ constructor(options = {}) {
76
+ /**
77
+ * Collected type errors
78
+ * @type {Array<{message: string, location: Object|null}>}
79
+ */
80
+ this.errors = [];
81
+
82
+ /**
83
+ * Collected warnings
84
+ * @type {Array<{message: string, location: Object|null}>}
85
+ */
86
+ this.warnings = [];
87
+
88
+ /**
89
+ * Map from AST node to inferred type
90
+ * @type {Map<Object, Object>}
91
+ */
92
+ this.typeMap = new Map();
93
+
94
+ /**
95
+ * Configuration options
96
+ * @type {Object}
97
+ */
98
+ this.options = options;
99
+
100
+ /**
101
+ * Overload registry for static resolution.
102
+ * Creates a shallow copy to avoid modifying the global OVERLOADS object
103
+ * when adding type-checking-only overloads.
104
+ * @type {Object}
105
+ */
106
+ this.overloads = {};
107
+ for (const name of Object.keys(OVERLOADS)) {
108
+ this.overloads[name] = [...OVERLOADS[name]];
109
+ }
110
+
111
+ /**
112
+ * Module resolver for import type analysis (optional)
113
+ * @type {ModuleResolver|null}
114
+ */
115
+ this.moduleResolver = options.moduleResolver || null;
116
+
117
+ /**
118
+ * Parser function for analyzing module source (optional)
119
+ * @type {Function|null}
120
+ */
121
+ this.parser = options.parser || null;
122
+
123
+ /**
124
+ * Cache of module export types (modulePath -> Map<exportName, Type>)
125
+ * @type {Map<string, Map<string, Object>>}
126
+ */
127
+ this.moduleTypeCache = new Map();
128
+
129
+ /**
130
+ * Map of TypeVariable id to TypeVariable object for bidirectional inference
131
+ * @type {Map<number, TypeVariable>}
132
+ */
133
+ this.typeVarMap = new Map();
134
+
135
+ /**
136
+ * Stack of deferred constraint collections for nested function definitions.
137
+ * Each entry is an array of constraints for the currently-being-analyzed function.
138
+ * @type {Array<Array>}
139
+ */
140
+ this.constraintStack = [];
141
+ }
142
+
143
+ /**
144
+ * Record a deferred constraint for the currently-being-analyzed function.
145
+ * Called when scoreOverload encounters a variable rank that must match a concrete rank.
146
+ *
147
+ * @param {Object} constraint - The deferred constraint
148
+ */
149
+ recordDeferredConstraint(constraint) {
150
+ if (this.constraintStack.length > 0) {
151
+ this.constraintStack[this.constraintStack.length - 1].push(constraint);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Start tracking constraints for a new function definition.
157
+ */
158
+ pushConstraintContext() {
159
+ this.constraintStack.push([]);
160
+ }
161
+
162
+ /**
163
+ * Finish tracking constraints and return collected constraints.
164
+ * @returns {Array} The deferred constraints collected for this function
165
+ */
166
+ popConstraintContext() {
167
+ return this.constraintStack.pop() || [];
168
+ }
169
+
170
+ /**
171
+ * Register complex module arithmetic overloads for type checking.
172
+ * Called when any import from 'complex' module is seen.
173
+ * This allows operators like + - * / to work with complex numbers.
174
+ */
175
+ _registerComplexOverloads() {
176
+ // Don't register twice
177
+ if (this._complexOverloadsRegistered) return;
178
+ this._complexOverloadsRegistered = true;
179
+
180
+ // Create mock "primitive" functions with complex signatures for type checking
181
+ // These signatures match what the Stone complex module provides
182
+ // Register under both function names (for import) and operator names (for +, -, etc.)
183
+ const complexOps = [
184
+ { name: 'add', opName: '__op_add', sig: { in: ['complex', 'complex'], out: 'complex' } },
185
+ { name: 'sub', opName: '__op_sub', sig: { in: ['complex', 'complex'], out: 'complex' } },
186
+ { name: 'mul', opName: '__op_mul', sig: { in: ['complex', 'complex'], out: 'complex' } },
187
+ { name: 'div', opName: '__op_div', sig: { in: ['complex', 'complex'], out: 'complex' } },
188
+ { name: 'neg', opName: '__op_neg', sig: { in: ['complex'], out: 'complex' } },
189
+ { name: 'pow', opName: '__op_pow', sig: { in: ['complex', 'complex'], out: 'complex' } },
190
+ ];
191
+
192
+ for (const { name, opName, sig } of complexOps) {
193
+ // Create a mock function with the .stone signature
194
+ const mockFn = { stone: sig };
195
+
196
+ // Add to overloads under function name (for import from complex module)
197
+ if (!this.overloads[name]) {
198
+ this.overloads[name] = [];
199
+ }
200
+ this.overloads[name].push(mockFn);
201
+
202
+ // Also add under operator name (for +, -, *, /, ^ operators)
203
+ if (!this.overloads[opName]) {
204
+ this.overloads[opName] = [];
205
+ }
206
+ this.overloads[opName].push({ stone: sig });
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Check deferred constraints from a function definition against actual argument types.
212
+ * Called at function call sites to verify that inferred rank constraints are satisfied.
213
+ *
214
+ * @param {string} funcName - The function name (for error messages)
215
+ * @param {FunctionType} funcType - The function type with constraints
216
+ * @param {Array} argTypes - The actual argument types at the call site
217
+ * @param {Object} node - The call site node (for error location)
218
+ */
219
+ checkDeferredConstraints(funcName, funcType, argTypes, node) {
220
+ if (!funcType.constraints || funcType.constraints.length === 0) {
221
+ return;
222
+ }
223
+
224
+ for (const constraint of funcType.constraints) {
225
+ if (constraint.type === 'rank') {
226
+ const paramIndex = constraint.paramIndex;
227
+ if (paramIndex >= argTypes.length) {
228
+ continue; // Arity mismatch handled elsewhere
229
+ }
230
+
231
+ let argType = argTypes[paramIndex];
232
+ let actualRank = null;
233
+
234
+ // Flatten nested array types before extracting rank
235
+ // e.g., array<array<num, 1>, 1> should have effective rank 2
236
+ if (argType instanceof ArrayType || (argType && argType.kind === 'array')) {
237
+ argType = flattenNestedArrayType(argType);
238
+ }
239
+
240
+ // Extract the rank from the argument type
241
+ if (argType instanceof ArrayType) {
242
+ actualRank = argType.rank;
243
+ } else if (argType && argType.kind === 'array') {
244
+ actualRank = argType.rank;
245
+ }
246
+
247
+ if (actualRank === null) {
248
+ // Argument isn't an array - type mismatch error handled elsewhere
249
+ continue;
250
+ }
251
+
252
+ // Check if rank is concrete and matches
253
+ if (typeof actualRank === 'number') {
254
+ if (actualRank !== constraint.requiredRank) {
255
+ this.addError(
256
+ `Argument ${paramIndex + 1} to '${funcName}' requires array of rank ${constraint.requiredRank}, but got rank ${actualRank}`,
257
+ node.location
258
+ );
259
+ }
260
+ } else if (typeof actualRank === 'object' && actualRank.var) {
261
+ // Argument also has variable rank - propagate the constraint
262
+ // This means we're calling a constrained function from another function with variable rank
263
+ this.recordDeferredConstraint({
264
+ type: 'rank',
265
+ paramIndex: paramIndex,
266
+ rankVar: actualRank.var,
267
+ requiredRank: constraint.requiredRank
268
+ });
269
+ }
270
+ // If rank is neither number nor variable object, it's a type we don't handle
271
+ }
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Type check an AST and return type information
277
+ *
278
+ * This is the main entry point for type checking. It:
279
+ * 1. Creates a fresh type environment
280
+ * 2. Registers built-in types and functions
281
+ * 3. Visits all AST nodes, inferring and checking types
282
+ * 4. Returns collected errors, warnings, and type map
283
+ *
284
+ * @param {Object} ast - The parsed AST (Program node)
285
+ * @returns {Object} Type checking result
286
+ * @returns {Array} returns.errors - Array of type errors
287
+ * @returns {Array} returns.warnings - Array of type warnings
288
+ * @returns {Map} returns.types - Map from AST nodes to their inferred types
289
+ * @returns {boolean} returns.success - True if no errors were found
290
+ *
291
+ * @example
292
+ * const result = checker.check(ast);
293
+ * if (result.success) {
294
+ * // Access inferred types via result.types or node.inferredType
295
+ * const funcType = result.types.get(functionNode);
296
+ * }
297
+ */
298
+ check(ast) {
299
+ this.errors = [];
300
+ this.warnings = [];
301
+ this.typeMap = new Map();
302
+
303
+ const env = new TypeEnvironment();
304
+ this.registerBuiltins(env);
305
+
306
+ try {
307
+ this.visitProgram(ast, env);
308
+ } catch (error) {
309
+ this.errors.push({
310
+ message: error.message,
311
+ location: null,
312
+ });
313
+ }
314
+
315
+ // Collect extension names from the type environment
316
+ // This includes both locally defined extensions and imported ones
317
+ const extensions = new Set();
318
+ this._collectExtensions(env, extensions);
319
+
320
+ return {
321
+ errors: this.errors,
322
+ warnings: this.warnings,
323
+ types: this.typeMap,
324
+ success: this.errors.length === 0,
325
+ extensions, // Set of extension function names for the compiler
326
+ };
327
+ }
328
+
329
+ /**
330
+ * Recursively collect extension names from environment chain
331
+ * @private
332
+ */
333
+ _collectExtensions(env, extensions) {
334
+ if (!env) return;
335
+ for (const name of env.extensions.keys()) {
336
+ extensions.add(name);
337
+ }
338
+ // Note: extensions are typically only defined in the current env,
339
+ // not inherited, but we recurse for completeness
340
+ if (env.parent) {
341
+ this._collectExtensions(env.parent, extensions);
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Register built-in functions and types.
347
+ * Note: Functions that have proper overload definitions in OVERLOADS are NOT registered here,
348
+ * as they will be handled by overload resolution which provides better type checking.
349
+ */
350
+ registerBuiltins(env) {
351
+ // Only register functions that are NOT in OVERLOADS or have no overloaded variants.
352
+ // Functions like abs, sqrt, pow, sin, cos, min, max etc. are in OVERLOADS and
353
+ // will be resolved through the overload system for proper type handling.
354
+
355
+ // Array functions (len has overloads, sum is simple)
356
+ env.define("len", new FunctionType([freshTypeVar()], INT));
357
+ env.define("sum", new FunctionType([new ArrayType(NUM, 1)], NUM));
358
+
359
+ // Type conversion (these are not overloaded)
360
+ env.define("int", new FunctionType([freshTypeVar()], INT));
361
+ env.define("float", new FunctionType([freshTypeVar()], FLOAT));
362
+ env.define("str", new FunctionType([freshTypeVar()], STRING));
363
+ env.define("bool", new FunctionType([freshTypeVar()], BOOL));
364
+
365
+ // I/O
366
+ env.define("print", new FunctionType([freshTypeVar()], UNIT));
367
+
368
+ // Terminal constructors
369
+ // Note: graph2d/graph3d are now in the 'plots' module (import graph2d from plots)
370
+ const terminalType = new RecordType(new Map(), true);
371
+ env.define("console_terminal", new FunctionType([terminalType], terminalType));
372
+ }
373
+
374
+ /**
375
+ * Check if a name is defined in the scope chain (not just via overload resolution)
376
+ */
377
+ isDefinedInScope(env, name) {
378
+ let current = env;
379
+ while (current) {
380
+ if (current.bindings.has(name)) {
381
+ return true;
382
+ }
383
+ current = current.parent;
384
+ }
385
+ return false;
386
+ }
387
+
388
+ /**
389
+ * Add an error with stage information
390
+ */
391
+ addError(message, location) {
392
+ this.errors.push({
393
+ stage: 'type_check',
394
+ type: 'type',
395
+ message,
396
+ location
397
+ });
398
+ }
399
+
400
+ /**
401
+ * Add a warning with stage information
402
+ */
403
+ addWarning(message, location) {
404
+ this.warnings.push({
405
+ stage: 'type_check',
406
+ type: 'warning',
407
+ message,
408
+ location
409
+ });
410
+ }
411
+
412
+ /**
413
+ * Set the inferred type for an AST node
414
+ */
415
+ setType(node, type) {
416
+ this.typeMap.set(node, type);
417
+ node.inferredType = type;
418
+ return type;
419
+ }
420
+
421
+ /**
422
+ * Get the inferred type for an AST node
423
+ */
424
+ getType(node) {
425
+ return this.typeMap.get(node);
426
+ }
427
+ }
428
+
429
+ // Mix in all method groups from extracted modules
430
+ Object.assign(TypeChecker.prototype,
431
+ typeCompatibilityMethods,
432
+ bidirectionalInferenceMethods,
433
+ overloadResolutionMethods,
434
+ moduleAnalysisMethods
435
+ );
436
+
437
+ // Register all visitor methods
438
+ registerVisitors(TypeChecker);
439
+
440
+ // ============================================================================
441
+ // EXPORTS
442
+ // ============================================================================
443
+
444
+ /**
445
+ * Type check an AST
446
+ */
447
+ export function typeCheck(ast, options = {}) {
448
+ const checker = new TypeChecker(options);
449
+ return checker.check(ast);
450
+ }
451
+
452
+ export { TypeEnvironment };