@venn-lang/core 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 (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/index.d.ts +3654 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +13137 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +61 -0
  8. package/src/ast/call-args.ts +15 -0
  9. package/src/ast/dotted-path.ts +22 -0
  10. package/src/ast/index.ts +7 -0
  11. package/src/ast/is-statement.ts +40 -0
  12. package/src/ast/split-call.ts +28 -0
  13. package/src/ast/walk.ts +6 -0
  14. package/src/codes/build-problem.ts +24 -0
  15. package/src/codes/catalog.ts +35 -0
  16. package/src/codes/code.types.ts +7 -0
  17. package/src/codes/index.ts +3 -0
  18. package/src/compile/compile.ts +169 -0
  19. package/src/compile/compile.types.ts +40 -0
  20. package/src/compile/index.ts +3 -0
  21. package/src/compile/lex-scope.ts +78 -0
  22. package/src/compile/nodes/binary.ts +56 -0
  23. package/src/compile/nodes/call.ts +50 -0
  24. package/src/compile/nodes/collection.ts +83 -0
  25. package/src/compile/nodes/const-lit.ts +30 -0
  26. package/src/compile/nodes/fast-binary.ts +71 -0
  27. package/src/compile/nodes/fn.ts +87 -0
  28. package/src/compile/nodes/index.ts +8 -0
  29. package/src/compile/nodes/literal.ts +86 -0
  30. package/src/compile/nodes/map-fields.ts +79 -0
  31. package/src/compile/nodes/member.ts +32 -0
  32. package/src/events/envelope.types.ts +17 -0
  33. package/src/events/event-data.types.ts +25 -0
  34. package/src/events/ids.types.ts +10 -0
  35. package/src/events/index.ts +5 -0
  36. package/src/events/run-plan.types.ts +18 -0
  37. package/src/events/status.types.ts +2 -0
  38. package/src/expand/accepted-kinds.ts +23 -0
  39. package/src/expand/deco/deco-decorator.ts +77 -0
  40. package/src/expand/deco/deco-env.ts +24 -0
  41. package/src/expand/deco/deco.types.ts +43 -0
  42. package/src/expand/deco/document-decos.ts +64 -0
  43. package/src/expand/deco/index.ts +7 -0
  44. package/src/expand/deco/read-signature.ts +57 -0
  45. package/src/expand/deco/run-body.ts +119 -0
  46. package/src/expand/decorate-callable.ts +49 -0
  47. package/src/expand/decorations.ts +41 -0
  48. package/src/expand/expand.ts +108 -0
  49. package/src/expand/expand.types.ts +78 -0
  50. package/src/expand/handles/around-verbs.ts +35 -0
  51. package/src/expand/handles/binding-verbs.ts +27 -0
  52. package/src/expand/handles/common-verbs.ts +40 -0
  53. package/src/expand/handles/fn-verbs.ts +58 -0
  54. package/src/expand/handles/handle.types.ts +33 -0
  55. package/src/expand/handles/index.ts +4 -0
  56. package/src/expand/handles/kind-of.ts +36 -0
  57. package/src/expand/handles/make-handle.ts +100 -0
  58. package/src/expand/handles/missing-verb.ts +27 -0
  59. package/src/expand/handles/runnable-verbs.ts +13 -0
  60. package/src/expand/handles/type-verbs.ts +56 -0
  61. package/src/expand/index.ts +40 -0
  62. package/src/expand/make-context.ts +69 -0
  63. package/src/expand/node-meta.ts +30 -0
  64. package/src/expand/node-span.ts +17 -0
  65. package/src/expand/swap-node.ts +28 -0
  66. package/src/expand/wrong-kind.ts +102 -0
  67. package/src/expr/cell.types.ts +25 -0
  68. package/src/expr/closure.ts +53 -0
  69. package/src/expr/closure.types.ts +31 -0
  70. package/src/expr/eval-env.types.ts +9 -0
  71. package/src/expr/evaluate.ts +18 -0
  72. package/src/expr/frame.ts +60 -0
  73. package/src/expr/index.ts +14 -0
  74. package/src/expr/invoke.ts +140 -0
  75. package/src/expr/member-value.ts +91 -0
  76. package/src/expr/methods/index.ts +65 -0
  77. package/src/expr/methods/list-grouping.ts +107 -0
  78. package/src/expr/methods/list-methods.ts +57 -0
  79. package/src/expr/methods/list-selection.ts +142 -0
  80. package/src/expr/methods/map-extras.ts +87 -0
  81. package/src/expr/methods/map-methods.ts +17 -0
  82. package/src/expr/methods/number-methods.ts +29 -0
  83. package/src/expr/methods/string-extras.ts +66 -0
  84. package/src/expr/methods/string-methods.ts +32 -0
  85. package/src/expr/methods/unit-methods.ts +54 -0
  86. package/src/expr/namespace.ts +20 -0
  87. package/src/expr/native.types.ts +40 -0
  88. package/src/expr/operators.ts +91 -0
  89. package/src/expr/pending.ts +47 -0
  90. package/src/expr/prelude.ts +75 -0
  91. package/src/expr/task.ts +71 -0
  92. package/src/format/format-text.ts +22 -0
  93. package/src/format/format.types.ts +18 -0
  94. package/src/format/from-settings.ts +40 -0
  95. package/src/format/index.ts +5 -0
  96. package/src/format/organize-header.ts +66 -0
  97. package/src/format/reindent.ts +35 -0
  98. package/src/generated/ast.ts +2383 -0
  99. package/src/generated/grammar.ts +5236 -0
  100. package/src/generated/module.ts +25 -0
  101. package/src/grammar/venn.langium +291 -0
  102. package/src/graph/graph.types.ts +20 -0
  103. package/src/graph/index.ts +2 -0
  104. package/src/graph/to-graph.ts +87 -0
  105. package/src/index.ts +93 -0
  106. package/src/interpolation/compile-template.ts +40 -0
  107. package/src/interpolation/index.ts +4 -0
  108. package/src/interpolation/interpolation.types.ts +18 -0
  109. package/src/interpolation/scan-interpolations.ts +47 -0
  110. package/src/interpolation/template.types.ts +19 -0
  111. package/src/lang/default-services.ts +14 -0
  112. package/src/lang/index.ts +3 -0
  113. package/src/lang/venn-lexer.ts +34 -0
  114. package/src/lang/venn-module.ts +51 -0
  115. package/src/module/index.ts +5 -0
  116. package/src/module/specifier.ts +21 -0
  117. package/src/parse/error-to-problem.ts +43 -0
  118. package/src/parse/index.ts +3 -0
  119. package/src/parse/parse-expression.ts +27 -0
  120. package/src/parse/parse-output.types.ts +8 -0
  121. package/src/parse/parse.ts +26 -0
  122. package/src/problem/build-diff.ts +116 -0
  123. package/src/problem/diff.types.ts +20 -0
  124. package/src/problem/format-value.ts +40 -0
  125. package/src/problem/index.ts +8 -0
  126. package/src/problem/problem-error.ts +15 -0
  127. package/src/problem/problem.types.ts +21 -0
  128. package/src/problem/related.types.ts +7 -0
  129. package/src/problem/severity.types.ts +2 -0
  130. package/src/problem/span.types.ts +8 -0
  131. package/src/typecheck/action-signature.ts +53 -0
  132. package/src/typecheck/builtin-types.ts +63 -0
  133. package/src/typecheck/builtins.ts +267 -0
  134. package/src/typecheck/catalog.types.ts +16 -0
  135. package/src/typecheck/check-deco.ts +174 -0
  136. package/src/typecheck/check-stmts.ts +151 -0
  137. package/src/typecheck/check-types.ts +232 -0
  138. package/src/typecheck/context.ts +31 -0
  139. package/src/typecheck/imported-types.ts +127 -0
  140. package/src/typecheck/index.ts +26 -0
  141. package/src/typecheck/infer.ts +416 -0
  142. package/src/typecheck/kind-types.ts +79 -0
  143. package/src/typecheck/member-docs.ts +211 -0
  144. package/src/typecheck/named-types.ts +57 -0
  145. package/src/typecheck/prelude-types.ts +152 -0
  146. package/src/typecheck/reshaped-fns.ts +58 -0
  147. package/src/typecheck/scheme.ts +75 -0
  148. package/src/typecheck/seed-params.ts +72 -0
  149. package/src/typecheck/seed-values.ts +64 -0
  150. package/src/typecheck/show.ts +64 -0
  151. package/src/typecheck/solidify.ts +60 -0
  152. package/src/typecheck/spec-to-type.ts +88 -0
  153. package/src/typecheck/type-env.ts +46 -0
  154. package/src/typecheck/type-ref.ts +62 -0
  155. package/src/typecheck/type.types.ts +168 -0
  156. package/src/typecheck/unify.ts +192 -0
  157. package/src/typecheck/unit-types.ts +49 -0
  158. package/src/units/combine.ts +97 -0
  159. package/src/units/index.ts +6 -0
  160. package/src/units/parse-instant.ts +12 -0
  161. package/src/units/parse-number.ts +48 -0
  162. package/src/units/unit-guards.ts +16 -0
  163. package/src/units/unit.types.ts +38 -0
  164. package/src/value/equals.ts +4 -0
  165. package/src/value/index.ts +4 -0
  166. package/src/value/is-numeric.ts +6 -0
  167. package/src/value/truthiness.ts +9 -0
  168. package/src/value/value.types.ts +12 -0
@@ -0,0 +1,232 @@
1
+ import type { AstNode } from "langium";
2
+ import { buildProblem, CODES } from "../codes/index.js";
3
+ import type { ImportedDeco } from "../expand/index.js";
4
+ import type { Declaration, Document, Expr, FnDecl } from "../generated/ast.js";
5
+ import * as ast from "../generated/ast.js";
6
+ import type { Problem, Span } from "../problem/index.js";
7
+ import type { TypeCatalog } from "./catalog.types.js";
8
+ import { checkDecoBody, checkDecos, decosInReach } from "./check-deco.js";
9
+ import { checkBlock, checkFragment, checkStatement } from "./check-stmts.js";
10
+ import { createContext, type TypeContext, type TypeMismatch } from "./context.js";
11
+ import { type Infer, inferFn, type Slot } from "./infer.js";
12
+ import { collectNamedTypes } from "./named-types.js";
13
+ import { PRELUDE_SPECS } from "./prelude-types.js";
14
+ import { reshapedFns } from "./reshaped-fns.js";
15
+ import { generalize, mono } from "./scheme.js";
16
+ import { seedParams } from "./seed-params.js";
17
+ import { seedValues } from "./seed-values.js";
18
+ import { showType } from "./show.js";
19
+ import { DYNAMIC, type Type } from "./type.types.js";
20
+ import { emptyEnv, type TypeEnv } from "./type-env.js";
21
+ import { prune, unify } from "./unify.js";
22
+
23
+ /** What one check of a document produced. */
24
+ export interface CheckTypesResult {
25
+ problems: Problem[];
26
+ /** Every expression's inferred type, keyed by node, for hover. */
27
+ types: Map<object, Type>;
28
+ /** Per string literal, the expression parsed from each of its ${…} slots. */
29
+ slots: Map<object, (Expr | undefined)[]>;
30
+ }
31
+
32
+ /** What the checker may be told about the world outside the file. */
33
+ export interface CheckTypesOptions {
34
+ uri?: string;
35
+ /** The types and signatures the loaded plugins contribute. */
36
+ catalog?: TypeCatalog;
37
+ /** The `pub deco`s this file's imports reach. Without them an imported
38
+ * `@name` is a name the checker knows nothing about, and says nothing about. */
39
+ decos?: ReadonlyMap<string, ImportedDeco>;
40
+ /** What the names this file imports turned out to be, from the files it names. */
41
+ imports?: ReadonlyMap<string, Type>;
42
+ }
43
+
44
+ /**
45
+ * Infer and check the types across a document.
46
+ *
47
+ * Errors are raised only where a type is actually known. Anything touching
48
+ * `dynamic`, such as a plugin that published no signature or an HTTP response,
49
+ * is left alone, so the checker helps without ever blocking.
50
+ *
51
+ * @returns the problems found, plus the inferred type of every expression and
52
+ * the expressions parsed out of each `${…}`, both of which the editor reads.
53
+ */
54
+ export function checkTypes(document: Document, options: CheckTypesOptions = {}): CheckTypesResult {
55
+ const uri = options.uri ?? "memory://inline.vn";
56
+ const ctx = createContext();
57
+ // Shared with the seeding pass: parsing every `${…}` twice is the one part of
58
+ // a second walk that would actually cost something.
59
+ const parsed = new Map<AstNode, Slot[]>();
60
+ const decos = decosInReach({ document, imported: options.decos });
61
+ const shared = { document, catalog: options.catalog, decos, parsed, run: pass };
62
+ const values = seedValues(shared);
63
+ const seeds = seedParams(shared);
64
+ const infer: Infer = {
65
+ ctx,
66
+ named: collectNamedTypes(document, ctx, options.catalog),
67
+ catalog: options.catalog,
68
+ decos,
69
+ seeds,
70
+ values,
71
+ parsed,
72
+ imports: options.imports,
73
+ types: new Map(),
74
+ slots: new Map(),
75
+ };
76
+ pass(document, infer);
77
+ // After the pass, so a decorator's arguments are read once, and before the
78
+ // mismatches are turned into problems, since that is where they land.
79
+ const deco = checkDecos({ document, infer, uri });
80
+ return {
81
+ problems: [...ctx.mismatches.map((m) => problem(m, uri)), ...deco],
82
+ types: infer.types ?? new Map(),
83
+ slots: infer.slots ?? new Map(),
84
+ };
85
+ }
86
+
87
+ /** One walk of the whole document: top-level bindings, then everything that runs. */
88
+ function pass(document: Document, infer: Infer): void {
89
+ walk(document, topLevelEnv(document, infer), infer);
90
+ }
91
+
92
+ /**
93
+ * Hoist functions and bind top-level values, so the whole file sees them.
94
+ *
95
+ * Generalising is what makes a helper reusable at more than one type, and also
96
+ * what stops a call site from ever reaching the declaration, since each use gets
97
+ * its own copy. The seeding pass therefore skips it: that pass exists precisely
98
+ * to let the callers speak.
99
+ */
100
+ function topLevelEnv(document: Document, infer: Infer): TypeEnv {
101
+ const fns = document.decls.filter(ast.isFnDecl);
102
+ // A decorator may hand this function a shape nobody here can read; its body is
103
+ // still checked, but its signature is not the one callers will meet.
104
+ const reshaped = reshapedFns({ document, decos: infer.decos ?? new Map() });
105
+ const outer = withImports(preludeEnv(), infer);
106
+ let env = withSeededValues(hoist({ fns, reshaped, ctx: infer.ctx, env: outer }), infer);
107
+ for (const decl of fns) {
108
+ const inferred = inferFn(decl, env, infer);
109
+ if (!reshaped.has(decl)) unify(env.lookup(decl.name)?.type ?? placeholder(infer), inferred);
110
+ }
111
+ if (!infer.seeding) env = generalizeFns(fns, env, infer);
112
+ return bindValues(document, env, infer);
113
+ }
114
+
115
+ /**
116
+ * What the file's values hold, put in reach of the function bodies about to be
117
+ * checked. The real binding still happens below, in source order and with the
118
+ * annotations read; this only stops a body from meeting a name it cannot know.
119
+ */
120
+ function withSeededValues(env: TypeEnv, infer: Infer): TypeEnv {
121
+ let next = env;
122
+ for (const [name, type] of infer.values ?? []) next = next.with(name, mono(type));
123
+ return next;
124
+ }
125
+
126
+ /**
127
+ * The names this file imported, each with the type its own module gave it.
128
+ *
129
+ * Generalised over everything free in them, for two reasons: a generic helper
130
+ * stays generic across the file boundary, and the variables belonging to the
131
+ * pass that inferred them are replaced with fresh ones here rather than being
132
+ * unified with anything in this file.
133
+ */
134
+ function withImports(env: TypeEnv, infer: Infer): TypeEnv {
135
+ let next = env;
136
+ for (const [name, type] of infer.imports ?? []) next = next.with(name, generalize(type, EMPTY));
137
+ return next;
138
+ }
139
+
140
+ const EMPTY: ReadonlySet<number> = new Set();
141
+
142
+ /** The prelude is in scope everywhere, so inference knows what its verbs return. */
143
+ function preludeEnv(): TypeEnv {
144
+ let env = emptyEnv();
145
+ for (const [name, spec] of Object.entries(PRELUDE_SPECS)) env = env.with(name, mono(spec.type));
146
+ return env;
147
+ }
148
+
149
+ function hoist(args: {
150
+ fns: readonly FnDecl[];
151
+ reshaped: ReadonlySet<FnDecl>;
152
+ ctx: TypeContext;
153
+ env: TypeEnv;
154
+ }): TypeEnv {
155
+ let next = args.env;
156
+ for (const decl of args.fns) {
157
+ next = next.with(decl.name, mono(args.reshaped.has(decl) ? DYNAMIC : args.ctx.fresh()));
158
+ }
159
+ return next;
160
+ }
161
+
162
+ function generalizeFns(fns: readonly FnDecl[], env: TypeEnv, infer: Infer): TypeEnv {
163
+ let next = env;
164
+ for (const decl of fns) {
165
+ const type = env.lookup(decl.name)?.type;
166
+ if (!type) continue;
167
+ // The declaration carries its own type, so hovering the name shows it.
168
+ infer.types?.set(decl, prune(type));
169
+ next = next.with(decl.name, generalize(prune(type), new Set()));
170
+ }
171
+ return next;
172
+ }
173
+
174
+ function bindValues(document: Document, env: TypeEnv, infer: Infer): TypeEnv {
175
+ let next = env;
176
+ for (const decl of document.decls) {
177
+ if (ast.isLetStmt(decl)) next = checkStatement(decl, next, infer);
178
+ }
179
+ return next;
180
+ }
181
+
182
+ /** Check the executable parts: flows, fragments, and top-level statements. */
183
+ function walk(document: Document, env: TypeEnv, infer: Infer): void {
184
+ for (const decl of document.decls) checkDeclaration(decl, env, infer);
185
+ }
186
+
187
+ function checkDeclaration(decl: Declaration, env: TypeEnv, infer: Infer): void {
188
+ if (ast.isFlowDecl(decl)) checkBlock(decl.body, env, infer);
189
+ else if (ast.isFragmentDecl(decl)) checkFragment(decl, env, infer);
190
+ else if (ast.isDecoDecl(decl)) checkDecoBody(decl, env, infer);
191
+ else if (!ast.isFnDecl(decl) && !ast.isLetStmt(decl) && isExecutable(decl)) {
192
+ checkStatement(decl as never, env, infer);
193
+ }
194
+ }
195
+
196
+ function isExecutable(decl: Declaration): boolean {
197
+ return !(ast.isTypeDecl(decl) || ast.isConfigDecl(decl) || ast.isMatrixDecl(decl));
198
+ }
199
+
200
+ function placeholder(infer: Infer): Type {
201
+ return infer.ctx.fresh();
202
+ }
203
+
204
+ function problem(mismatch: TypeMismatch, uri: string): Problem {
205
+ const title = titleOf(mismatch);
206
+ return buildProblem({
207
+ spec: mismatch.unit ? CODES.VN3012_UNIT_MISMATCH : CODES.VN3010_TYPE_MISMATCH,
208
+ span: spanOf(mismatch.node, uri),
209
+ title,
210
+ });
211
+ }
212
+
213
+ /** A unit clash already reads as a sentence; anything else is a type mismatch. */
214
+ function titleOf(mismatch: TypeMismatch): string {
215
+ if (mismatch.unit) return mismatch.note ?? "These values cannot be combined.";
216
+ if (mismatch.note) return `Type ${showType(mismatch.expected)} ${mismatch.note}.`;
217
+ return `Type mismatch: expected ${showType(mismatch.expected)}, found ${showType(mismatch.actual)}.`;
218
+ }
219
+
220
+ function spanOf(node: { $cstNode?: unknown }, uri: string): Span {
221
+ const cst = node.$cstNode as
222
+ | { offset: number; length: number; range?: { start: { line: number; character: number } } }
223
+ | undefined;
224
+ const start = cst?.range?.start;
225
+ return {
226
+ uri,
227
+ offset: cst?.offset ?? 0,
228
+ length: cst?.length ?? 0,
229
+ line: (start?.line ?? 0) + 1,
230
+ column: (start?.character ?? 0) + 1,
231
+ };
232
+ }
@@ -0,0 +1,31 @@
1
+ import type { AstNode } from "langium";
2
+ import type { Type, TypeVar } from "./type.types.js";
3
+
4
+ /**
5
+ * Per-run inference state: a fresh-variable source and the list of type
6
+ * mismatches found. Held in a context (not a module global) so two checks never
7
+ * share variable ids or leak diagnostics into each other.
8
+ */
9
+ export interface TypeContext {
10
+ fresh(): TypeVar;
11
+ mismatches: TypeMismatch[];
12
+ }
13
+
14
+ /** One place where two types could not be made equal, at a source node. */
15
+ export interface TypeMismatch {
16
+ node: AstNode;
17
+ expected: Type;
18
+ actual: Type;
19
+ note?: string;
20
+ /** A unit clash rather than a plain type clash: VN3012 instead of VN3010. */
21
+ unit?: boolean;
22
+ }
23
+
24
+ /** A fresh inference context: variable ids from zero, no mismatches recorded. */
25
+ export function createContext(): TypeContext {
26
+ let next = 0;
27
+ return {
28
+ fresh: () => ({ kind: "var", id: next++, ref: undefined }),
29
+ mismatches: [],
30
+ };
31
+ }
@@ -0,0 +1,127 @@
1
+ import type { TypeSpec } from "@venn-lang/types";
2
+ import type { Document } from "../generated/ast.js";
3
+ import * as ast from "../generated/ast.js";
4
+ import { isPackageSpecifier } from "../module/index.js";
5
+ import type { TypeCatalog } from "./catalog.types.js";
6
+ import { checkTypes } from "./check-types.js";
7
+ import { type ResolveRef, specToType } from "./spec-to-type.js";
8
+ import { record, type Type } from "./type.types.js";
9
+
10
+ /** What the names a file imported turned out to be, ready to bind in its env. */
11
+ export type ImportedTypes = ReadonlyMap<string, Type>;
12
+
13
+ export interface ImportedTypesArgs {
14
+ document: Document;
15
+ uri: string;
16
+ /** Every module the import graph reached, already parsed, by resolved URI. */
17
+ modules: ReadonlyMap<string, Document>;
18
+ /** How a specifier written in one file names another. */
19
+ resolve: (from: string, spec: string) => string;
20
+ catalog?: TypeCatalog;
21
+ /**
22
+ * What each installed package publishes, derived from its `.d.ts`.
23
+ *
24
+ * Keyed by the specifier as written, because that is what the import says.
25
+ * Absent for a host that has not derived any, which is every run in an editor
26
+ * that has not installed anything yet.
27
+ */
28
+ packages?: ReadonlyMap<string, Record<string, TypeSpec>>;
29
+ }
30
+
31
+ /**
32
+ * The types of the names a document imports, worked out from the files it names.
33
+ *
34
+ * Each module is checked on its own, with *its* imports resolved first, so a
35
+ * `pub fn` that calls another file has the signature it really has. That is what
36
+ * lets `triplo("texto")` be refused against a `fn(number) -> number` declared
37
+ * one file away.
38
+ *
39
+ * @returns a type per imported name. A name whose module publishes nothing for
40
+ * it is simply absent, and the checker treats it as `dynamic`.
41
+ */
42
+ export function importedTypes(args: ImportedTypesArgs): ImportedTypes {
43
+ const state: State = {
44
+ modules: args.modules,
45
+ resolve: args.resolve,
46
+ catalog: args.catalog,
47
+ packages: args.packages,
48
+ done: new Map(),
49
+ busy: new Set([args.uri]),
50
+ };
51
+ return bindingsOf({ document: args.document, uri: args.uri, state });
52
+ }
53
+
54
+ interface State {
55
+ modules: ReadonlyMap<string, Document>;
56
+ resolve: (from: string, spec: string) => string;
57
+ catalog?: TypeCatalog;
58
+ packages?: ReadonlyMap<string, Record<string, TypeSpec>>;
59
+ done: Map<string, Map<string, Type>>;
60
+ busy: Set<string>;
61
+ }
62
+
63
+ /** What each name a document imports binds to, following `import { … }` and `* as`. */
64
+ function bindingsOf(args: { document: Document; uri: string; state: State }): Map<string, Type> {
65
+ const out = new Map<string, Type>();
66
+ for (const decl of args.document.imports) {
67
+ if (!ast.isValueImport(decl)) continue;
68
+ const published = isPackageSpecifier(decl.path)
69
+ ? packageTypes(decl.path, args.state)
70
+ : publishedBy(args.state.resolve(args.uri, decl.path), args.state);
71
+ if (decl.wildcard) out.set(decl.wildcard, record(published));
72
+ else for (const name of decl.names) take(out, published, name);
73
+ }
74
+ return out;
75
+ }
76
+
77
+ function take(out: Map<string, Type>, published: ReadonlyMap<string, Type>, name: string): void {
78
+ const found = published.get(name);
79
+ if (found) out.set(name, found);
80
+ }
81
+
82
+ /**
83
+ * What an installed package publishes, as this language's types.
84
+ *
85
+ * Derived elsewhere, by whoever can read a `.d.ts`, and handed over already
86
+ * converted, so the checker stays a checker and never learns what npm is.
87
+ */
88
+ function packageTypes(spec: string, state: State): ReadonlyMap<string, Type> {
89
+ const found = state.packages?.get(spec);
90
+ if (!found) return new Map();
91
+ // A package's types name nothing of this language's, so a reference in one is
92
+ // a name from a `.d.ts` that did not survive the conversion: `dynamic`.
93
+ const unknown: ResolveRef = () => undefined;
94
+ return new Map(Object.entries(found).map(([name, one]) => [name, specToType(one, unknown)]));
95
+ }
96
+
97
+ /**
98
+ * The types a module publishes, checked once.
99
+ *
100
+ * A module already being checked answers with nothing rather than looping: two
101
+ * files that call each other cannot both be inferred from the other, and the
102
+ * honest answer for the second is that its signature is not yet known. Whatever
103
+ * it declared outright is still read on its own next time round.
104
+ */
105
+ function publishedBy(uri: string, state: State): ReadonlyMap<string, Type> {
106
+ const cached = state.done.get(uri);
107
+ if (cached) return cached;
108
+ const module = state.modules.get(uri);
109
+ if (!module || state.busy.has(uri)) return new Map();
110
+ state.busy.add(uri);
111
+ const out = exportedTypes(module, uri, state);
112
+ state.busy.delete(uri);
113
+ state.done.set(uri, out);
114
+ return out;
115
+ }
116
+
117
+ function exportedTypes(module: Document, uri: string, state: State): Map<string, Type> {
118
+ const imports = bindingsOf({ document: module, uri, state });
119
+ const checked = checkTypes(module, { uri, catalog: state.catalog, imports });
120
+ const out = new Map<string, Type>();
121
+ for (const decl of module.decls) {
122
+ if (!ast.isFnDecl(decl) || !decl.export) continue;
123
+ const found = checked.types.get(decl);
124
+ if (found) out.set(decl.name, found);
125
+ }
126
+ return out;
127
+ }
@@ -0,0 +1,26 @@
1
+ export type { BuiltinType } from "./builtin-types.js";
2
+ export { BUILTIN_TYPES, isBuiltinType } from "./builtin-types.js";
3
+ export { memberType, resolveMember } from "./builtins.js";
4
+ export type { TypeCatalog } from "./catalog.types.js";
5
+ export type { CheckTypesOptions, CheckTypesResult } from "./check-types.js";
6
+ export { checkTypes } from "./check-types.js";
7
+ export { createContext } from "./context.js";
8
+ export { type ImportedTypes, importedTypes } from "./imported-types.js";
9
+ export { KIND_SPECS, KIND_TYPES } from "./kind-types.js";
10
+ export type { MemberDoc } from "./member-docs.js";
11
+ export { MEMBER_DOCS, memberKind } from "./member-docs.js";
12
+ export type { PreludeArg, PreludeSpec } from "./prelude-types.js";
13
+ export { isPrelude, PRELUDE_SPECS } from "./prelude-types.js";
14
+ export { showType, showTypes } from "./show.js";
15
+ export type { ResolveRef } from "./spec-to-type.js";
16
+ export { specToType } from "./spec-to-type.js";
17
+ export type {
18
+ FnType,
19
+ LiteralType,
20
+ OpaqueType,
21
+ RecordType,
22
+ Type,
23
+ UnionType,
24
+ } from "./type.types.js";
25
+ export { DYNAMIC, literal, opaque, union } from "./type.types.js";
26
+ export { prune } from "./unify.js";