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,43 @@
1
+ /**
2
+ * OverloadedFunctionType - represents overloaded functions with multiple signatures
3
+ *
4
+ * Used for primitives that can be called with different argument types,
5
+ * and also for user-defined function overloads.
6
+ */
7
+
8
+ /**
9
+ * Represents an overloaded function with multiple signatures.
10
+ * Used for primitives that can be called with different argument types,
11
+ * and now also for user-defined function overloads.
12
+ */
13
+ export class OverloadedFunctionType {
14
+ constructor(name, overloads = [], userOverloads = []) {
15
+ this.name = name;
16
+ this.overloads = overloads; // Array of primitive functions with .stone signatures
17
+ this.userOverloads = userOverloads; // Array of { funcType, node, overloadId } for user-defined overloads
18
+ }
19
+
20
+ /**
21
+ * Add a user-defined function overload
22
+ * @param {FunctionType} funcType - The function's type signature
23
+ * @param {Object} node - The AST node of the function definition
24
+ */
25
+ addUserOverload(funcType, node) {
26
+ // Use already-set overloadId from node, or from funcType's stored value
27
+ const overloadId = node.overloadId || funcType._overloadId || `${this.name}_${this.userOverloads.length}`;
28
+ if (!node.overloadId) {
29
+ node.overloadId = overloadId; // Store on AST for executor registration
30
+ }
31
+ this.userOverloads.push({ funcType, node, overloadId });
32
+ }
33
+
34
+ toString() {
35
+ const primSigs = this.overloads
36
+ .filter(fn => fn.stone)
37
+ .map(fn => `(${fn.stone.in.join(", ")}) -> ${fn.stone.out}`);
38
+ const userSigs = this.userOverloads
39
+ .map(({ funcType }) => funcType.toString());
40
+ const allSigs = [...primSigs, ...userSigs].join(" | ");
41
+ return `overloaded[${this.name}]: ${allSigs}`;
42
+ }
43
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Type Environment - tracks type bindings in scope
3
+ *
4
+ * Manages variable bindings, type aliases, declared types, extensions, and immutability
5
+ * with parent-child hierarchy for nested scopes.
6
+ */
7
+
8
+ /**
9
+ * Extension info - tracks metadata for imported extension methods
10
+ */
11
+ export class ExtensionInfo {
12
+ constructor(name, selfType, functionType, functionRef = null) {
13
+ this.name = name; // Method name (e.g., "T", "shift")
14
+ this.selfType = selfType; // TypeAnnotation for self parameter
15
+ this.functionType = functionType; // Full function type
16
+ this.functionRef = functionRef; // Reference to compiled function (set later)
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Type environment - tracks type bindings in scope
22
+ */
23
+ export class TypeEnvironment {
24
+ constructor(parent = null) {
25
+ this.bindings = new Map();
26
+ this.typeAliases = new Map();
27
+ this.declaredTypes = new Map(); // Track type annotations for deferred resolution
28
+ this.immutableBindings = new Set(); // Track immutable bindings (cannot be rebound)
29
+ this.extensions = new Map(); // Track imported extension methods: name -> ExtensionInfo
30
+ this.parent = parent;
31
+ }
32
+
33
+ /**
34
+ * Define a new binding
35
+ */
36
+ define(name, type) {
37
+ this.bindings.set(name, type);
38
+ }
39
+
40
+ /**
41
+ * Look up a binding
42
+ */
43
+ lookup(name) {
44
+ if (this.bindings.has(name)) {
45
+ return this.bindings.get(name);
46
+ }
47
+ if (this.parent) {
48
+ return this.parent.lookup(name);
49
+ }
50
+ return null;
51
+ }
52
+
53
+ /**
54
+ * Define a type alias
55
+ */
56
+ defineTypeAlias(name, type) {
57
+ this.typeAliases.set(name, type);
58
+ }
59
+
60
+ /**
61
+ * Look up a type alias
62
+ */
63
+ lookupTypeAlias(name) {
64
+ if (this.typeAliases.has(name)) {
65
+ return this.typeAliases.get(name);
66
+ }
67
+ if (this.parent) {
68
+ return this.parent.lookupTypeAlias(name);
69
+ }
70
+ return null;
71
+ }
72
+
73
+ /**
74
+ * Define a declared type (from type annotation)
75
+ * Used for deferred type resolution of empty arrays
76
+ */
77
+ defineDeclared(name, type) {
78
+ this.declaredTypes.set(name, type);
79
+ }
80
+
81
+ /**
82
+ * Look up a declared type
83
+ * For primed variables (e.g., arr'), also checks the base name (arr)
84
+ */
85
+ lookupDeclared(name) {
86
+ if (this.declaredTypes.has(name)) {
87
+ return this.declaredTypes.get(name);
88
+ }
89
+ // For primed variables, check base name (arr' → arr)
90
+ if (name.endsWith("'")) {
91
+ const baseName = name.slice(0, -1);
92
+ if (this.declaredTypes.has(baseName)) {
93
+ return this.declaredTypes.get(baseName);
94
+ }
95
+ }
96
+ if (this.parent) {
97
+ return this.parent.lookupDeclared(name);
98
+ }
99
+ return null;
100
+ }
101
+
102
+ /**
103
+ * Check if a binding exists
104
+ */
105
+ has(name) {
106
+ return this.bindings.has(name) || (this.parent && this.parent.has(name));
107
+ }
108
+
109
+ /**
110
+ * Check if a binding exists in the current scope only (not parent)
111
+ */
112
+ hasInCurrentScope(name) {
113
+ return this.bindings.has(name);
114
+ }
115
+
116
+ /**
117
+ * Mark a binding as immutable (cannot be rebound)
118
+ */
119
+ markImmutable(name) {
120
+ this.immutableBindings.add(name);
121
+ }
122
+
123
+ /**
124
+ * Check if a binding is immutable in the scope chain
125
+ */
126
+ isImmutable(name) {
127
+ if (this.immutableBindings.has(name)) return true;
128
+ if (this.parent) return this.parent.isImmutable(name);
129
+ return false;
130
+ }
131
+
132
+ /**
133
+ * Create a child environment
134
+ */
135
+ createChild() {
136
+ return new TypeEnvironment(this);
137
+ }
138
+
139
+ /**
140
+ * Define an extension method
141
+ */
142
+ defineExtension(name, extensionInfo) {
143
+ this.extensions.set(name, extensionInfo);
144
+ }
145
+
146
+ /**
147
+ * Look up an extension method by name
148
+ */
149
+ lookupExtension(name) {
150
+ if (this.extensions.has(name)) {
151
+ return this.extensions.get(name);
152
+ }
153
+ if (this.parent) {
154
+ return this.parent.lookupExtension(name);
155
+ }
156
+ return null;
157
+ }
158
+
159
+ /**
160
+ * Check if an extension exists in the current scope only (not parent)
161
+ */
162
+ hasExtensionInCurrentScope(name) {
163
+ return this.extensions.has(name);
164
+ }
165
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Bidirectional Inference Methods - mixin for TypeChecker
3
+ *
4
+ * Methods for collecting return statements, unifying return types,
5
+ * and back-propagating types to incomplete expressions.
6
+ */
7
+
8
+ import {
9
+ TypeVariable,
10
+ UNIT,
11
+ unify,
12
+ formatType,
13
+ } from "../types/types.js";
14
+
15
+ /**
16
+ * Bidirectional inference methods to be mixed into TypeChecker.prototype
17
+ */
18
+ export const bidirectionalInferenceMethods = {
19
+ /**
20
+ * Recursively collect all return statements from a function body
21
+ * Uses types already computed during the first pass (stored on node.inferredType)
22
+ * Returns array of { node, type, isIncomplete }
23
+ */
24
+ collectReturnStatements(body) {
25
+ const returns = [];
26
+
27
+ const collect = (statements) => {
28
+ for (const stmt of statements) {
29
+ if (stmt.type === "ReturnStatement") {
30
+ // Use the type already computed during the first pass
31
+ // Do NOT re-visit, as that may give incorrect results
32
+ const valueType = stmt.inferredType || UNIT;
33
+ const isIncomplete = this.isIncompleteType(valueType);
34
+ returns.push({ node: stmt, type: valueType, isIncomplete });
35
+ } else if (stmt.type === "BranchExpression") {
36
+ // Recurse into branches
37
+ for (const path of stmt.paths) {
38
+ collect(path.body);
39
+ }
40
+ } else if (stmt.type === "LoopExpression") {
41
+ collect(stmt.body);
42
+ } else if (stmt.type === "BlockExpression") {
43
+ collect(stmt.statements || []);
44
+ } else if (stmt.type === "BindingStructure") {
45
+ // Handle block expressions like { if (...) { return x } else { return y } }
46
+ for (const binding of stmt.bindings || []) {
47
+ if (binding.value) {
48
+ collect([binding.value]);
49
+ }
50
+ }
51
+ if (stmt.trailingExpr) {
52
+ collect([stmt.trailingExpr]);
53
+ }
54
+ }
55
+ // Continue visiting other statements (they might contain nested returns)
56
+ }
57
+ };
58
+
59
+ collect(body);
60
+ return returns;
61
+ },
62
+
63
+ /**
64
+ * Unify all return types, finding common type or reporting errors
65
+ * Returns { unifiedType, errors }
66
+ */
67
+ unifyReturnTypes(returnInfos) {
68
+ if (returnInfos.length === 0) {
69
+ return { unifiedType: UNIT, errors: [] };
70
+ }
71
+
72
+ const errors = [];
73
+
74
+ // Separate complete and incomplete types
75
+ // Exclude guard passthrough returns - these return the input unchanged
76
+ // when it's already the correct type, so they shouldn't constrain the return type
77
+ const complete = returnInfos.filter(r => !r.isIncomplete && !r.isGuardPassthrough);
78
+ const incomplete = returnInfos.filter(r => r.isIncomplete && !r.isGuardPassthrough);
79
+
80
+ // If no returns at all
81
+ if (complete.length === 0 && incomplete.length === 0) {
82
+ return { unifiedType: UNIT, errors };
83
+ }
84
+
85
+ // If all returns are incomplete, try to unify them
86
+ // This allows polymorphic functions like reverse(arr) that return types dependent on parameters
87
+ if (complete.length === 0) {
88
+ // Try to unify all incomplete types - if they conflict, error
89
+ let unifiedIncomplete = incomplete[0].type;
90
+ for (let i = 1; i < incomplete.length; i++) {
91
+ const result = unify(unifiedIncomplete, incomplete[i].type);
92
+ if (!result.success) {
93
+ errors.push({
94
+ message: `Return type mismatch: ${formatType(this.toStructuredType(unifiedIncomplete))} vs ${formatType(this.toStructuredType(incomplete[i].type))}`,
95
+ location: incomplete[i].node.location
96
+ });
97
+ } else if (result.unifiedType) {
98
+ // Use the unified type from the result if provided (e.g., LiteralUnionType)
99
+ unifiedIncomplete = result.unifiedType;
100
+ } else if (unifiedIncomplete instanceof TypeVariable) {
101
+ unifiedIncomplete = unifiedIncomplete.resolve();
102
+ }
103
+ }
104
+ return { unifiedType: unifiedIncomplete, errors };
105
+ }
106
+
107
+ // Unify all complete types
108
+ let unifiedType = complete[0].type;
109
+ for (let i = 1; i < complete.length; i++) {
110
+ const result = unify(unifiedType, complete[i].type);
111
+ if (!result.success) {
112
+ errors.push({
113
+ message: `Return type mismatch: ${formatType(unifiedType)} vs ${formatType(complete[i].type)}`,
114
+ location: complete[i].node.location
115
+ });
116
+ } else {
117
+ // Use the unified type from the result if provided (e.g., LiteralUnionType from merging literals)
118
+ if (result.unifiedType) {
119
+ unifiedType = result.unifiedType;
120
+ } else if (unifiedType instanceof TypeVariable) {
121
+ // Resolve if it's a TypeVariable
122
+ unifiedType = unifiedType.resolve();
123
+ }
124
+ }
125
+ }
126
+
127
+ return { unifiedType, errors };
128
+ },
129
+
130
+ /**
131
+ * Back-propagate unified type to incomplete return expressions
132
+ */
133
+ resolveIncompleteTypes(returnInfos, unifiedType) {
134
+ for (const { node, type, isIncomplete, isGuardPassthrough } of returnInfos) {
135
+ // Skip guard passthrough returns - they return input unchanged
136
+ // and shouldn't be constrained to the converted output type
137
+ if (isIncomplete && !isGuardPassthrough) {
138
+ // Unify the incomplete type with the resolved type
139
+ unify(type, unifiedType);
140
+
141
+ // Update the AST node's inferred type
142
+ node.inferredType = unifiedType;
143
+ if (node.value) {
144
+ node.value.inferredType = unifiedType;
145
+ }
146
+ }
147
+ }
148
+ },
149
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Frontend Type Checker - Barrel exports
3
+ */
4
+ export { TypeChecker, OverloadedFunctionType } from './typeChecker.js';
5
+ export { TypeEnvironment, ExtensionInfo } from './TypeEnvironment.js';
6
+ export { typeCompatibilityMethods } from './typeCompatibility.js';
7
+ export { bidirectionalInferenceMethods } from './bidirectionalInference.js';
8
+ export { overloadResolutionMethods } from './overloadResolution.js';
9
+ export { moduleAnalysisMethods } from './moduleAnalysis.js';
10
+ export { BINARY_OP_NAMES, UNARY_OP_NAMES } from './operatorMappings.js';
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Module Analysis Methods - mixin for TypeChecker
3
+ *
4
+ * Methods for analyzing module imports and exports to determine types
5
+ * for static type checking.
6
+ */
7
+
8
+ import {
9
+ FunctionType,
10
+ RecordType,
11
+ freshTypeVar,
12
+ } from "../types/types.js";
13
+ import { TypeEnvironment } from "./TypeEnvironment.js";
14
+ import { OverloadedFunctionType } from "./OverloadedFunctionType.js";
15
+
16
+ /**
17
+ * Module analysis methods to be mixed into TypeChecker.prototype
18
+ */
19
+ export const moduleAnalysisMethods = {
20
+ /**
21
+ * Get export types for a module.
22
+ * Returns a Map<exportName, Type> for all exports in the module.
23
+ *
24
+ * @param {string} modulePath - The module path (e.g., "primitives", "math", "array")
25
+ * @returns {Map<string, Object>|null} Map of export names to their types
26
+ */
27
+ getModuleExportTypes(modulePath) {
28
+ // Check cache first
29
+ if (this.moduleTypeCache.has(modulePath)) {
30
+ return this.moduleTypeCache.get(modulePath);
31
+ }
32
+
33
+ // Handle 'primitives' module specially - these are JS-wrapped overloaded functions
34
+ if (modulePath === "primitives") {
35
+ const exports = this.analyzePrimitivesModule();
36
+ this.moduleTypeCache.set(modulePath, exports);
37
+ return exports;
38
+ }
39
+
40
+ // Handle 'plots' module - runtime terminal constructors
41
+ if (modulePath === "plots") {
42
+ const exports = this.analyzePlotsModule();
43
+ this.moduleTypeCache.set(modulePath, exports);
44
+ return exports;
45
+ }
46
+
47
+ // For .stn modules, we need the moduleResolver and parser
48
+ if (!this.moduleResolver || !this.parser) {
49
+ return null; // Can't analyze without these - fall back to type variables
50
+ }
51
+
52
+ // Check if it's a .stn builtin module
53
+ if (this.moduleResolver.isStnBuiltinModule(modulePath)) {
54
+ const exports = this.analyzeStnModule(modulePath);
55
+ if (exports) {
56
+ this.moduleTypeCache.set(modulePath, exports);
57
+ }
58
+ return exports;
59
+ }
60
+
61
+ // For file-based modules, we'd need async loading which isn't supported
62
+ // in the synchronous type checker. Return null to fall back to type variables.
63
+ return null;
64
+ },
65
+
66
+ /**
67
+ * Analyze the primitives module and extract types for all exports.
68
+ * Primitives are overloaded functions from the OVERLOADS registry.
69
+ */
70
+ analyzePrimitivesModule() {
71
+ const exports = new Map();
72
+
73
+ // Each overload set in OVERLOADS is an exportable primitive function
74
+ for (const [name, overloadSet] of Object.entries(this.overloads)) {
75
+ if (overloadSet && overloadSet.length > 0) {
76
+ // Create an OverloadedFunctionType for functions with multiple signatures
77
+ exports.set(name, new OverloadedFunctionType(name, overloadSet));
78
+ }
79
+ }
80
+
81
+ return exports;
82
+ },
83
+
84
+ /**
85
+ * Analyze the plots module and extract types for terminal constructors.
86
+ * Exports graph2d and graph3d functions.
87
+ *
88
+ * Terminal constructors take a config record and return a terminal handle.
89
+ * The terminal handle has dynamic methods (add, remove, etc.) that can't be
90
+ * statically typed, so we use type variables to allow any operations.
91
+ */
92
+ analyzePlotsModule() {
93
+ const exports = new Map();
94
+
95
+ // Use type variables for config and terminal handle since they're dynamically typed
96
+ // Config can be any record, terminal handle has runtime-defined methods
97
+ const configType = freshTypeVar();
98
+ const terminalHandleType = freshTypeVar();
99
+
100
+ // graph2d(config) -> TerminalHandle
101
+ exports.set("graph2d", new FunctionType([configType], terminalHandleType));
102
+ // graph3d(config) -> TerminalHandle
103
+ exports.set("graph3d", new FunctionType([freshTypeVar()], freshTypeVar()));
104
+
105
+ return exports;
106
+ },
107
+
108
+ /**
109
+ * Analyze a .stn module and extract export types.
110
+ * Recursively type-checks the module to determine export types.
111
+ *
112
+ * @param {string} modulePath - The module path
113
+ * @returns {Map<string, Object>|null} Map of export names to types
114
+ */
115
+ analyzeStnModule(modulePath) {
116
+ if (!this.moduleResolver || !this.parser) {
117
+ return null;
118
+ }
119
+
120
+ try {
121
+ const stnContent = this.moduleResolver.getStnBuiltinContent(modulePath);
122
+ if (!stnContent) {
123
+ return null;
124
+ }
125
+
126
+ // Parse the module
127
+ const ast = this.parser(stnContent, { filename: `<builtin:${modulePath}>` });
128
+ if (!ast || !ast.exports) {
129
+ return null;
130
+ }
131
+
132
+ // Create a fresh environment for the module
133
+ const moduleEnv = new TypeEnvironment();
134
+ this.registerBuiltins(moduleEnv);
135
+
136
+ // First pass: process imports to get types from other modules
137
+ for (const stmt of ast.statements) {
138
+ if (stmt.type === "ImportStatement") {
139
+ this.analyzeModuleImport(stmt, moduleEnv);
140
+ } else if (stmt.type === "NamespaceImportStatement") {
141
+ this.visit(stmt, moduleEnv);
142
+ }
143
+ }
144
+
145
+ // Second pass: process all statements to build environment
146
+ // This includes ExportStatements which are in both ast.statements and ast.exports
147
+ for (const stmt of ast.statements) {
148
+ if (stmt.type !== "ImportStatement" && stmt.type !== "NamespaceImportStatement") {
149
+ this.visit(stmt, moduleEnv);
150
+ }
151
+ }
152
+
153
+ // Extract export types and extension info
154
+ const exports = new Map();
155
+ const extensionExports = new Set(); // Track which exports are extensions
156
+
157
+ for (const exportStmt of ast.exports) {
158
+ if (exportStmt.type === "ExportStatement") {
159
+ // Direct export: export fn foo() or export x = 5
160
+ const decl = exportStmt.declaration;
161
+ if (decl.type === "FunctionDefinition") {
162
+ // For overloaded functions, the env already has the OverloadedFunctionType
163
+ // We only need to add once per name
164
+ if (!exports.has(decl.name)) {
165
+ const funcType = moduleEnv.lookup(decl.name);
166
+ if (funcType) {
167
+ exports.set(decl.name, funcType);
168
+ }
169
+ // Track if this is an extension function
170
+ if (decl.isExtension) {
171
+ extensionExports.add(decl.name);
172
+ }
173
+ }
174
+ } else if (decl.type === "Assignment") {
175
+ const varType = moduleEnv.lookup(decl.target);
176
+ if (varType) {
177
+ exports.set(decl.target, varType);
178
+ }
179
+ }
180
+ } else if (exportStmt.type === "SelectiveReExportStatement") {
181
+ // Re-export: export add, sub from primitives
182
+ const sourceExports = this.getModuleExportTypes(exportStmt.modulePath);
183
+ if (sourceExports) {
184
+ for (const spec of exportStmt.specifiers) {
185
+ const sourceType = sourceExports.get(spec.name);
186
+ if (sourceType) {
187
+ const exportName = spec.alias || spec.name;
188
+ exports.set(exportName, sourceType);
189
+ // Check if the source export is an extension
190
+ if (sourceExports._extensions && sourceExports._extensions.has(spec.name)) {
191
+ extensionExports.add(exportName);
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ // NamespaceReExportStatement handled differently (exports namespace object)
198
+ }
199
+
200
+ // Attach extension info to the exports map
201
+ exports._extensions = extensionExports;
202
+ return exports;
203
+ } catch (error) {
204
+ // Module analysis failed - log but don't throw
205
+ this.addWarning(`Failed to analyze module '${modulePath}': ${error.message}`, null);
206
+ return null;
207
+ }
208
+ },
209
+
210
+ /**
211
+ * Process an import statement during module analysis.
212
+ * Registers imported names with their types from the source module.
213
+ * Reports errors for imports that don't exist in the module.
214
+ */
215
+ analyzeModuleImport(importStmt, env) {
216
+ // Register complex module overloads when any import from complex module is seen
217
+ // This allows operators like + - * / to work with complex numbers
218
+ if (importStmt.modulePath === 'complex') {
219
+ this._registerComplexOverloads();
220
+ }
221
+
222
+ const sourceExports = this.getModuleExportTypes(importStmt.modulePath);
223
+
224
+ for (const spec of importStmt.specifiers) {
225
+ // Support both spec.name (old format) and spec.importedName (new format)
226
+ const importedName = spec.importedName || spec.name;
227
+ const localName = spec.localName || importedName;
228
+
229
+ if (sourceExports) {
230
+ const sourceType = sourceExports.get(importedName);
231
+ if (sourceType) {
232
+ env.define(localName, sourceType);
233
+ continue;
234
+ }
235
+ // Module was analyzed but import name doesn't exist - report error
236
+ this.addError(
237
+ `'${importedName}' is not exported from module '${importStmt.modulePath}'`,
238
+ spec.location || importStmt.location
239
+ );
240
+ // Still define with type variable to allow continued type checking
241
+ env.define(localName, freshTypeVar());
242
+ } else {
243
+ // Module couldn't be analyzed - fall back to type variable silently
244
+ env.define(localName, freshTypeVar());
245
+ }
246
+ }
247
+ },
248
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Operator Mappings - maps operators to overload function names
3
+ */
4
+
5
+ /**
6
+ * Map binary operators to overload function names
7
+ */
8
+ export const BINARY_OP_NAMES = {
9
+ "+": "__op_add",
10
+ "-": "__op_sub",
11
+ "*": "__op_mul",
12
+ "/": "__op_div",
13
+ "%": "__op_mod",
14
+ "^": "__op_pow",
15
+ ".+": "__op_elem_add",
16
+ ".-": "__op_elem_sub",
17
+ ".*": "__op_elem_mul",
18
+ "./": "__op_elem_div",
19
+ ".^": "__op_elem_pow",
20
+ "@": "__op_matmul",
21
+ "<": "__op_lt",
22
+ ">": "__op_gt",
23
+ "<=": "__op_lte",
24
+ ">=": "__op_gte",
25
+ "==": "__op_eq",
26
+ "!=": "__op_neq",
27
+ };
28
+
29
+ /**
30
+ * Map unary operators to overload function names
31
+ */
32
+ export const UNARY_OP_NAMES = {
33
+ "-": "__op_neg",
34
+ "!": "__op_not",
35
+ };