edict-lang 1.2.0 → 1.6.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/README.md +39 -13
- package/dist/ast/nodes.d.ts +4 -3
- package/dist/ast/nodes.d.ts.map +1 -1
- package/dist/ast/nodes.js +1 -0
- package/dist/ast/nodes.js.map +1 -1
- package/dist/ast/type-constants.d.ts +12 -0
- package/dist/ast/type-constants.d.ts.map +1 -0
- package/dist/ast/type-constants.js +16 -0
- package/dist/ast/type-constants.js.map +1 -0
- package/dist/ast/types.d.ts +1 -1
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/builtins/builtin-enums.d.ts +12 -0
- package/dist/builtins/builtin-enums.d.ts.map +1 -0
- package/dist/builtins/builtin-enums.js +45 -0
- package/dist/builtins/builtin-enums.js.map +1 -0
- package/dist/builtins/builtin-types.d.ts +20 -0
- package/dist/builtins/builtin-types.d.ts.map +1 -0
- package/dist/builtins/builtin-types.js +6 -0
- package/dist/builtins/builtin-types.js.map +1 -0
- package/dist/builtins/builtins.d.ts +3 -0
- package/dist/builtins/builtins.d.ts.map +1 -0
- package/dist/builtins/builtins.js +3 -0
- package/dist/builtins/builtins.js.map +1 -0
- package/dist/builtins/domains/array.d.ts +3 -0
- package/dist/builtins/domains/array.d.ts.map +1 -0
- package/dist/builtins/domains/array.js +236 -0
- package/dist/builtins/domains/array.js.map +1 -0
- package/dist/builtins/domains/core.d.ts +3 -0
- package/dist/builtins/domains/core.d.ts.map +1 -0
- package/dist/builtins/domains/core.js +45 -0
- package/dist/builtins/domains/core.js.map +1 -0
- package/dist/builtins/domains/crypto.d.ts +3 -0
- package/dist/builtins/domains/crypto.d.ts.map +1 -0
- package/dist/builtins/domains/crypto.js +49 -0
- package/dist/builtins/domains/crypto.js.map +1 -0
- package/dist/builtins/domains/datetime.d.ts +3 -0
- package/dist/builtins/domains/datetime.d.ts.map +1 -0
- package/dist/builtins/domains/datetime.js +45 -0
- package/dist/builtins/domains/datetime.js.map +1 -0
- package/dist/builtins/domains/http.d.ts +3 -0
- package/dist/builtins/domains/http.d.ts.map +1 -0
- package/dist/builtins/domains/http.js +55 -0
- package/dist/builtins/domains/http.js.map +1 -0
- package/dist/builtins/domains/int64.d.ts +3 -0
- package/dist/builtins/domains/int64.d.ts.map +1 -0
- package/dist/builtins/domains/int64.js +31 -0
- package/dist/builtins/domains/int64.js.map +1 -0
- package/dist/builtins/domains/io.d.ts +3 -0
- package/dist/builtins/domains/io.d.ts.map +1 -0
- package/dist/builtins/domains/io.js +79 -0
- package/dist/builtins/domains/io.js.map +1 -0
- package/dist/builtins/domains/json.d.ts +3 -0
- package/dist/builtins/domains/json.d.ts.map +1 -0
- package/dist/builtins/domains/json.js +47 -0
- package/dist/builtins/domains/json.js.map +1 -0
- package/dist/builtins/domains/math.d.ts +3 -0
- package/dist/builtins/domains/math.d.ts.map +1 -0
- package/dist/builtins/domains/math.js +47 -0
- package/dist/builtins/domains/math.js.map +1 -0
- package/dist/builtins/domains/option.d.ts +3 -0
- package/dist/builtins/domains/option.d.ts.map +1 -0
- package/dist/builtins/domains/option.js +56 -0
- package/dist/builtins/domains/option.js.map +1 -0
- package/dist/builtins/domains/random.d.ts +3 -0
- package/dist/builtins/domains/random.d.ts.map +1 -0
- package/dist/builtins/domains/random.js +50 -0
- package/dist/builtins/domains/random.js.map +1 -0
- package/dist/builtins/domains/regex.d.ts +3 -0
- package/dist/builtins/domains/regex.d.ts.map +1 -0
- package/dist/builtins/domains/regex.js +69 -0
- package/dist/builtins/domains/regex.js.map +1 -0
- package/dist/builtins/domains/result.d.ts +3 -0
- package/dist/builtins/domains/result.d.ts.map +1 -0
- package/dist/builtins/domains/result.js +84 -0
- package/dist/builtins/domains/result.js.map +1 -0
- package/dist/builtins/domains/string.d.ts +3 -0
- package/dist/builtins/domains/string.d.ts.map +1 -0
- package/dist/builtins/domains/string.js +139 -0
- package/dist/builtins/domains/string.js.map +1 -0
- package/dist/builtins/domains/type-conversion.d.ts +3 -0
- package/dist/builtins/domains/type-conversion.d.ts.map +1 -0
- package/dist/builtins/domains/type-conversion.js +42 -0
- package/dist/builtins/domains/type-conversion.js.map +1 -0
- package/dist/builtins/host-helpers.d.ts +66 -0
- package/dist/builtins/host-helpers.d.ts.map +1 -0
- package/dist/builtins/host-helpers.js +127 -0
- package/dist/builtins/host-helpers.js.map +1 -0
- package/dist/builtins/registry.d.ts +48 -0
- package/dist/builtins/registry.d.ts.map +1 -0
- package/dist/builtins/registry.js +118 -0
- package/dist/builtins/registry.js.map +1 -0
- package/dist/check.d.ts +9 -2
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +30 -9
- package/dist/check.js.map +1 -1
- package/dist/checker/check.d.ts +18 -1
- package/dist/checker/check.d.ts.map +1 -1
- package/dist/checker/check.js +157 -108
- package/dist/checker/check.js.map +1 -1
- package/dist/checker/type-env.d.ts +2 -0
- package/dist/checker/type-env.d.ts.map +1 -1
- package/dist/checker/type-env.js +9 -0
- package/dist/checker/type-env.js.map +1 -1
- package/dist/codegen/browser-host-adapter.d.ts +29 -0
- package/dist/codegen/browser-host-adapter.d.ts.map +1 -0
- package/dist/codegen/browser-host-adapter.js +51 -0
- package/dist/codegen/browser-host-adapter.js.map +1 -0
- package/dist/codegen/builtins.d.ts +2 -26
- package/dist/codegen/builtins.d.ts.map +1 -1
- package/dist/codegen/builtins.js +2 -341
- package/dist/codegen/builtins.js.map +1 -1
- package/dist/codegen/closures.d.ts +17 -0
- package/dist/codegen/closures.d.ts.map +1 -0
- package/dist/codegen/closures.js +140 -0
- package/dist/codegen/closures.js.map +1 -0
- package/dist/codegen/codegen.d.ts +6 -30
- package/dist/codegen/codegen.d.ts.map +1 -1
- package/dist/codegen/codegen.js +154 -1103
- package/dist/codegen/codegen.js.map +1 -1
- package/dist/codegen/collect-strings.d.ts +4 -0
- package/dist/codegen/collect-strings.d.ts.map +1 -0
- package/dist/codegen/collect-strings.js +76 -0
- package/dist/codegen/collect-strings.js.map +1 -0
- package/dist/codegen/compile-calls.d.ts +10 -0
- package/dist/codegen/compile-calls.d.ts.map +1 -0
- package/dist/codegen/compile-calls.js +374 -0
- package/dist/codegen/compile-calls.js.map +1 -0
- package/dist/codegen/compile-data.d.ts +22 -0
- package/dist/codegen/compile-data.d.ts.map +1 -0
- package/dist/codegen/compile-data.js +243 -0
- package/dist/codegen/compile-data.js.map +1 -0
- package/dist/codegen/compile-expr.d.ts +10 -0
- package/dist/codegen/compile-expr.d.ts.map +1 -0
- package/dist/codegen/compile-expr.js +156 -0
- package/dist/codegen/compile-expr.js.map +1 -0
- package/dist/codegen/compile-match.d.ts +7 -0
- package/dist/codegen/compile-match.d.ts.map +1 -0
- package/dist/codegen/compile-match.js +195 -0
- package/dist/codegen/compile-match.js.map +1 -0
- package/dist/codegen/compile-scalars.d.ts +25 -0
- package/dist/codegen/compile-scalars.d.ts.map +1 -0
- package/dist/codegen/compile-scalars.js +211 -0
- package/dist/codegen/compile-scalars.js.map +1 -0
- package/dist/codegen/hof-generators.d.ts +39 -0
- package/dist/codegen/hof-generators.d.ts.map +1 -0
- package/dist/codegen/hof-generators.js +336 -0
- package/dist/codegen/hof-generators.js.map +1 -0
- package/dist/codegen/host-adapter.d.ts +44 -0
- package/dist/codegen/host-adapter.d.ts.map +1 -0
- package/dist/codegen/host-adapter.js +9 -0
- package/dist/codegen/host-adapter.js.map +1 -0
- package/dist/codegen/imports.d.ts +15 -0
- package/dist/codegen/imports.d.ts.map +1 -0
- package/dist/codegen/imports.js +165 -0
- package/dist/codegen/imports.js.map +1 -0
- package/dist/codegen/node-host-adapter.d.ts +35 -0
- package/dist/codegen/node-host-adapter.d.ts.map +1 -0
- package/dist/codegen/node-host-adapter.js +155 -0
- package/dist/codegen/node-host-adapter.js.map +1 -0
- package/dist/codegen/runner.d.ts +36 -2
- package/dist/codegen/runner.d.ts.map +1 -1
- package/dist/codegen/runner.js +147 -271
- package/dist/codegen/runner.js.map +1 -1
- package/dist/codegen/types.d.ts +96 -0
- package/dist/codegen/types.d.ts.map +1 -0
- package/dist/codegen/types.js +63 -0
- package/dist/codegen/types.js.map +1 -0
- package/dist/compact/expand.d.ts +25 -0
- package/dist/compact/expand.d.ts.map +1 -0
- package/dist/compact/expand.js +199 -0
- package/dist/compact/expand.js.map +1 -0
- package/dist/compile.d.ts +2 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +2 -2
- package/dist/compile.js.map +1 -1
- package/dist/contracts/translate.js.map +1 -1
- package/dist/contracts/verify.d.ts +10 -2
- package/dist/contracts/verify.d.ts.map +1 -1
- package/dist/contracts/verify.js +26 -21
- package/dist/contracts/verify.js.map +1 -1
- package/dist/effects/call-graph.d.ts.map +1 -1
- package/dist/effects/call-graph.js +27 -4
- package/dist/effects/call-graph.js.map +1 -1
- package/dist/effects/effect-check.d.ts +10 -2
- package/dist/effects/effect-check.d.ts.map +1 -1
- package/dist/effects/effect-check.js +12 -7
- package/dist/effects/effect-check.js.map +1 -1
- package/dist/errors/error-catalog.d.ts +1 -1
- package/dist/errors/error-catalog.d.ts.map +1 -1
- package/dist/errors/error-catalog.js +119 -0
- package/dist/errors/error-catalog.js.map +1 -1
- package/dist/errors/structured-errors.d.ts +28 -1
- package/dist/errors/structured-errors.d.ts.map +1 -1
- package/dist/errors/structured-errors.js +9 -0
- package/dist/errors/structured-errors.js.map +1 -1
- package/dist/index.d.ts +20 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -10
- package/dist/index.js.map +1 -1
- package/dist/lint/lint.d.ts +9 -0
- package/dist/lint/lint.d.ts.map +1 -0
- package/dist/lint/lint.js +354 -0
- package/dist/lint/lint.js.map +1 -0
- package/dist/lint/warnings.d.ts +54 -0
- package/dist/lint/warnings.d.ts.map +1 -0
- package/dist/lint/warnings.js +39 -0
- package/dist/lint/warnings.js.map +1 -0
- package/dist/mcp/create-server.d.ts.map +1 -1
- package/dist/mcp/create-server.js +66 -5
- package/dist/mcp/create-server.js.map +1 -1
- package/dist/mcp/handlers.d.ts +21 -5
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js +65 -15
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/prompts.d.ts +17 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +181 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/server.js +1 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/resolver/resolve.d.ts.map +1 -1
- package/dist/resolver/resolve.js +62 -16
- package/dist/resolver/resolve.js.map +1 -1
- package/dist/validator/node-validators.d.ts.map +1 -1
- package/dist/validator/node-validators.js +60 -5
- package/dist/validator/node-validators.js.map +1 -1
- package/package.json +4 -2
package/dist/checker/check.js
CHANGED
|
@@ -7,22 +7,28 @@ import { typeMismatch, arityMismatch, notAFunction, unknownField, unknownRecord,
|
|
|
7
7
|
import { findCandidates } from "../resolver/levenshtein.js";
|
|
8
8
|
import { TypeEnv } from "./type-env.js";
|
|
9
9
|
import { typesEqual, isUnknown, resolveType } from "./types-equal.js";
|
|
10
|
-
import { BUILTIN_FUNCTIONS } from "../
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const FLOAT_TYPE = { kind: "basic", name: "Float" };
|
|
14
|
-
const STRING_TYPE = { kind: "basic", name: "String" };
|
|
15
|
-
const BOOL_TYPE = { kind: "basic", name: "Bool" };
|
|
10
|
+
import { BUILTIN_FUNCTIONS } from "../builtins/builtins.js";
|
|
11
|
+
import { OPTION_ENUM_DEF, RESULT_ENUM_DEF } from "../builtins/builtin-enums.js";
|
|
12
|
+
import { UNKNOWN_TYPE, INT_TYPE, FLOAT_TYPE, STRING_TYPE, BOOL_TYPE } from "../ast/type-constants.js";
|
|
16
13
|
/**
|
|
17
14
|
* Entry point: type-check a validated + resolved Edict module.
|
|
18
15
|
*/
|
|
19
16
|
export function typeCheck(module) {
|
|
20
17
|
const errors = [];
|
|
18
|
+
const typeInfo = {
|
|
19
|
+
inferredReturnTypes: new Map(),
|
|
20
|
+
inferredLetTypes: new Map(),
|
|
21
|
+
inferredLambdaParamTypes: new Map(),
|
|
22
|
+
};
|
|
21
23
|
const rootEnv = new TypeEnv();
|
|
22
24
|
// Register built-in function signatures
|
|
23
25
|
for (const [name, builtin] of BUILTIN_FUNCTIONS) {
|
|
24
26
|
rootEnv.bind(name, builtin.type);
|
|
25
27
|
}
|
|
28
|
+
// Register built-in enum type definitions (Option, Result)
|
|
29
|
+
// so enum_constructor and match patterns work through typeCheck.
|
|
30
|
+
rootEnv.registerTypeDef("Option", OPTION_ENUM_DEF);
|
|
31
|
+
rootEnv.registerTypeDef("Result", RESULT_ENUM_DEF);
|
|
26
32
|
// Register type definitions (records, enums, type aliases)
|
|
27
33
|
for (const def of module.definitions) {
|
|
28
34
|
registerTypeDef(def, rootEnv);
|
|
@@ -31,25 +37,26 @@ export function typeCheck(module) {
|
|
|
31
37
|
for (const def of module.definitions) {
|
|
32
38
|
registerValueDef(def, rootEnv);
|
|
33
39
|
}
|
|
34
|
-
// Register imports
|
|
40
|
+
// Register imports — use declared types when available, fall back to unknown
|
|
35
41
|
for (const imp of module.imports) {
|
|
36
42
|
for (const name of imp.names) {
|
|
37
|
-
|
|
43
|
+
const declaredType = imp.types?.[name];
|
|
44
|
+
rootEnv.bind(name, declaredType ?? UNKNOWN_TYPE);
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
// Type-check each definition
|
|
41
48
|
for (const def of module.definitions) {
|
|
42
49
|
switch (def.kind) {
|
|
43
50
|
case "fn":
|
|
44
|
-
checkFunction(def, rootEnv, errors);
|
|
51
|
+
checkFunction(def, rootEnv, errors, typeInfo);
|
|
45
52
|
break;
|
|
46
53
|
case "const":
|
|
47
|
-
checkConst(def, rootEnv, errors);
|
|
54
|
+
checkConst(def, rootEnv, errors, typeInfo);
|
|
48
55
|
break;
|
|
49
56
|
// record, enum, type — no body to type-check
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
|
-
return errors;
|
|
59
|
+
return { errors, typeInfo };
|
|
53
60
|
}
|
|
54
61
|
// =============================================================================
|
|
55
62
|
// Registration
|
|
@@ -68,9 +75,9 @@ function registerValueDef(def, env) {
|
|
|
68
75
|
case "fn": {
|
|
69
76
|
const fnType = {
|
|
70
77
|
kind: "fn_type",
|
|
71
|
-
params: def.params.map((p) => p.type),
|
|
78
|
+
params: def.params.map((p) => p.type ?? UNKNOWN_TYPE),
|
|
72
79
|
effects: [...def.effects],
|
|
73
|
-
returnType: def.returnType,
|
|
80
|
+
returnType: def.returnType ?? UNKNOWN_TYPE,
|
|
74
81
|
};
|
|
75
82
|
env.bind(def.name, fnType);
|
|
76
83
|
break;
|
|
@@ -83,89 +90,96 @@ function registerValueDef(def, env) {
|
|
|
83
90
|
// =============================================================================
|
|
84
91
|
// Function checking
|
|
85
92
|
// =============================================================================
|
|
86
|
-
function checkFunction(fn, rootEnv, errors) {
|
|
93
|
+
function checkFunction(fn, rootEnv, errors, typeInfo) {
|
|
87
94
|
const fnEnv = rootEnv.child();
|
|
88
95
|
// Bind params
|
|
89
96
|
for (const param of fn.params) {
|
|
90
97
|
fnEnv.bind(param.name, param.type);
|
|
91
98
|
}
|
|
92
|
-
//
|
|
99
|
+
// Infer body type first (needed when returnType is omitted)
|
|
100
|
+
const bodyType = inferExprList(fn.body, fnEnv, errors, typeInfo);
|
|
101
|
+
// Determine effective return type: explicit annotation or inferred from body
|
|
102
|
+
const hadExplicitReturnType = !!fn.returnType;
|
|
103
|
+
const effectiveReturnType = fn.returnType ?? bodyType;
|
|
104
|
+
// Store inferred return type in side-table (no AST mutation)
|
|
105
|
+
if (!fn.returnType) {
|
|
106
|
+
typeInfo.inferredReturnTypes.set(fn.id, effectiveReturnType);
|
|
107
|
+
}
|
|
108
|
+
// Check contracts (postconditions bind `result` to the effective return type)
|
|
93
109
|
for (const contract of fn.contracts) {
|
|
94
110
|
if (contract.kind === "post") {
|
|
95
111
|
const postEnv = fnEnv.child();
|
|
96
|
-
postEnv.bind("result",
|
|
97
|
-
const condType = inferExpr(contract.condition, postEnv, errors);
|
|
98
|
-
checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors
|
|
112
|
+
postEnv.bind("result", effectiveReturnType);
|
|
113
|
+
const condType = inferExpr(contract.condition, postEnv, errors, typeInfo);
|
|
114
|
+
checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors);
|
|
99
115
|
}
|
|
100
116
|
else {
|
|
101
|
-
const condType = inferExpr(contract.condition, fnEnv, errors);
|
|
102
|
-
checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors
|
|
117
|
+
const condType = inferExpr(contract.condition, fnEnv, errors, typeInfo);
|
|
118
|
+
checkExpectedType(condType, BOOL_TYPE, contract.id, fnEnv, errors);
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (bodyType && !isUnknown(bodyType) && !isUnknown(resolveType(fn.returnType, fnEnv))) {
|
|
109
|
-
checkExpectedType(bodyType, fn.returnType, fn.id, fnEnv, errors, "function body type must match return type");
|
|
121
|
+
// If returnType was explicit, check body against it
|
|
122
|
+
if (hadExplicitReturnType && bodyType && !isUnknown(bodyType) && !isUnknown(resolveType(fn.returnType, fnEnv))) {
|
|
123
|
+
checkExpectedType(bodyType, fn.returnType, fn.id, fnEnv, errors);
|
|
110
124
|
}
|
|
111
125
|
}
|
|
112
|
-
function checkConst(def, env, errors) {
|
|
113
|
-
const valType = inferExpr(def.value, env, errors);
|
|
114
|
-
checkExpectedType(valType, def.type, def.id, env, errors
|
|
126
|
+
function checkConst(def, env, errors, typeInfo) {
|
|
127
|
+
const valType = inferExpr(def.value, env, errors, typeInfo);
|
|
128
|
+
checkExpectedType(valType, def.type, def.id, env, errors);
|
|
115
129
|
}
|
|
116
130
|
// =============================================================================
|
|
117
131
|
// Type inference
|
|
118
132
|
// =============================================================================
|
|
119
|
-
function inferExprList(exprs, env, errors) {
|
|
133
|
+
function inferExprList(exprs, env, errors, typeInfo) {
|
|
120
134
|
if (exprs.length === 0)
|
|
121
135
|
return UNKNOWN_TYPE;
|
|
122
136
|
let currentEnv = env;
|
|
123
137
|
let lastType = UNKNOWN_TYPE;
|
|
124
138
|
for (const expr of exprs) {
|
|
125
|
-
lastType = inferExpr(expr, currentEnv, errors);
|
|
139
|
+
lastType = inferExpr(expr, currentEnv, errors, typeInfo);
|
|
126
140
|
if (expr.kind === "let") {
|
|
127
141
|
currentEnv = currentEnv.child();
|
|
128
|
-
// Bind the let name in child env
|
|
129
|
-
const bindType = expr.type ?? lastType;
|
|
142
|
+
// Bind the let name in child env, using inferred type from side-table if needed
|
|
143
|
+
const bindType = expr.type ?? typeInfo.inferredLetTypes.get(expr.id) ?? lastType;
|
|
130
144
|
currentEnv.bind(expr.name, bindType);
|
|
131
145
|
}
|
|
132
146
|
}
|
|
133
147
|
return lastType;
|
|
134
148
|
}
|
|
135
|
-
function inferExpr(expr, env, errors) {
|
|
149
|
+
function inferExpr(expr, env, errors, typeInfo) {
|
|
136
150
|
switch (expr.kind) {
|
|
137
151
|
case "literal":
|
|
138
152
|
return inferLiteral(expr);
|
|
139
153
|
case "ident":
|
|
140
154
|
return env.getType(expr.name) ?? UNKNOWN_TYPE;
|
|
141
155
|
case "binop":
|
|
142
|
-
return inferBinop(expr, env, errors);
|
|
156
|
+
return inferBinop(expr, env, errors, typeInfo);
|
|
143
157
|
case "unop":
|
|
144
|
-
return inferUnop(expr, env, errors);
|
|
158
|
+
return inferUnop(expr, env, errors, typeInfo);
|
|
145
159
|
case "call":
|
|
146
|
-
return inferCall(expr, env, errors);
|
|
160
|
+
return inferCall(expr, env, errors, typeInfo);
|
|
147
161
|
case "if":
|
|
148
|
-
return inferIf(expr, env, errors);
|
|
162
|
+
return inferIf(expr, env, errors, typeInfo);
|
|
149
163
|
case "let":
|
|
150
|
-
return inferLet(expr, env, errors);
|
|
164
|
+
return inferLet(expr, env, errors, typeInfo);
|
|
151
165
|
case "match":
|
|
152
|
-
return inferMatch(expr, env, errors);
|
|
166
|
+
return inferMatch(expr, env, errors, typeInfo);
|
|
153
167
|
case "array":
|
|
154
|
-
return inferArray(expr, env, errors);
|
|
168
|
+
return inferArray(expr, env, errors, typeInfo);
|
|
155
169
|
case "tuple_expr":
|
|
156
|
-
return inferTuple(expr, env, errors);
|
|
170
|
+
return inferTuple(expr, env, errors, typeInfo);
|
|
157
171
|
case "record_expr":
|
|
158
|
-
return inferRecordExpr(expr, env, errors);
|
|
172
|
+
return inferRecordExpr(expr, env, errors, typeInfo);
|
|
159
173
|
case "enum_constructor":
|
|
160
|
-
return inferEnumConstructor(expr, env, errors);
|
|
174
|
+
return inferEnumConstructor(expr, env, errors, typeInfo);
|
|
161
175
|
case "access":
|
|
162
|
-
return inferAccess(expr, env, errors);
|
|
176
|
+
return inferAccess(expr, env, errors, typeInfo);
|
|
163
177
|
case "lambda":
|
|
164
|
-
return inferLambda(expr, env, errors);
|
|
178
|
+
return inferLambda(expr, env, errors, typeInfo);
|
|
165
179
|
case "block":
|
|
166
|
-
return inferExprList(expr.body, env.child(), errors);
|
|
180
|
+
return inferExprList(expr.body, env.child(), errors, typeInfo);
|
|
167
181
|
case "string_interp":
|
|
168
|
-
return inferStringInterp(expr, env, errors);
|
|
182
|
+
return inferStringInterp(expr, env, errors, typeInfo);
|
|
169
183
|
}
|
|
170
184
|
}
|
|
171
185
|
// =============================================================================
|
|
@@ -184,9 +198,9 @@ function inferLiteral(expr) {
|
|
|
184
198
|
}
|
|
185
199
|
return UNKNOWN_TYPE;
|
|
186
200
|
}
|
|
187
|
-
function inferBinop(expr, env, errors) {
|
|
188
|
-
const leftType = inferExpr(expr.left, env, errors);
|
|
189
|
-
const rightType = inferExpr(expr.right, env, errors);
|
|
201
|
+
function inferBinop(expr, env, errors, typeInfo) {
|
|
202
|
+
const leftType = inferExpr(expr.left, env, errors, typeInfo);
|
|
203
|
+
const rightType = inferExpr(expr.right, env, errors, typeInfo);
|
|
190
204
|
// unknown propagation
|
|
191
205
|
if (isUnknown(leftType) || isUnknown(rightType))
|
|
192
206
|
return UNKNOWN_TYPE;
|
|
@@ -234,8 +248,8 @@ function inferBinop(expr, env, errors) {
|
|
|
234
248
|
errors.push(typeMismatch(expr.id, UNKNOWN_TYPE, leftType)); // Expected numeric
|
|
235
249
|
return UNKNOWN_TYPE;
|
|
236
250
|
}
|
|
237
|
-
function inferUnop(expr, env, errors) {
|
|
238
|
-
const operandType = inferExpr(expr.operand, env, errors);
|
|
251
|
+
function inferUnop(expr, env, errors, typeInfo) {
|
|
252
|
+
const operandType = inferExpr(expr.operand, env, errors, typeInfo);
|
|
239
253
|
if (isUnknown(operandType))
|
|
240
254
|
return UNKNOWN_TYPE;
|
|
241
255
|
if (expr.op === "not") {
|
|
@@ -251,12 +265,12 @@ function inferUnop(expr, env, errors) {
|
|
|
251
265
|
}
|
|
252
266
|
return operandType;
|
|
253
267
|
}
|
|
254
|
-
function inferCall(expr, env, errors) {
|
|
255
|
-
const fnType = inferExpr(expr.fn, env, errors);
|
|
268
|
+
function inferCall(expr, env, errors, typeInfo) {
|
|
269
|
+
const fnType = inferExpr(expr.fn, env, errors, typeInfo);
|
|
256
270
|
if (isUnknown(fnType)) {
|
|
257
271
|
// If fn is unknown, we can't type-check args. Infer them for side effects (let bindings)
|
|
258
272
|
for (const arg of expr.args)
|
|
259
|
-
inferExpr(arg, env, errors);
|
|
273
|
+
inferExpr(arg, env, errors, typeInfo);
|
|
260
274
|
return UNKNOWN_TYPE;
|
|
261
275
|
}
|
|
262
276
|
const resolved = resolveType(fnType, env);
|
|
@@ -264,7 +278,7 @@ function inferCall(expr, env, errors) {
|
|
|
264
278
|
errors.push(notAFunction(expr.id, fnType));
|
|
265
279
|
// Still infer arg types for error propagation
|
|
266
280
|
for (const arg of expr.args)
|
|
267
|
-
inferExpr(arg, env, errors);
|
|
281
|
+
inferExpr(arg, env, errors, typeInfo);
|
|
268
282
|
return UNKNOWN_TYPE;
|
|
269
283
|
}
|
|
270
284
|
// Check arity
|
|
@@ -274,21 +288,27 @@ function inferCall(expr, env, errors) {
|
|
|
274
288
|
// Check arg types (up to the minimum of args/params)
|
|
275
289
|
const checkCount = Math.min(expr.args.length, resolved.params.length);
|
|
276
290
|
for (let i = 0; i < checkCount; i++) {
|
|
277
|
-
const
|
|
278
|
-
|
|
291
|
+
const arg = expr.args[i];
|
|
292
|
+
const expectedParamType = resolved.params[i];
|
|
293
|
+
// If arg is a lambda and expected param is fn_type, propagate param types
|
|
294
|
+
const resolvedExpected = resolveType(expectedParamType, env);
|
|
295
|
+
const argType = (arg.kind === "lambda" && resolvedExpected.kind === "fn_type")
|
|
296
|
+
? inferLambdaWithContext(arg, resolvedExpected, env, errors, typeInfo)
|
|
297
|
+
: inferExpr(arg, env, errors, typeInfo);
|
|
298
|
+
checkExpectedType(argType, expectedParamType, arg.id, env, errors);
|
|
279
299
|
}
|
|
280
300
|
// Infer remaining surplus args
|
|
281
301
|
for (let i = checkCount; i < expr.args.length; i++) {
|
|
282
|
-
inferExpr(expr.args[i], env, errors);
|
|
302
|
+
inferExpr(expr.args[i], env, errors, typeInfo);
|
|
283
303
|
}
|
|
284
304
|
return resolved.returnType;
|
|
285
305
|
}
|
|
286
|
-
function inferIf(expr, env, errors) {
|
|
287
|
-
const condType = inferExpr(expr.condition, env, errors);
|
|
288
|
-
checkExpectedType(condType, BOOL_TYPE, expr.id, env, errors
|
|
289
|
-
const thenType = inferExprList(expr.then, env.child(), errors);
|
|
306
|
+
function inferIf(expr, env, errors, typeInfo) {
|
|
307
|
+
const condType = inferExpr(expr.condition, env, errors, typeInfo);
|
|
308
|
+
checkExpectedType(condType, BOOL_TYPE, expr.id, env, errors);
|
|
309
|
+
const thenType = inferExprList(expr.then, env.child(), errors, typeInfo);
|
|
290
310
|
if (expr.else) {
|
|
291
|
-
const elseType = inferExprList(expr.else, env.child(), errors);
|
|
311
|
+
const elseType = inferExprList(expr.else, env.child(), errors, typeInfo);
|
|
292
312
|
if (!isUnknown(thenType) && !isUnknown(elseType)) {
|
|
293
313
|
if (!typesEqual(thenType, elseType, env)) {
|
|
294
314
|
errors.push(typeMismatch(expr.id, thenType, elseType));
|
|
@@ -299,21 +319,25 @@ function inferIf(expr, env, errors) {
|
|
|
299
319
|
// No else → Option<thenType>
|
|
300
320
|
return { kind: "option", inner: thenType };
|
|
301
321
|
}
|
|
302
|
-
function inferLet(expr, env, errors) {
|
|
303
|
-
const valType = inferExpr(expr.value, env, errors);
|
|
322
|
+
function inferLet(expr, env, errors, typeInfo) {
|
|
323
|
+
const valType = inferExpr(expr.value, env, errors, typeInfo);
|
|
304
324
|
if (expr.type) {
|
|
305
|
-
checkExpectedType(valType, expr.type, expr.id, env, errors
|
|
325
|
+
checkExpectedType(valType, expr.type, expr.id, env, errors);
|
|
306
326
|
return expr.type;
|
|
307
327
|
}
|
|
328
|
+
// Store inferred type in side-table (no AST mutation)
|
|
329
|
+
if (!isUnknown(valType)) {
|
|
330
|
+
typeInfo.inferredLetTypes.set(expr.id, valType);
|
|
331
|
+
}
|
|
308
332
|
return valType;
|
|
309
333
|
}
|
|
310
|
-
function inferMatch(expr, env, errors) {
|
|
311
|
-
const targetType = inferExpr(expr.target, env, errors);
|
|
334
|
+
function inferMatch(expr, env, errors, typeInfo) {
|
|
335
|
+
const targetType = inferExpr(expr.target, env, errors, typeInfo);
|
|
312
336
|
let resultType = null;
|
|
313
337
|
for (const arm of expr.arms) {
|
|
314
338
|
const armEnv = env.child();
|
|
315
339
|
inferPattern(arm.pattern, targetType, armEnv, env, errors);
|
|
316
|
-
const bodyType = inferExprList(arm.body, armEnv, errors);
|
|
340
|
+
const bodyType = inferExprList(arm.body, armEnv, errors, typeInfo);
|
|
317
341
|
if (resultType === null) {
|
|
318
342
|
resultType = bodyType;
|
|
319
343
|
}
|
|
@@ -382,13 +406,13 @@ function inferPattern(pattern, targetType, armEnv, rootEnv, errors) {
|
|
|
382
406
|
}
|
|
383
407
|
}
|
|
384
408
|
}
|
|
385
|
-
function inferArray(expr, env, errors) {
|
|
409
|
+
function inferArray(expr, env, errors, typeInfo) {
|
|
386
410
|
if (expr.elements.length === 0) {
|
|
387
411
|
return { kind: "array", element: UNKNOWN_TYPE };
|
|
388
412
|
}
|
|
389
|
-
const firstType = inferExpr(expr.elements[0], env, errors);
|
|
413
|
+
const firstType = inferExpr(expr.elements[0], env, errors, typeInfo);
|
|
390
414
|
for (let i = 1; i < expr.elements.length; i++) {
|
|
391
|
-
const elType = inferExpr(expr.elements[i], env, errors);
|
|
415
|
+
const elType = inferExpr(expr.elements[i], env, errors, typeInfo);
|
|
392
416
|
if (!isUnknown(firstType) && !isUnknown(elType)) {
|
|
393
417
|
if (!typesEqual(firstType, elType, env)) {
|
|
394
418
|
errors.push(typeMismatch(expr.elements[i].id, firstType, elType));
|
|
@@ -397,31 +421,31 @@ function inferArray(expr, env, errors) {
|
|
|
397
421
|
}
|
|
398
422
|
return { kind: "array", element: firstType };
|
|
399
423
|
}
|
|
400
|
-
function inferTuple(expr, env, errors) {
|
|
401
|
-
const elementTypes = expr.elements.map((el) => inferExpr(el, env, errors));
|
|
424
|
+
function inferTuple(expr, env, errors, typeInfo) {
|
|
425
|
+
const elementTypes = expr.elements.map((el) => inferExpr(el, env, errors, typeInfo));
|
|
402
426
|
return { kind: "tuple", elements: elementTypes };
|
|
403
427
|
}
|
|
404
|
-
function inferRecordExpr(expr, env, errors) {
|
|
428
|
+
function inferRecordExpr(expr, env, errors, typeInfo) {
|
|
405
429
|
const def = env.lookupTypeDef(expr.name);
|
|
406
430
|
if (!def) {
|
|
407
|
-
const cands =
|
|
431
|
+
const cands = env.allTypeDefNames("record");
|
|
408
432
|
const suggestion = cands.length > 0
|
|
409
433
|
? { nodeId: expr.id, field: "name", value: findCandidates(expr.name, cands)[0] ?? cands[0] }
|
|
410
434
|
: undefined;
|
|
411
435
|
errors.push(unknownRecord(expr.id, expr.name, cands, suggestion));
|
|
412
436
|
// Still infer field value types
|
|
413
437
|
for (const f of expr.fields)
|
|
414
|
-
inferExpr(f.value, env, errors);
|
|
438
|
+
inferExpr(f.value, env, errors, typeInfo);
|
|
415
439
|
return UNKNOWN_TYPE;
|
|
416
440
|
}
|
|
417
441
|
if (def.kind !== "record") {
|
|
418
|
-
const cands =
|
|
442
|
+
const cands = env.allTypeDefNames("record");
|
|
419
443
|
const suggestion = cands.length > 0
|
|
420
444
|
? { nodeId: expr.id, field: "name", value: findCandidates(expr.name, cands)[0] ?? cands[0] }
|
|
421
445
|
: undefined;
|
|
422
446
|
errors.push(unknownRecord(expr.id, expr.name, cands, suggestion));
|
|
423
447
|
for (const f of expr.fields)
|
|
424
|
-
inferExpr(f.value, env, errors);
|
|
448
|
+
inferExpr(f.value, env, errors, typeInfo);
|
|
425
449
|
return UNKNOWN_TYPE;
|
|
426
450
|
}
|
|
427
451
|
const recordDef = def;
|
|
@@ -444,34 +468,34 @@ function inferRecordExpr(expr, env, errors) {
|
|
|
444
468
|
? { nodeId: expr.id, field: "field", value: cands[0] }
|
|
445
469
|
: undefined;
|
|
446
470
|
errors.push(unknownField(expr.id, expr.name, fieldInit.name, availFields, suggestion));
|
|
447
|
-
inferExpr(fieldInit.value, env, errors);
|
|
471
|
+
inferExpr(fieldInit.value, env, errors, typeInfo);
|
|
448
472
|
continue;
|
|
449
473
|
}
|
|
450
|
-
const valType = inferExpr(fieldInit.value, env, errors);
|
|
451
|
-
checkExpectedType(valType, fieldDef.type, expr.id, env, errors
|
|
474
|
+
const valType = inferExpr(fieldInit.value, env, errors, typeInfo);
|
|
475
|
+
checkExpectedType(valType, fieldDef.type, expr.id, env, errors);
|
|
452
476
|
}
|
|
453
477
|
return { kind: "named", name: expr.name };
|
|
454
478
|
}
|
|
455
|
-
function inferEnumConstructor(expr, env, errors) {
|
|
479
|
+
function inferEnumConstructor(expr, env, errors, typeInfo) {
|
|
456
480
|
const def = env.lookupTypeDef(expr.enumName);
|
|
457
481
|
if (!def) {
|
|
458
|
-
const cands =
|
|
482
|
+
const cands = env.allTypeDefNames("enum");
|
|
459
483
|
const suggestion = cands.length > 0
|
|
460
484
|
? { nodeId: expr.id, field: "enumName", value: findCandidates(expr.enumName, cands)[0] ?? cands[0] }
|
|
461
485
|
: undefined;
|
|
462
486
|
errors.push(unknownEnum(expr.id, expr.enumName, cands, suggestion));
|
|
463
487
|
for (const f of expr.fields)
|
|
464
|
-
inferExpr(f.value, env, errors);
|
|
488
|
+
inferExpr(f.value, env, errors, typeInfo);
|
|
465
489
|
return UNKNOWN_TYPE;
|
|
466
490
|
}
|
|
467
491
|
if (def.kind !== "enum") {
|
|
468
|
-
const cands =
|
|
492
|
+
const cands = env.allTypeDefNames("enum");
|
|
469
493
|
const suggestion = cands.length > 0
|
|
470
494
|
? { nodeId: expr.id, field: "enumName", value: findCandidates(expr.enumName, cands)[0] ?? cands[0] }
|
|
471
495
|
: undefined;
|
|
472
496
|
errors.push(unknownEnum(expr.id, expr.enumName, cands, suggestion));
|
|
473
497
|
for (const f of expr.fields)
|
|
474
|
-
inferExpr(f.value, env, errors);
|
|
498
|
+
inferExpr(f.value, env, errors, typeInfo);
|
|
475
499
|
return UNKNOWN_TYPE;
|
|
476
500
|
}
|
|
477
501
|
const enumDef = def;
|
|
@@ -484,7 +508,7 @@ function inferEnumConstructor(expr, env, errors) {
|
|
|
484
508
|
: (availVariants.length > 0 ? { nodeId: expr.id, field: "variant", value: availVariants[0] } : undefined);
|
|
485
509
|
errors.push(unknownVariant(expr.id, expr.enumName, expr.variant, availVariants, suggestion));
|
|
486
510
|
for (const f of expr.fields)
|
|
487
|
-
inferExpr(f.value, env, errors);
|
|
511
|
+
inferExpr(f.value, env, errors, typeInfo);
|
|
488
512
|
return UNKNOWN_TYPE;
|
|
489
513
|
}
|
|
490
514
|
// Check field count and types
|
|
@@ -497,16 +521,16 @@ function inferEnumConstructor(expr, env, errors) {
|
|
|
497
521
|
? { nodeId: expr.id, field: "field", value: cands[0] }
|
|
498
522
|
: undefined;
|
|
499
523
|
errors.push(unknownField(expr.id, `${expr.enumName}.${expr.variant}`, fieldInit.name, availFields, suggestion));
|
|
500
|
-
inferExpr(fieldInit.value, env, errors);
|
|
524
|
+
inferExpr(fieldInit.value, env, errors, typeInfo);
|
|
501
525
|
continue;
|
|
502
526
|
}
|
|
503
|
-
const valType = inferExpr(fieldInit.value, env, errors);
|
|
504
|
-
checkExpectedType(valType, fieldDef.type, expr.id, env, errors
|
|
527
|
+
const valType = inferExpr(fieldInit.value, env, errors, typeInfo);
|
|
528
|
+
checkExpectedType(valType, fieldDef.type, expr.id, env, errors);
|
|
505
529
|
}
|
|
506
530
|
return { kind: "named", name: expr.enumName };
|
|
507
531
|
}
|
|
508
|
-
function inferAccess(expr, env, errors) {
|
|
509
|
-
const targetType = inferExpr(expr.target, env, errors);
|
|
532
|
+
function inferAccess(expr, env, errors, typeInfo) {
|
|
533
|
+
const targetType = inferExpr(expr.target, env, errors, typeInfo);
|
|
510
534
|
if (isUnknown(targetType))
|
|
511
535
|
return UNKNOWN_TYPE;
|
|
512
536
|
const resolved = resolveType(targetType, env);
|
|
@@ -531,22 +555,53 @@ function inferAccess(expr, env, errors) {
|
|
|
531
555
|
}
|
|
532
556
|
return field.type;
|
|
533
557
|
}
|
|
534
|
-
function inferLambda(expr, env, errors) {
|
|
558
|
+
function inferLambda(expr, env, errors, typeInfo) {
|
|
535
559
|
const lamEnv = env.child();
|
|
536
560
|
for (const param of expr.params) {
|
|
537
|
-
lamEnv.bind(param.name, param.type);
|
|
561
|
+
lamEnv.bind(param.name, param.type ?? UNKNOWN_TYPE);
|
|
562
|
+
}
|
|
563
|
+
const bodyType = inferExprList(expr.body, lamEnv, errors, typeInfo);
|
|
564
|
+
return {
|
|
565
|
+
kind: "fn_type",
|
|
566
|
+
params: expr.params.map((p) => p.type ?? UNKNOWN_TYPE),
|
|
567
|
+
effects: [],
|
|
568
|
+
returnType: bodyType,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Infer lambda type with expected fn_type context from a call site.
|
|
573
|
+
* Propagates param types from the expected signature to untyped lambda params.
|
|
574
|
+
*/
|
|
575
|
+
function inferLambdaWithContext(expr, expectedType, env, errors, typeInfo) {
|
|
576
|
+
const lamEnv = env.child();
|
|
577
|
+
for (let i = 0; i < expr.params.length; i++) {
|
|
578
|
+
const param = expr.params[i];
|
|
579
|
+
if (param.type) {
|
|
580
|
+
// Explicit type — use it
|
|
581
|
+
lamEnv.bind(param.name, param.type);
|
|
582
|
+
}
|
|
583
|
+
else if (i < expectedType.params.length) {
|
|
584
|
+
// Infer from expected fn_type
|
|
585
|
+
const inferred = expectedType.params[i];
|
|
586
|
+
// Store inferred type in side-table (no AST mutation)
|
|
587
|
+
typeInfo.inferredLambdaParamTypes.set(param.id, inferred);
|
|
588
|
+
lamEnv.bind(param.name, inferred);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
lamEnv.bind(param.name, UNKNOWN_TYPE);
|
|
592
|
+
}
|
|
538
593
|
}
|
|
539
|
-
const bodyType = inferExprList(expr.body, lamEnv, errors);
|
|
594
|
+
const bodyType = inferExprList(expr.body, lamEnv, errors, typeInfo);
|
|
540
595
|
return {
|
|
541
596
|
kind: "fn_type",
|
|
542
|
-
params: expr.params.map((p) => p.type),
|
|
597
|
+
params: expr.params.map((p) => p.type ?? typeInfo.inferredLambdaParamTypes.get(p.id) ?? UNKNOWN_TYPE),
|
|
543
598
|
effects: [],
|
|
544
599
|
returnType: bodyType,
|
|
545
600
|
};
|
|
546
601
|
}
|
|
547
|
-
function inferStringInterp(expr, env, errors) {
|
|
602
|
+
function inferStringInterp(expr, env, errors, typeInfo) {
|
|
548
603
|
for (const part of expr.parts) {
|
|
549
|
-
const partType = inferExpr(part, env, errors);
|
|
604
|
+
const partType = inferExpr(part, env, errors, typeInfo);
|
|
550
605
|
checkExpectedType(partType, STRING_TYPE, part.id, env, errors);
|
|
551
606
|
}
|
|
552
607
|
return STRING_TYPE;
|
|
@@ -554,7 +609,7 @@ function inferStringInterp(expr, env, errors) {
|
|
|
554
609
|
// =============================================================================
|
|
555
610
|
// Utility functions
|
|
556
611
|
// =============================================================================
|
|
557
|
-
function checkExpectedType(actual, expected, nodeId, env, errors
|
|
612
|
+
function checkExpectedType(actual, expected, nodeId, env, errors) {
|
|
558
613
|
if (isUnknown(actual) || isUnknown(expected))
|
|
559
614
|
return;
|
|
560
615
|
if (!typesEqual(actual, expected, env)) {
|
|
@@ -573,7 +628,7 @@ function isString(type) {
|
|
|
573
628
|
function isNumeric(type, env) {
|
|
574
629
|
const resolved = resolveType(type, env);
|
|
575
630
|
if (resolved.kind === "basic") {
|
|
576
|
-
return resolved.name === "Int" || resolved.name === "Float";
|
|
631
|
+
return resolved.name === "Int" || resolved.name === "Int64" || resolved.name === "Float";
|
|
577
632
|
}
|
|
578
633
|
if (resolved.kind === "unit_type")
|
|
579
634
|
return true;
|
|
@@ -589,10 +644,4 @@ function inferLiteralPatternType(value) {
|
|
|
589
644
|
}
|
|
590
645
|
return UNKNOWN_TYPE;
|
|
591
646
|
}
|
|
592
|
-
/**
|
|
593
|
-
* Collect known type definition names of a specific kind (for error candidates).
|
|
594
|
-
*/
|
|
595
|
-
function collectTypeDefNames(env, kind) {
|
|
596
|
-
return env.allTypeDefNames(kind);
|
|
597
|
-
}
|
|
598
647
|
//# sourceMappingURL=check.js.map
|