@formaui-net/nexo 0.0.0 → 1.0.0-preview.1

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 (49) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +148 -6
  4. package/package.json +23 -3
  5. package/src/common/ArrayUtils.mjs +16 -0
  6. package/src/common/ObjectUtils.mjs +33 -0
  7. package/src/language/DefinitionLocations.mjs +41 -0
  8. package/src/language/DefinitionValidator.mjs +1265 -0
  9. package/src/language/DefinitionWriter.mjs +475 -0
  10. package/src/language/Dictionary.mjs +52 -0
  11. package/src/language/Evaluator.mjs +3480 -0
  12. package/src/language/ImportedVocabulary.mjs +137 -0
  13. package/src/language/Imports.mjs +141 -0
  14. package/src/language/Index.mjs +29 -0
  15. package/src/language/LanguageResolver.mjs +4068 -0
  16. package/src/language/Limits.mjs +78 -0
  17. package/src/language/LineStructure.mjs +385 -0
  18. package/src/language/Lowering.mjs +453 -0
  19. package/src/language/MemberSelector.mjs +89 -0
  20. package/src/language/Names.mjs +65 -0
  21. package/src/language/NexoError.mjs +145 -0
  22. package/src/language/Pair.mjs +21 -0
  23. package/src/language/ParameterBindings.mjs +361 -0
  24. package/src/language/ParsedTexts.mjs +62 -0
  25. package/src/language/Parser.mjs +1817 -0
  26. package/src/language/Range.mjs +51 -0
  27. package/src/language/RecentlyUsed.mjs +50 -0
  28. package/src/language/ResolvedDefinition.mjs +219 -0
  29. package/src/language/ResultPath.mjs +101 -0
  30. package/src/language/ScopeLocals.mjs +102 -0
  31. package/src/language/StructureTypes.mjs +48 -0
  32. package/src/language/SyntaxTree.mjs +203 -0
  33. package/src/language/TextLocations.mjs +185 -0
  34. package/src/language/TextPositions.mjs +23 -0
  35. package/src/language/Tokenizer.mjs +917 -0
  36. package/src/language/Type.mjs +401 -0
  37. package/src/language/TypeChecker.mjs +449 -0
  38. package/src/language/ValueCapture.mjs +315 -0
  39. package/src/language/Versions.mjs +14 -0
  40. package/src/language/vocabulary/Declarations.mjs +64 -0
  41. package/src/language/vocabulary/ItemPositions.mjs +48 -0
  42. package/src/language/vocabulary/ListOrdering.mjs +145 -0
  43. package/src/language/vocabulary/MemberFailure.mjs +21 -0
  44. package/src/language/vocabulary/NexoModule.mjs +1119 -0
  45. package/src/language/vocabulary/NexoRegistry.mjs +1540 -0
  46. package/src/language/vocabulary/TypeNameScope.mjs +63 -0
  47. package/src/language/vocabulary/Vocabulary.mjs +640 -0
  48. package/src/nexo.d.ts +743 -0
  49. package/src/nexo.js +338 -0
@@ -0,0 +1,4068 @@
1
+ /**
2
+ * LanguageResolver — resolves a Nexo definition against the vocabulary and validates its types, before anything is evaluated
3
+ */
4
+
5
+ import { DefinitionValidator } from './DefinitionValidator.mjs';
6
+ import { DefinitionWriter } from './DefinitionWriter.mjs';
7
+ import { ImportedVocabulary } from './ImportedVocabulary.mjs';
8
+ import { Imports } from './Imports.mjs';
9
+ import { NexoError, NexoErrors } from './NexoError.mjs';
10
+ import { Names } from './Names.mjs';
11
+ import { ResolvedDefinition } from './ResolvedDefinition.mjs';
12
+ import { ScopeLocals } from './ScopeLocals.mjs';
13
+ import { Type } from './Type.mjs';
14
+ import { NexoModule } from './vocabulary/NexoModule.mjs';
15
+ import { Vocabulary } from './vocabulary/Vocabulary.mjs';
16
+
17
+ /** How each form of a declaration is written, and the node that writes it. */
18
+ const FORM_NODE_KINDS = { property: 'memberAccess', function: 'call', indexer: 'indexerAccess' };
19
+
20
+ /** No facts, the set a scope starts with: what narrows a stable reference on a path. */
21
+ const NO_FACTS = new Map();
22
+
23
+ export class LanguageResolver {
24
+ #registry;
25
+ #typeChecker;
26
+ #bindings;
27
+ #locations;
28
+
29
+ /** The local names the definition's imports give modules, each beginning its module's namespace, which a message's text writes as the script does. @type {ReadonlySet<string>} */
30
+ #namespaceNames = Names.noNamespaceNames;
31
+
32
+ /** What the definition's imports make available: its namespaces, the types it names, and the declarations it may select, Nexo's own among them. @type {ImportedVocabulary} */
33
+ #imported;
34
+
35
+ /** @type {Map<string, object>} */
36
+ #resolutions = new Map();
37
+
38
+ /**
39
+ * The pointer each node of the definition stands at, recorded as resolution reaches the node there, a call's copy of a function's body among them, so the resolved definition finds a node's resolution by the pointer
40
+ * it is stored at rather than walking the definition again to build each pointer anew, which measured a sixth of resolving. A node is reached at the one pointer it stands at, however many readings resolve it; a node
41
+ * made only to be resolved in another's place, and a body copy a refused reading made, is recorded and never asked for, and is released with the node, which nothing else holds.
42
+ * @type {WeakMap<object, string>}
43
+ */
44
+ #pointers = new WeakMap();
45
+
46
+ /** @type {object[]} */
47
+ #usedDeclarations = [];
48
+
49
+ /** The optional chains being resolved, the innermost last, each with whether an optional access of it can meet a null receiver. @type {{ shortCircuits: boolean }[]} */
50
+ #optionalChains = [];
51
+
52
+ /** The selection of a declaration among those of a name for a receiver, the one every access, indexer and qualified call reads. */
53
+ #memberSelector;
54
+
55
+ /**
56
+ * @param {import('./vocabulary/NexoRegistry.mjs').NexoRegistry} registry The vocabulary to resolve against.
57
+ * @param {import('./ParameterBindings.mjs').ParameterBindings} bindings What the request's parameters are bound to.
58
+ * @param {import('./TextLocations.mjs').TextLocations | null} locations Where in a text each part of the definition came from.
59
+ */
60
+ constructor(registry, bindings, locations) {
61
+ this.#registry = registry;
62
+ this.#typeChecker = registry.typeChecker;
63
+ this.#memberSelector = registry.memberSelector;
64
+ this.#bindings = bindings;
65
+ this.#locations = locations;
66
+ this.#imported = new ImportedVocabulary(registry, []);
67
+ }
68
+
69
+ /** Resolves and types the definition, answering what it resolved to or throwing where it cannot. */
70
+ resolveDefinition(definition) {
71
+ this.#resolveImports(definition.imports);
72
+
73
+ const result = this.#resolveTerminal(definition.expression, '/expression', { contextType: null, contextKey: {}, locals: ScopeLocals.empty, facts: NO_FACTS, objectRoot: null });
74
+
75
+ // A host call that supplies nothing gives no result, and one that supplies values gives, for each, what its result projection makes of it, or the value itself.
76
+ const type = result.suppliesNothing === true ? null : result.type;
77
+
78
+ if (type !== null && LanguageResolver.#heldReferenceOf(type) === 'function') {
79
+ this.#fail('NEXO3035', 'The result would hold a function, which is used within the expression and never part of its result: call it, as name(…)', '/expression');
80
+ }
81
+
82
+ // A value of Any in it is data or not as it is evaluated, which checks it then.
83
+ if (type !== null && !this.#registry.isDataOnceChecked(type)) {
84
+ this.#fail('NEXO3043', `The result would hold ${type}, which is no data; read the facts it needs through an object expression`, '/expression');
85
+ }
86
+
87
+ const hostCall = result.kind === 'hostCall' ? this.#hostCallOf(definition.expression, '/expression') : null;
88
+ const deferredAccesses = this.#deferredAccessesOf();
89
+
90
+ // Evaluation recognizes a value of Any, which a declaration it uses gives the script, and a supplied value where its type holds a nullable reference, so a resolution of neither, in any branch, recognizes nothing.
91
+ const suppliedType = hostCall === null || result.suppliesNothing === true ? null : (this.#resolutions.get(hostCall.pointer)?.type ?? Type.any);
92
+ const recognizesSupplied = suppliedType !== null && (suppliedType.kind === 'any' || this.#registry.holdsNullableReference(suppliedType));
93
+ const mayRecognize = recognizesSupplied || deferredAccesses.length > 0 || this.#usedDeclarations.some(({ declaration }) => this.#registry.givesAny(declaration));
94
+
95
+ return new ResolvedDefinition(definition, type, hostCall, this.#resolutions, this.#pointers, this.#usedDeclarations, deferredAccesses, this.#functionBodyCopies, mayRecognize);
96
+ }
97
+
98
+ /**
99
+ * Each deferred access the resolutions hold, by its pointer, with the name it accesses and every declaration its selection could choose, each named as a message names a use, qualified by the type it is declared for,
100
+ * since no receiver's type is known to qualify it by.
101
+ */
102
+ #deferredAccessesOf() {
103
+ const deferred = [...this.#resolutions].filter(([, resolution]) => resolution.kind === 'deferred');
104
+
105
+ return deferred.map(([pointer, { name, candidates }]) => ({ name, pointer, candidates: candidates.map(declaration => ({ declaration, qualifiedName: LanguageResolver.#formatQualifiedName(declaration, this.#registry.getReceiverTypes(declaration)[0]), pointer })) }));
106
+ }
107
+
108
+ /**
109
+ * Resolves the definition's imports against the registry: each names a module it holds (NEXO3002), and a type import a public type or extension its module declares (NEXO3052, NEXO3053); what they make available
110
+ * is then the vocabulary the script resolves against.
111
+ */
112
+ #resolveImports(imports) {
113
+ imports.forEach((entry, index) => {
114
+ const pointer = `/imports/${index}`;
115
+
116
+ if (this.#registry.getModule(entry.module) === undefined) {
117
+ this.#fail('NEXO3002', `'${Imports.quote(entry)}' imports the module '${entry.module}', which the registry does not hold`, pointer);
118
+ }
119
+
120
+ if (Imports.isModuleImport(entry)) {
121
+ return;
122
+ }
123
+
124
+ const declaration = this.#registry.getModuleType(entry.module, entry.type);
125
+
126
+ if (declaration === undefined) {
127
+ this.#fail('NEXO3052', `'${Imports.quote(entry)}' imports '${entry.module}.${entry.type}', which its module does not declare`, pointer);
128
+ }
129
+
130
+ if (this.#registry.isInternal(declaration)) {
131
+ this.#fail('NEXO3053', `'${Imports.quote(entry)}' imports '${entry.module}.${entry.type}', which is internal to its module`, pointer);
132
+ }
133
+ });
134
+ this.#imported = new ImportedVocabulary(this.#registry, imports);
135
+ this.#namespaceNames = this.#imported.namespaceNames;
136
+ }
137
+
138
+ /** The receiver members of the name `.` reads that the script's imports make available, Nexo's own among them, extension members, which `:` reads, left out. */
139
+ #receiverMembersNamed(name) {
140
+ return this.#imported.getAvailable(this.#membersReadBy({ name }));
141
+ }
142
+
143
+ /** The receiver members of the access's name it reads, whatever the script imports: extension members for an extension access, and every other member for any access else. */
144
+ #membersReadBy(access) {
145
+ const isExtensionAccess = access.extension !== undefined;
146
+
147
+ return this.#registry.getReceiverMembers(access.name).filter(declaration => this.#memberSelector.isExtensionMember(declaration) === isExtensionAccess);
148
+ }
149
+
150
+ /**
151
+ * Refuses an access none of whose declarations the script's imports make available applies to its receiver, where one they leave out does (NEXO3005), naming the modules that declare those and giving the import to
152
+ * write; on a receiver of unknown type, null, any declaration counts. An internal declaration is none, as it is no candidate.
153
+ */
154
+ #refuseUnimported(declared, receiverType, subject, pointer) {
155
+ const unimported = this.#memberSelector.getAccessible(declared).filter(declaration => !this.#imported.makesAvailable(declaration) && (receiverType === null || this.#typeChecker.resolveApplyingReceiverType(declaration, receiverType) !== undefined));
156
+
157
+ if (unimported.length === 0) {
158
+ return;
159
+ }
160
+
161
+ const modules = [...new Set(unimported.map(declaration => this.#registry.getDeclaringModuleName(declaration)))];
162
+
163
+ this.#fail('NEXO3005', `${subject} is declared only by ${modules.map(module => `'${module}'`).join(' and ')}, which the script does not import: write ${modules.map(module => `'import ${module}'`).join(' or ')} at the script's start`, pointer);
164
+ }
165
+
166
+ /** Resolves the node in the script's terminal position, where a host call may stand: the script's expression, the result a body there returns and the body a `do` there holds. */
167
+ #resolveTerminal(node, pointer, scope) {
168
+ this.#pointers.set(node, pointer);
169
+
170
+ switch (node.kind) {
171
+ case 'body':
172
+ return this.#resolveBody(node, pointer, scope, true);
173
+ case 'do':
174
+ return this.#resolveTerminal(node.body, `${pointer}/body`, scope);
175
+ default:
176
+ return this.#resolveNode(node, pointer, scope);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * The host call the script ends in, found along its terminal position through bodies, `do` blocks, result projections and its optional chain: its node, where it stands, its name, its declaration, null on a receiver of
182
+ * unknown type, and its candidates, the declaration alone or, on a receiver of unknown type, every one that takes the call as it is written.
183
+ */
184
+ #hostCallOf(node, pointer) {
185
+ switch (node.kind) {
186
+ case 'body':
187
+ return this.#hostCallOf(node.result, `${pointer}/result`);
188
+ case 'do':
189
+ return this.#hostCallOf(node.body, `${pointer}/body`);
190
+ case 'object':
191
+ return this.#hostCallOf(node.context, `${pointer}/context`);
192
+ case 'optionalChain':
193
+ return this.#hostCallOf(node.chain, `${pointer}/chain`);
194
+ default: {
195
+ const { declaration = null, candidates } = this.#resolutions.get(pointer);
196
+
197
+ return { node, pointer, name: LanguageResolver.#accessOf(node).name, declaration, candidates: Object.freeze(declaration === null ? [...candidates] : [declaration]) };
198
+ }
199
+ }
200
+ }
201
+
202
+ #resolveNode(node, pointer, scope) {
203
+ this.#pointers.set(node, pointer);
204
+
205
+ switch (node.kind) {
206
+ case 'literal':
207
+ return this.#resolveLiteral(node);
208
+ case 'scriptParameter':
209
+ return this.#resolveParameter(node, pointer, scope);
210
+ case 'root':
211
+ return this.#resolveObjectRoot(pointer, scope);
212
+ case 'it':
213
+ if (scope.contextType === null) {
214
+ this.#fail('NEXO3010', "'it' is the implicit parameter, and there is none here", pointer);
215
+ }
216
+
217
+ // An `it` of an argument body's own scope, its implicit parameter, which the member supplies, is recorded on the scope's `argumentBody`, so the argument is read as a body.
218
+ if (scope.argumentBody?.key === scope.contextKey && !scope.argumentBody.isRead) {
219
+ scope.argumentBody.isRead = true;
220
+ this.#readArgumentBodies.push(scope.argumentBody);
221
+ }
222
+
223
+ return { type: scope.facts.get(scope.contextKey) ?? scope.contextType, kind: 'value', isFixed: false };
224
+ case 'local':
225
+ return this.#resolveLocal(node, pointer, scope);
226
+ case 'memberAccess':
227
+ return this.#resolveMemberAccess(node, pointer, scope);
228
+ case 'call':
229
+ // A call of the function a name holds calls the local, and a call of a member the member its callee names.
230
+ return node.callee.kind === 'local' ? this.#resolveFunctionCall(node, pointer, scope) : this.#resolveMemberAccess(node, pointer, scope);
231
+ case 'indexerAccess':
232
+ return this.#resolveIndexerAccess(node, pointer, scope);
233
+ case 'equality':
234
+ return this.#resolveEquality(node, pointer, scope, false);
235
+ case 'arithmetic':
236
+ // A subtraction of a bare name from a property access may be one name written with a hyphen, as `it.client-size` is, which a failure to find the member suggests.
237
+ const access = node.left.kind === 'optionalChain' ? node.left.chain : node.left;
238
+
239
+ if (node.operator === '-' && access.kind === 'memberAccess' && typeof access.name === 'string' && (node.right.kind === 'scriptParameter' || node.right.kind === 'local')) {
240
+ this.#joinedNames.set(access, `${access.name}${node.right.name[0].toUpperCase()}${node.right.name.slice(1)}`);
241
+ }
242
+
243
+ for (const side of ['left', 'right']) {
244
+ this.#requireNumber(this.#resolveNode(node[side], `${pointer}/${side}`, scope), `The ${side} operand of '${node.operator}'`, `${pointer}/${side}`);
245
+ }
246
+
247
+ return { type: Type.createNamedType('Number'), kind: 'value', isFixed: false };
248
+ case 'arithmeticNegation':
249
+ this.#requireNumber(this.#resolveNode(node.operand, `${pointer}/operand`, scope), "What '-' negates", `${pointer}/operand`);
250
+
251
+ return { type: Type.createNamedType('Number'), kind: 'value', isFixed: false };
252
+ case 'fromEndIndex':
253
+ this.#requirePosition(this.#resolveNode(node.offset, `${pointer}/offset`, scope), "An index's offset", `${pointer}/offset`, false);
254
+
255
+ return { type: Type.createNamedType('Index'), kind: 'value', isFixed: false };
256
+ case 'range':
257
+ for (const boundary of ['start', 'end']) {
258
+ if (node[boundary] !== undefined) {
259
+ this.#requirePosition(this.#resolveNode(node[boundary], `${pointer}/${boundary}`, scope), `A range's ${boundary}`, `${pointer}/${boundary}`, true);
260
+ }
261
+ }
262
+
263
+ return { type: Type.createNamedType('Range'), kind: 'value', isFixed: false };
264
+ case 'list':
265
+ return { type: Type.createListType(this.#resolveCollectionItems(node.items.map((item, index) => ({ node: item, pointer: `${pointer}/items/${index}` })), scope, pointer, "A list's items", this.#expectedTypes.get(node)?.itemType)), kind: 'value', isFixed: false };
266
+ case 'dictionary': {
267
+ const values = node.entries.map((entry, index) => ({ node: entry.value, pointer: `${pointer}/entries/${index}/value` }));
268
+
269
+ return { type: Type.createDictionaryType(Type.createNamedType('String'), this.#resolveCollectionItems(values, scope, pointer, "A dictionary's values")), kind: 'value', isFixed: false };
270
+ }
271
+ case 'interpolatedString':
272
+ node.parts.forEach((part, index) => {
273
+ if (part.kind === 'expression') {
274
+ this.#requireInterpolationValue(this.#resolveNode(part.expression, `${pointer}/parts/${index}/expression`, scope), `${pointer}/parts/${index}/expression`);
275
+ }
276
+ });
277
+
278
+ return { type: Type.createNamedType('String'), kind: 'value', isFixed: false };
279
+ case 'comparison':
280
+ // Each operand is named by the operator it stands after, the first by the one after it.
281
+ node.operands.forEach((operand, index) => {
282
+ const what = index === 0 ? `The left operand of '${node.operators[0]}'` : `The right operand of '${node.operators[index - 1]}'`;
283
+
284
+ this.#requireNumber(this.#resolveNode(operand, `${pointer}/operands/${index}`, scope), what, `${pointer}/operands/${index}`, 'a comparison');
285
+ });
286
+
287
+ return { type: Type.createNamedType('Boolean'), kind: 'value', isFixed: false };
288
+ case 'conditional':
289
+ return this.#resolveConditional(node, pointer, scope);
290
+ case 'body':
291
+ return this.#resolveBody(node, pointer, scope);
292
+ case 'do':
293
+ return this.#resolveNode(node.body, `${pointer}/body`, scope);
294
+ case 'function':
295
+ // An arrow function written as an argument is a function reference of its own, which captures the scope it is written in.
296
+ return { type: Type.createFunctionType([LanguageResolver.#functionLocalOf(node, pointer, scope)]), kind: 'value', isFixed: false };
297
+ case 'optionalChain':
298
+ return this.#resolveOptionalChain(node, pointer, scope);
299
+ case 'coalescing':
300
+ return this.#resolveCoalescing(node, pointer, scope);
301
+ case 'not':
302
+ return this.#resolveNot(node, pointer, scope);
303
+ case 'cast':
304
+ return this.#resolveCast(node, pointer, scope);
305
+ case 'conversion':
306
+ return this.#resolveConversion(node, pointer, scope);
307
+ case 'try':
308
+ return this.#resolveTry(node, pointer, scope);
309
+ case 'and':
310
+ case 'or':
311
+ return this.#resolveLogical(node, pointer, scope);
312
+ default:
313
+ return this.#resolveObjectExpression(node, pointer, scope);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * The structured type each node constructs, or a list of one, by the node, while the node is being resolved in a place that expects it: given to a value argument by its parameter's type, and passed on to an
319
+ * object field by its property's, to a list's items, to a conditional's branches and to a coalescing's sides, so an object expression written where a structure is expected constructs it. An entry lasts as long
320
+ * as the resolution it is made for, so another reading of the node, a speculative one for another declaration or a receiver's, expects nothing of it.
321
+ * @type {WeakMap<object, Type>}
322
+ */
323
+ #expectedTypes = new WeakMap();
324
+
325
+ /** Resolves the node, expecting of it the type given where an object expression can construct that type, a structured type or a list of one, nullable or not, for as long as this resolution of it lasts. */
326
+ #resolveExpecting(node, pointer, scope, type) {
327
+ const constructible = type === undefined ? null : this.#constructibleTypeOf(type.toNonNullable());
328
+
329
+ if (constructible === null) {
330
+ return this.#resolveNode(node, pointer, scope);
331
+ }
332
+
333
+ this.#expectedTypes.set(node, constructible);
334
+
335
+ try {
336
+ return this.#resolveNode(node, pointer, scope);
337
+ } finally {
338
+ this.#expectedTypes.delete(node);
339
+ }
340
+ }
341
+
342
+ /** Whether the node is written in a form a structure's type reaches: an object expression following no value, a collection expression of items, a conditional or a coalescing. */
343
+ static #mayConstruct(node) {
344
+ return (node.kind === 'object' && node.context === undefined) || node.kind === 'list' || node.kind === 'conditional' || node.kind === 'coalescing';
345
+ }
346
+
347
+ /** The type, where an object expression can construct it, or a list of it at any depth, which its items can; null for any other, a type parameter its receiver hasn't bound among them. */
348
+ #constructibleTypeOf(type) {
349
+ if (type.isList) {
350
+ return this.#constructibleTypeOf(type.itemType.toNonNullable()) === null ? null : type;
351
+ }
352
+
353
+ return type.kind === 'named' && type.typeArguments.length === 0 && this.#registry.getDataShape(type) !== null ? type : null;
354
+ }
355
+
356
+ /**
357
+ * Checks the object fields an object expression gives against the data shape of the structure it constructs: each value assignable to its property's type, one of Any checked when it is evaluated, and every
358
+ * property it leaves out nullable, which then holds null. It answers the structure, and records the construction for evaluation, with the properties whose values it checks then.
359
+ */
360
+ #resolveConstruction(constructed, shape, fieldTypes, pointer) {
361
+ const checks = new Map();
362
+
363
+ for (const [name, propertyType] of shape) {
364
+ if (!fieldTypes.has(name)) {
365
+ if (!propertyType.isNullable) {
366
+ this.#fail('NEXO3087', `The ${constructed} written here leaves out '${name}', which is ${propertyType} and can't be null`, pointer);
367
+ }
368
+
369
+ continue;
370
+ }
371
+
372
+ const fieldType = fieldTypes.get(name);
373
+ const assignability = this.#typeChecker.assignabilityOf(fieldType, propertyType);
374
+
375
+ if (assignability === 'refused') {
376
+ const [expectedName, actualName] = Type.namesOf(propertyType, fieldType);
377
+ const index = [...fieldTypes.keys()].indexOf(name);
378
+
379
+ this.#fail('NEXO3088', `The property '${name}' of the ${constructed} written here is ${expectedName}, and is given ${actualName}`, `${pointer}/objectFields/${index}/value`);
380
+ }
381
+
382
+ if (assignability === 'checked') {
383
+ checks.set(name, propertyType);
384
+ }
385
+ }
386
+
387
+ this.#resolutions.set(pointer, { kind: 'construction', type: constructed, checks });
388
+
389
+ return { type: constructed, kind: 'value', isFixed: false };
390
+ }
391
+
392
+ /** The result each condition and operand resolved to, by its node, which the values a computed property access can give follow where it is fixed or its type tells its falsey part. @type {WeakMap<object, object>} */
393
+ #decidingResults = new WeakMap();
394
+
395
+ /**
396
+ * The argument bodies whose implicit parameter a reading has read, in the order it read them, so a reading a speculative resolution discards reads none of them: an argument is an implicit body only where an
397
+ * accepted reading reads its `it`. It lasts as long as this resolution, and holds each argument body once.
398
+ * @type {{ key: object, isRead: boolean }[]}
399
+ */
400
+ #readArgumentBodies = [];
401
+
402
+ /**
403
+ * The result of each argument resolved ahead of its invocation, an indexer's key to select its signature and a value argument to bind its member's type parameters, by the argument, with the scope and the place it
404
+ * was resolved at, which the invocation takes next rather than resolving the argument again, so arguments nested within arguments resolve in time linear in their depth. An entry is made as the argument is
405
+ * resolved ahead and removed as the invocation reads it, so it lasts from one to the other alone.
406
+ * @type {WeakMap<object, { scope: object, pointer: string, result: object }>}
407
+ */
408
+ #resolvedArguments = new WeakMap();
409
+
410
+ /**
411
+ * The nodes whose truthiness alone is tested where they are being resolved, by the node: a condition, what a conditional binding binds, the operand of `not`, an operand of `and` or `or`, and a predicate's argument
412
+ * written as an expression of `it`. A logical expression among them gives values that need share no type, and every other is used as a value, whose values reaching it share one. An entry lasts as long as the
413
+ * resolution it is made for, so another reading of the node, a candidate's taking it as a value among them, tests nothing.
414
+ * @type {WeakSet<object>}
415
+ */
416
+ #testedNodes = new WeakSet();
417
+
418
+ /** Resolves the node where its truthiness alone is tested, for as long as this resolution of it lasts. */
419
+ #resolveTested(node, pointer, scope) {
420
+ this.#testedNodes.add(node);
421
+
422
+ try {
423
+ return this.#resolveNode(node, pointer, scope);
424
+ } finally {
425
+ this.#testedNodes.delete(node);
426
+ }
427
+ }
428
+
429
+ /**
430
+ * Resolves a coalescing: a value and a fallback of one type, as a conditional's branches are, its type the value's made non-null where the fallback's is; the whole is resolved before any of it is evaluated.
431
+ * Where the value is fixed, it is the side that value selects, fixed where that side is.
432
+ */
433
+ #resolveCoalescing(node, pointer, scope) {
434
+ const results = ['value', 'fallback'].map(side => {
435
+ const result = this.#resolveExpecting(node[side], `${pointer}/${side}`, scope, this.#expectedTypes.get(node));
436
+
437
+ if (result.kind !== 'value') {
438
+ this.#fail('NEXO3048', `The ${side} of '??' is a value, and this one is ${LanguageResolver.#descriptionOf(result.kind)}`, `${pointer}/${side}`);
439
+ }
440
+
441
+ return result;
442
+ });
443
+ const [value, fallback] = results.map(result => result.type);
444
+
445
+ // A value of the type null is never anything but null, so the fallback's type is the whole's.
446
+ const type = value.kind === 'null' ? fallback : this.#typeChecker.merge(value.toNonNullable(), fallback);
447
+
448
+ if (type === null) {
449
+ this.#fail('NEXO3041', `The value and the fallback of '??' are of one type, and these are ${value} and ${fallback}`, `${pointer}/fallback`);
450
+ }
451
+
452
+ return LanguageResolver.#fixedAs(type, results[0].isFixed ? results[results[0].value === null ? 1 : 0] : null);
453
+ }
454
+
455
+ /** Resolves an optional chain: the chain it holds, nullable where an optional access of it can meet a null receiver, which ends the chain with null. */
456
+ #resolveOptionalChain(node, pointer, scope) {
457
+ const optionalChain = { shortCircuits: false };
458
+
459
+ this.#optionalChains.push(optionalChain);
460
+
461
+ try {
462
+ const result = this.#resolveNode(node.chain, `${pointer}/chain`, scope);
463
+
464
+ return optionalChain.shortCircuits ? { ...result, type: result.type.toNullable(), isFixed: false } : result;
465
+ } finally {
466
+ this.#optionalChains.pop();
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Resolves an equality: two values, compared, and answers a Boolean that is never null, whatever their types.
472
+ * A string, a number, a Boolean or a reference is compared with a value of a type one of theirs fits, and a value of any other type with null alone; a value of the type null, and a never, compare with any.
473
+ * Where `==`, or `!=` as it is negated, compares a value with a string that names one of its Boolean members, the refusal gives the member to write.
474
+ */
475
+ #resolveEquality(node, pointer, scope, isNegated) {
476
+ const left = this.#resolveNode(node.left, `${pointer}/left`, scope);
477
+ const right = this.#resolveNode(node.right, `${pointer}/right`, scope);
478
+ // An equality its negation holds is `!=`, as the canonical text writes it.
479
+ const operator = isNegated ? '!=' : '==';
480
+
481
+ for (const [side, result] of [['left', left], ['right', right]]) {
482
+ if (result.kind !== 'value') {
483
+ this.#fail('NEXO3048', `'${operator}' compares values, and this is ${LanguageResolver.#descriptionOf(result.kind)}`, `${pointer}/${side}`);
484
+ }
485
+ }
486
+
487
+ if (!LanguageResolver.#isNullOrNever(left.type) && !LanguageResolver.#isNullOrNever(right.type)) {
488
+ const leftType = left.type.toNonNullable();
489
+ const rightType = right.type.toNonNullable();
490
+ const isChecked = leftType.kind === 'any' || rightType.kind === 'any';
491
+
492
+ for (const [side, type] of [['left', leftType], ['right', rightType]]) {
493
+ if (type.kind !== 'any' && !this.#typeChecker.isComparable(type)) {
494
+ this.#fail('NEXO3066', `'${operator}' compares a value of ${type} with null alone, since only strings, numbers, Booleans and references are compared with each other`, `${pointer}/${side}`);
495
+ }
496
+ }
497
+
498
+ if (isChecked) {
499
+ this.#resolutions.set(pointer, { kind: 'equality', isChecked, operator });
500
+ } else if (!this.#typeChecker.fitsEitherWay(leftType, rightType)) {
501
+ this.#fail('NEXO3030', `'${operator}' compares values whose types fit one way or the other, and ${leftType} and ${rightType} fit neither way${this.#formatTestHint(node, left, right, isNegated)}`, pointer);
502
+ }
503
+ }
504
+
505
+ return { type: Type.createNamedType('Boolean'), kind: 'value', isFixed: false };
506
+ }
507
+
508
+ /**
509
+ * For an equality of a value and a string that names one of the value's Boolean members, as `input == "disabled"` or `input == state` writes one, the test to write, negated for `!=`: the property read, or the Boolean function called
510
+ * with no argument; nothing for any other. The name is read exactly, and the member is one the value's type has.
511
+ */
512
+ #formatTestHint(node, left, right, isNegated) {
513
+ if (!right.isFixed || typeof right.value !== 'string' || (node.right.kind !== 'literal' && node.right.kind !== 'scriptParameter')) {
514
+ return '';
515
+ }
516
+
517
+ const receiverType = left.type.toNonNullable();
518
+ const isTest = candidate => candidate.result === 'Boolean' && !NexoModule.isInvokedByHost(candidate) && (candidate.kind === 'property' || (candidate.kind === 'function' && candidate.parameters.every(parameter => !parameter.required)));
519
+ const selection = this.#memberSelector.select(this.#receiverMembersNamed(right.value).filter(isTest), receiverType);
520
+
521
+ if (selection.kind !== 'selected') {
522
+ return '';
523
+ }
524
+
525
+ const { declaration } = selection;
526
+
527
+ const optional = left.type.isNullable ? { optional: true } : {};
528
+ const name = node.right.kind === 'literal' ? right.value : node.right;
529
+ const test = declaration.kind === 'property' ? { kind: 'memberAccess', receiver: node.left, name, ...optional } : { kind: 'call', callee: { kind: 'memberAccess', receiver: node.left, name: right.value, ...optional }, arguments: [] };
530
+
531
+ return `: a test is the member that answers it, so write '${DefinitionWriter.quote(isNegated ? { kind: 'not', operand: test } : test, this.#namespaceNames)}'`;
532
+ }
533
+
534
+ /**
535
+ * Resolves a conditional: a condition, any value, or a conditional binding, any value, whose name the consequent alone sees, bound to that value where it is truthy, its truthy values of one type; and two branches
536
+ * of one type, nullable where either is. The consequent sees what the condition's being truthy guarantees of the stable references it tests, and the alternative what its being falsey does. The whole is resolved
537
+ * before any of it is evaluated.
538
+ */
539
+ #resolveConditional(node, pointer, scope) {
540
+ const condition = node.binding === undefined ? node.condition : node.binding.value;
541
+ const conditionPointer = node.binding === undefined ? `${pointer}/condition` : `${pointer}/binding/value`;
542
+
543
+ const tested = this.#resolveTested(condition, conditionPointer, scope);
544
+
545
+ this.#requireValue(tested, node.binding === undefined ? "The condition of 'if'" : "What 'if let' binds", conditionPointer);
546
+ this.#decidingResults.set(condition, tested);
547
+
548
+ const truthyScope = LanguageResolver.#withFacts(scope, this.#factsOf(condition, scope, true));
549
+ const falseyScope = LanguageResolver.#withFacts(scope, this.#factsOf(condition, scope, false));
550
+ let consequentScope = truthyScope;
551
+
552
+ if (node.binding !== undefined) {
553
+ // The consequent is taken where the value is truthy, so it sees the truthy values, of one type, fixed where the value is fixed; a value that is always falsey is never bound, and is a never there.
554
+ const isFalsey = tested.isFixed && !LanguageResolver.#isTruthy(tested.value);
555
+ const truthy = LanguageResolver.#valuesWhere(condition, tested, true);
556
+ const merged = isFalsey ? Type.never : this.#mergedTypeOf(truthy);
557
+
558
+ if (merged === null) {
559
+ this.#fail('NEXO3040', `What 'if let' binds is ${LanguageResolver.#describeValues(truthy)} where it is truthy, which no one type holds`, conditionPointer);
560
+ }
561
+
562
+ const type = LanguageResolver.#truthyPartOf(merged);
563
+ const result = tested.isFixed && !isFalsey ? { type, kind: 'value', isFixed: true, value: tested.value } : { type, kind: 'value', isFixed: false };
564
+
565
+ consequentScope = LanguageResolver.#consequentScopeOf(node, truthyScope, result);
566
+ }
567
+
568
+ const results = ['consequent', 'alternative'].map(branch => {
569
+ const result = this.#resolveExpecting(node[branch], `${pointer}/${branch}`, branch === 'consequent' ? consequentScope : falseyScope, this.#expectedTypes.get(node));
570
+
571
+ if (result.kind !== 'value') {
572
+ this.#fail('NEXO3048', `A branch of 'if' is a value, and this one is ${LanguageResolver.#descriptionOf(result.kind)}`, `${pointer}/${branch}`);
573
+ }
574
+
575
+ return result;
576
+ });
577
+ const branches = results.map(result => result.type);
578
+ const type = this.#typeChecker.merge(...branches);
579
+
580
+ if (type === null) {
581
+ this.#fail('NEXO3041', `The branches of 'if' are of one type, and these are ${branches[0]} and ${branches[1]}`, `${pointer}/alternative`);
582
+ }
583
+
584
+ // A fixed condition, or a fixed value bound, selects its branch when the expression is resolved, and the conditional is fixed where that branch is.
585
+ return LanguageResolver.#fixedAs(type, tested.isFixed ? results[LanguageResolver.#isTruthy(tested.value) ? 0 : 1] : null);
586
+ }
587
+
588
+ /** A result of the type, fixed to the value of the result that decides it where that one is fixed: a value resolution settles without evaluating a member. */
589
+ static #fixedAs(type, deciding) {
590
+ return deciding?.isFixed ? { type, kind: 'value', isFixed: true, value: deciding.value } : { type, kind: 'value', isFixed: false };
591
+ }
592
+
593
+ /**
594
+ * Resolves an and or an or: every value it combines, each seeing what the operands before it guarantee where it is reached, truthy ones for `and` and falsey ones for `or`, so the whole is validated before any of it is evaluated.
595
+ * It answers the operand that decides it, and gives the values that reach it: `and` the falsey values of each operand before its last, and the last's, and `or` the truthy values, and the last's. Where it is used as a
596
+ * value, they merge to its type, values no type holds being refused; where its truthiness alone is tested, they need not, and it has no type. Where fixed operands decide it, it is the one that does, fixed.
597
+ */
598
+ #resolveLogical(node, pointer, scope) {
599
+ const passesWhenTruthy = node.kind === 'and';
600
+ const results = [];
601
+ let passed = NO_FACTS;
602
+
603
+ node.operands.forEach((operand, index) => {
604
+ const operandPointer = `${pointer}/operands/${index}`;
605
+ const operandScope = LanguageResolver.#withFacts(scope, passed);
606
+
607
+ const result = this.#resolveTested(operand, operandPointer, operandScope);
608
+
609
+ this.#requireValue(result, `An operand of '${node.kind}'`, operandPointer);
610
+ this.#decidingResults.set(operand, result);
611
+ results.push(result);
612
+ passed = LanguageResolver.#unionOf(passed, this.#factsOf(operand, operandScope, passesWhenTruthy));
613
+ });
614
+
615
+ const last = results.length - 1;
616
+ let outcome = null;
617
+
618
+ // Operands whose values are fixed decide it before anything is evaluated, the operand that decides it giving its value, its type and its values.
619
+ for (let index = 0; index <= last && results[index].isFixed; index++) {
620
+ if (index === last || LanguageResolver.#isTruthy(results[index].value) !== passesWhenTruthy) {
621
+ outcome = { ...results[index], values: LanguageResolver.#valuesOf(node.operands[index], results[index]) };
622
+ break;
623
+ }
624
+ }
625
+
626
+ if (outcome === null) {
627
+ const values = results.flatMap((result, index) => (index === last ? LanguageResolver.#valuesOf(node.operands[index], result) : LanguageResolver.#valuesWhere(node.operands[index], result, !passesWhenTruthy)));
628
+
629
+ outcome = { type: this.#mergedTypeOf(values), kind: 'value', isFixed: false, values };
630
+ }
631
+
632
+ if (outcome.type === null && !this.#testedNodes.has(node)) {
633
+ const rewrite = node.kind === 'and' && node.operands.length === 2
634
+ ? `write 'if ${DefinitionWriter.quote(node.operands[0], this.#namespaceNames)} then ${DefinitionWriter.quote(node.operands[1], this.#namespaceNames)} else null end' if null is an acceptable alternative`
635
+ : "a conditional, 'if … then … else … end', chooses between values of one type";
636
+
637
+ this.#fail('NEXO3040', `What '${node.kind}' gives is ${LanguageResolver.#describeValues(outcome.values)}, which no one type holds; ${rewrite}`, pointer);
638
+ }
639
+
640
+ return outcome;
641
+ }
642
+
643
+ /**
644
+ * The values a node's result can give, each a type and whether a value of it is `truthy`, `falsey` or `either`: an `and`'s or an `or`'s own, a fixed value's, told from the value, and otherwise any value of its
645
+ * type. A local or any other node that holds a logical expression's value gives its type's values, so a value's type is settled where it is used.
646
+ */
647
+ static #valuesOf(node, result) {
648
+ if ((node.kind === 'and' || node.kind === 'or') && result.values !== undefined) {
649
+ return result.values;
650
+ }
651
+
652
+ if (result.isFixed) {
653
+ return [{ type: result.type, truthiness: LanguageResolver.#isTruthy(result.value) ? 'truthy' : 'falsey' }];
654
+ }
655
+
656
+ return [{ type: result.type, truthiness: 'either' }];
657
+ }
658
+
659
+ /** The values of a node's result that are truthy, or falsey: a value of either kind its type's truthy or falsey part, where it has one. */
660
+ static #valuesWhere(node, result, isTruthy) {
661
+ const truthiness = isTruthy ? 'truthy' : 'falsey';
662
+
663
+ return LanguageResolver.#valuesOf(node, result).flatMap(value => {
664
+ if (value.truthiness !== 'either') {
665
+ return value.truthiness === truthiness ? [value] : [];
666
+ }
667
+
668
+ const type = isTruthy ? LanguageResolver.#truthyPartOf(value.type) : LanguageResolver.#falseyPartOf(value.type);
669
+
670
+ return type.kind === 'never' ? [] : [{ type, truthiness }];
671
+ });
672
+ }
673
+
674
+ /** The one type the values merge to, a never for none, or null where no type holds them all. */
675
+ #mergedTypeOf(values) {
676
+ return values.reduce((merged, value) => (merged === null ? null : this.#typeChecker.merge(merged, value.type)), Type.never);
677
+ }
678
+
679
+ /** The types a result's values are of: its type, or, for a logical expression tested for its truthiness alone, which has none, each of its values'. */
680
+ static #typesOf(result) {
681
+ return result.type === null ? result.values.map(value => value.type) : [result.type];
682
+ }
683
+
684
+ /** Whether a value is truthy: every value is but null and false. */
685
+ static #isTruthy(value) {
686
+ return value !== null && value !== false;
687
+ }
688
+
689
+ /** The type a value of the type has where it is truthy: the type without null, a Boolean staying one, since no type tells true from false. */
690
+ static #truthyPartOf(type) {
691
+ return type.kind === 'null' ? Type.never : type.toNonNullable();
692
+ }
693
+
694
+ /** The type a value of the type has where it is falsey: a Boolean where it is one, `Any` where it is one, which may hold false, null where it can be null, and never where it is always truthy. */
695
+ static #falseyPartOf(type) {
696
+ if ((type.kind === 'named' && type.name === 'Boolean') || type.kind === 'any') {
697
+ return type;
698
+ }
699
+
700
+ return type.isNullable ? Type.null : Type.never;
701
+ }
702
+
703
+ /** How a refusal names values: a truthy Boolean as true and a falsey one as false, with null where it can be null, and any other by its type, each once. */
704
+ static #describeValues(values) {
705
+ const described = values.flatMap(({ type, truthiness }) => {
706
+ if (type.kind === 'never') {
707
+ return [];
708
+ }
709
+
710
+ if (truthiness === 'either' || !(type.kind === 'named' && type.name === 'Boolean')) {
711
+ return [String(type)];
712
+ }
713
+
714
+ return truthiness === 'truthy' ? ['true'] : type.isNullable ? ['false', 'null'] : ['false'];
715
+ });
716
+
717
+ return [...new Set(described)].join(' or ');
718
+ }
719
+
720
+ /**
721
+ * What the node's being truthy, or falsey, guarantees of the stable references it tests, each by its identity: a local, a conditional binding, a script parameter or the implicit parameter.
722
+ * A reference itself tells its own truthiness, `== null` whether it is null, `not` the opposite, and `and` and `or` what every operand tells on the path taken, or only what each path that reaches the outcome shares.
723
+ */
724
+ #factsOf(node, scope, isTruthy) {
725
+ switch (node.kind) {
726
+ case 'local':
727
+ case 'scriptParameter':
728
+ case 'it': {
729
+ const reference = this.#referenceOf(node, scope);
730
+
731
+ return reference === null ? NO_FACTS : new Map([[reference.key, isTruthy ? LanguageResolver.#truthyPartOf(reference.type) : LanguageResolver.#falseyPartOf(reference.type)]]);
732
+ }
733
+ case 'equality': {
734
+ const isNull = side => side.kind === 'literal' && side.value === null;
735
+ const tested = isNull(node.right) ? node.left : isNull(node.left) ? node.right : null;
736
+ const reference = tested === null ? null : this.#referenceOf(tested, scope);
737
+
738
+ if (reference === null) {
739
+ return NO_FACTS;
740
+ }
741
+
742
+ return new Map([[reference.key, isTruthy ? Type.null : LanguageResolver.#truthyPartOf(reference.type)]]);
743
+ }
744
+ case 'not':
745
+ return this.#factsOf(node.operand, scope, !isTruthy);
746
+ case 'and':
747
+ case 'or': {
748
+ const passesWhenTruthy = node.kind === 'and';
749
+ const isEveryPassed = isTruthy === passesWhenTruthy;
750
+ const stops = [];
751
+ let passed = NO_FACTS;
752
+
753
+ for (const operand of node.operands) {
754
+ const operandScope = LanguageResolver.#withFacts(scope, passed);
755
+
756
+ if (!isEveryPassed) {
757
+ stops.push(LanguageResolver.#unionOf(passed, this.#factsOf(operand, operandScope, !passesWhenTruthy)));
758
+ }
759
+
760
+ passed = LanguageResolver.#unionOf(passed, this.#factsOf(operand, operandScope, passesWhenTruthy));
761
+ }
762
+
763
+ return isEveryPassed ? passed : this.#commonOf(stops);
764
+ }
765
+ default:
766
+ return NO_FACTS;
767
+ }
768
+ }
769
+
770
+ /** The identity and the type, where it is resolved, of a stable reference, or null for one that is none: a member's value is read anew where it is used, so it tells nothing of a later read. */
771
+ #referenceOf(node, scope) {
772
+ switch (node.kind) {
773
+ case 'local': {
774
+ const local = scope.locals.get(node.name);
775
+
776
+ return local === undefined || local.result === null ? null : { key: local, type: scope.facts.get(local) ?? local.result.type };
777
+ }
778
+ case 'scriptParameter': {
779
+ if (!this.#bindings.has(node.name)) {
780
+ return null;
781
+ }
782
+
783
+ const key = `@${node.name}`;
784
+
785
+ return { key, type: scope.facts.get(key) ?? this.#bindings.getType(node.name) };
786
+ }
787
+ case 'it':
788
+ return scope.contextType === null ? null : { key: scope.contextKey, type: scope.facts.get(scope.contextKey) ?? scope.contextType };
789
+ default:
790
+ return null;
791
+ }
792
+ }
793
+
794
+ /** The scope with the facts added, a later fact on a reference narrowing it further. */
795
+ static #withFacts(scope, facts) {
796
+ return facts.size === 0 ? scope : { ...scope, facts: LanguageResolver.#unionOf(scope.facts, facts) };
797
+ }
798
+
799
+ /** Both sets of facts, the second's type for a reference both hold, since it was found where the first holds. */
800
+ static #unionOf(first, second) {
801
+ return second.size === 0 ? first : first.size === 0 ? second : new Map([...first, ...second]);
802
+ }
803
+
804
+ /** The facts every set holds, each reference typed as any of them allows; a reference one set lacks tells nothing. */
805
+ #commonOf(sets) {
806
+ const [first, ...rest] = sets;
807
+ const common = new Map();
808
+
809
+ for (const [key, type] of first) {
810
+ const merged = rest.reduce((sum, set) => (sum === null || !set.has(key) ? null : this.#typeChecker.merge(sum, set.get(key))), type);
811
+
812
+ if (merged !== null) {
813
+ common.set(key, merged);
814
+ }
815
+ }
816
+
817
+ return common;
818
+ }
819
+
820
+ /**
821
+ * Resolves a cast: its operand, a value, and the type it casts to, a reference type, its result of that type, nullable where the operand is, since null passes through it. An operand of the type's hierarchy, beneath it,
822
+ * above it or of it, is cast, and null too, since a binding typed from its value may be either; one that can never be of the type is refused. The cast keeps both types, for its evaluation and its message.
823
+ */
824
+ #resolveCast(node, pointer, scope) {
825
+ const target = this.#resolveNode(node.operand, `${pointer}/operand`, scope);
826
+ const targetType = this.#typeNamed(node.type, pointer);
827
+
828
+ if (target.kind !== 'value') {
829
+ this.#fail('NEXO3048', `A cast's operand is a value, and this one is ${LanguageResolver.#descriptionOf(target.kind)}`, `${pointer}/operand`);
830
+ }
831
+
832
+ if (!this.#registry.isReferenceType(targetType)) {
833
+ this.#fail(this.#declaresTypeNamed(node.type) ? 'NEXO3020' : 'NEXO3019', `'as' casts to a reference type, and '${node.type}' is none${this.#typeImportHintOf(node.type)}`, pointer);
834
+ }
835
+
836
+ const from = target.type;
837
+ const base = from.toNonNullable();
838
+ const isWithinHierarchy = from.kind === 'null' || from.kind === 'never' || base.kind === 'any' || (this.#registry.isReferenceType(base) && (this.#typeChecker.isAssignable(targetType, base) || this.#typeChecker.isAssignable(base, targetType)));
839
+ // A cast across hierarchies is the one a cast operator of its target's type declares to its type.
840
+ const declaration = isWithinHierarchy ? null : this.#selectOperator('cast', base, targetType, `The cast from ${base} to ${targetType}`, pointer);
841
+
842
+ if (declaration === undefined) {
843
+ this.#fail('NEXO3060', `A value of ${base} can never be cast to ${node.type}, a type of another hierarchy no cast operator reaches: the cast would always reject it`, pointer);
844
+ }
845
+
846
+ // A cast operator is a declaration the script uses, named by the syntax that invokes it, as a message names it.
847
+ if (declaration !== null) {
848
+ this.#usedDeclarations.push({ declaration, qualifiedName: this.#registry.formatOperator(declaration), pointer });
849
+ }
850
+
851
+ this.#resolutions.set(pointer, { kind: 'cast', type: targetType, sourceType: base, declaration });
852
+
853
+ return { type: from.kind === 'null' || from.isNullable ? targetType.toNullable() : targetType, kind: 'value', isFixed: false };
854
+ }
855
+
856
+ /**
857
+ * The type a cast or a conversion names: where the local name an import gives its module qualifies it, the type that module declares, refused where no import gives that local name (NEXO3004) or the module does not
858
+ * declare a type of that name; otherwise, by the script's tiers, the type a type import gives the name, then one of Nexo's own, then the one public type of its name among the modules the script imports, refused where
859
+ * several share it (NEXO3059), extensions taking no part, and a name none of them declares left to the cast or the conversion to refuse.
860
+ */
861
+ #typeNamed(name, pointer) {
862
+ const { moduleName, types } = this.#registry.resolveTypeName(name, this.#imported.typeNameScope);
863
+ const dot = name.indexOf('.');
864
+
865
+ if (dot >= 0) {
866
+ const qualifier = name.slice(0, dot);
867
+
868
+ if (moduleName === undefined) {
869
+ // Nexo's own module is never imported, and a script names its types unqualified.
870
+ const hint = qualifier === Names.nexoModuleName ? `: a script names Nexo's own types unqualified, as ${name.slice(dot + 1)}` : this.#registry.getModule(qualifier) !== undefined ? `: write 'import ${qualifier}' at the script's start` : '';
871
+
872
+ this.#fail('NEXO3004', `'${name}' is qualified by '${qualifier}', which no import of the script gives a module${hint}`, pointer);
873
+ }
874
+
875
+ if (types.length === 0) {
876
+ this.#fail('NEXO3019', `The module '${moduleName}' does not declare a type named '${name.slice(dot + 1)}'`, pointer);
877
+ }
878
+
879
+ if (this.#registry.isInternal(this.#registry.getTypeDeclaration(types[0]))) {
880
+ this.#failInternal(`The type '${name}'`, moduleName, pointer);
881
+ }
882
+
883
+ return types[0];
884
+ }
885
+
886
+ // A type import's type and Nexo's own are never internal; among the imported modules' an internal type is no candidate, so a name only internal types have names none a script reaches.
887
+ const reached = types.filter(type => type.moduleName === null || !this.#registry.isInternal(this.#registry.getTypeDeclaration(type)));
888
+
889
+ if (reached.length > 1) {
890
+ this.#fail('NEXO3059', `'${name}' names a type several modules declare, so it is named qualified by its module: ${reached.map(type => `${type.moduleName}.${name}`).join(' or ')}`, pointer);
891
+ }
892
+
893
+ if (reached.length === 0 && types.length > 0) {
894
+ this.#failInternal(`The type '${name}'`, types[0].moduleName, pointer);
895
+ }
896
+
897
+ return reached[0] ?? Type.createNamedType(name, false, null);
898
+ }
899
+
900
+ /** Whether a type of the name a cast or a conversion writes is one the script names: a qualified one, which its module was found to declare, one a type import gives, one of Nexo's own, or one an imported module declares. */
901
+ #declaresTypeNamed(name) {
902
+ return name.includes('.') || this.#registry.resolveTypeName(name, this.#imported.typeNameScope).types.length > 0;
903
+ }
904
+
905
+ /** The import to write for a type name no import gives, where registered modules the script does not import declare a public type of that name, as a message ends; empty otherwise. */
906
+ #typeImportHintOf(name) {
907
+ if (name.includes('.') || this.#declaresTypeNamed(name)) {
908
+ return '';
909
+ }
910
+
911
+ const modules = this.#registry.getModuleNamesDeclaringType(name).filter(moduleName => !this.#registry.isInternal(this.#registry.getTypeDeclaration(Type.createNamedType(name, false, moduleName))));
912
+
913
+ return modules.length === 0 ? '' : `; ${modules.map(module => `'${module}'`).join(' and ')} declare${modules.length === 1 ? 's' : ''} it: write ${modules.map(module => `'import ${module}'`).join(' or ')} at the script's start`;
914
+ }
915
+
916
+ /**
917
+ * The operator of the name a value of the source type is cast or converted to the destination by, among those of that destination the selector selects, the owner's winning and otherwise the extension's whose
918
+ * receiver is more specific than every other's; undefined where none applies, refusing an internal one (NEXO3023) and several none of which is more specific (NEXO3024), since no syntax selects one module's.
919
+ */
920
+ #selectOperator(name, source, destination, subject, pointer) {
921
+ const declared = this.#registry.getOperators(name).filter(operator => this.#registry.getOperatorDestination(operator).equals(destination));
922
+ const selection = this.#memberSelector.select(this.#imported.getAvailable(declared), source);
923
+
924
+ if (selection.kind === 'none') {
925
+ this.#refuseUnimported(declared, source, subject, pointer);
926
+ }
927
+
928
+ if (selection.kind === 'internal') {
929
+ this.#failInternal(subject, this.#registry.getDeclaringModuleName(selection.declarations[0]), pointer);
930
+ }
931
+
932
+ if (selection.kind === 'ambiguous') {
933
+ const modules = [...new Set(selection.declarations.map(operator => this.#registry.getDeclaringModuleName(operator)))];
934
+
935
+ this.#fail('NEXO3024', `${subject} is declared by the modules ${modules.map(module => `'${module}'`).join(' and ')}, none of whose receivers is more specific than every other's, so which applies is ambiguous`, pointer);
936
+ }
937
+
938
+ return selection.kind === 'selected' ? selection.declaration : undefined;
939
+ }
940
+
941
+ /** Resolves an attempt: the conversion or the cast it attempts, as it resolves strictly, its result made nullable, since a rejection answers null. */
942
+ #resolveTry(node, pointer, scope) {
943
+ const result = this.#resolveNode(node.operand, `${pointer}/operand`, scope);
944
+
945
+ return { type: result.type.toNullable(), kind: 'value', isFixed: false };
946
+ }
947
+
948
+ /**
949
+ * Resolves a conversion: its operand, a value, and the type of data it converts to, its result of that type, nullable where the operand is.
950
+ * A value already of the type answers itself, which no declaration overrides; any other takes the conversion operator of its type to that one the selector selects, never a chain of them.
951
+ * An operand of unknown type chooses none: null answers null, and a never is never evaluated.
952
+ */
953
+ #resolveConversion(node, pointer, scope) {
954
+ const target = this.#resolveNode(node.operand, `${pointer}/operand`, scope);
955
+
956
+ if (target.kind !== 'value') {
957
+ this.#fail('NEXO3048', `What 'to' converts is a value, and this one is ${LanguageResolver.#descriptionOf(target.kind)}`, `${pointer}/operand`);
958
+ }
959
+
960
+ const to = this.#typeNamed(node.type, pointer);
961
+
962
+ if (!this.#registry.isConvertible(to)) {
963
+ const cast = to?.kind === 'named' && this.#registry.isReferenceType(to) ? `; a reference is cast to a type of its hierarchy with 'as', as in … as ${node.type}` : '';
964
+
965
+ this.#fail(this.#declaresTypeNamed(node.type) ? 'NEXO3021' : 'NEXO3019', `'to' converts to a type of data named as it is declared, Number, String, Boolean or a structured type the vocabulary declares, and '${node.type}' is none of them${cast}${this.#typeImportHintOf(node.type)}`, pointer);
966
+ }
967
+
968
+ const from = target.type;
969
+
970
+ if (from.kind === 'null' || from.kind === 'never') {
971
+ this.#resolutions.set(pointer, { kind: 'conversion', declaration: null });
972
+
973
+ return { type: from.kind === 'null' ? to.toNullable() : to, kind: 'value', isFixed: false };
974
+ }
975
+
976
+ const source = from.toNonNullable();
977
+
978
+ // A value of Any converts as it is evaluated, by the conversion from its actual type among those the imports make available to the type, or to itself where it is of that type already.
979
+ if (source.kind === 'any') {
980
+ const candidates = this.#memberSelector.getAccessible(this.#imported.getAvailable(this.#registry.getOperators('convert'))).filter(operator => this.#registry.getOperatorDestination(operator).equals(to));
981
+
982
+ const candidateUses = candidates.map(declaration => ({ declaration, qualifiedName: this.#registry.formatOperator(declaration), pointer }));
983
+
984
+ this.#resolutions.set(pointer, { kind: 'conversion', declaration: undefined, candidates, candidateUses, type: to });
985
+
986
+ return { type: from.isNullable ? to.toNullable() : to, kind: 'value', isFixed: false };
987
+ }
988
+
989
+ const isIdentity = source.kind === 'named' && source.equals(to);
990
+ const declaration = isIdentity ? undefined : this.#selectOperator('convert', source, to, `The conversion from ${source} to ${to}`, pointer);
991
+
992
+ if (!isIdentity && declaration === undefined) {
993
+ const targets = [...new Set(this.#memberSelector.getAccessible(this.#imported.getAvailable(this.#registry.getOperators('convert'))).filter(operator => this.#typeChecker.resolveApplyingReceiverType(operator, source) !== undefined).map(operator => String(this.#registry.getOperatorDestination(operator))))];
994
+ const available = targets.length === 0 ? `${source} converts to nothing` : `${source} converts to ${targets.join(', ')}`;
995
+
996
+ this.#fail('NEXO3022', `No conversion from ${source} to ${to.name} is declared, and ${available}`, pointer);
997
+ }
998
+
999
+ if (declaration !== undefined) {
1000
+ this.#usedDeclarations.push({ declaration, qualifiedName: this.#registry.formatOperator(declaration), pointer });
1001
+ }
1002
+
1003
+ this.#resolutions.set(pointer, { kind: 'conversion', declaration: declaration ?? null, type: to });
1004
+
1005
+ return { type: from.isNullable ? to.toNullable() : to, kind: 'value', isFixed: false };
1006
+ }
1007
+
1008
+ /** Refuses an indexer's key that holds a function, since a key is data, as a collection's item is (NEXO3082). */
1009
+ #refuseFunctionKey(result, pointer) {
1010
+ if (result.kind === 'value' && LanguageResolver.#heldReferenceOf(result.type) === 'function') {
1011
+ this.#fail('NEXO3082', "An indexer's key is data, and this one holds a function, which no key is: call it, and index by what it answers", pointer);
1012
+ }
1013
+ }
1014
+
1015
+ /**
1016
+ * Resolves a collection expression's items, a list's or a dictionary's values, in order, and answers the type they merge to, a never where it holds none: each is a value, never a host call (NEXO3048) nor a function,
1017
+ * since a collection holds data and references (NEXO3082), and items of no common type are refused (NEXO3081), never widened to Any.
1018
+ */
1019
+ #resolveCollectionItems(parts, scope, pointer, subject, expectedItemType) {
1020
+ const types = [];
1021
+ let merged = Type.never;
1022
+
1023
+ for (const part of parts) {
1024
+ const result = this.#resolveExpecting(part.node, part.pointer, scope, expectedItemType);
1025
+
1026
+ if (result.kind !== 'value') {
1027
+ this.#fail('NEXO3048', `${subject} are values, and this one is ${LanguageResolver.#descriptionOf(result.kind)}`, part.pointer);
1028
+ }
1029
+
1030
+ if (LanguageResolver.#heldReferenceOf(result.type) === 'function') {
1031
+ this.#fail('NEXO3082', `${subject} are data and references, and this one holds a function, which a collection never does: call it, and hold what it answers`, part.pointer);
1032
+ }
1033
+
1034
+ types.push(result.type);
1035
+ merged = merged === null ? null : this.#typeChecker.merge(merged, result.type);
1036
+ }
1037
+
1038
+ if (merged === null) {
1039
+ this.#fail('NEXO3081', `${subject} share no type: ${[...new Set(types.map(type => String(type)))].join(', ')}`, pointer);
1040
+ }
1041
+
1042
+ return merged;
1043
+ }
1044
+
1045
+ /** The scope a conditional binding's consequent is resolved in: the scope around it, with the binding's name a local holding its value, of the result given. */
1046
+ static #consequentScopeOf(node, scope, result) {
1047
+ return { ...scope, locals: scope.locals.with(node.binding.name, { result, initializer: node.binding.value, scope, conditional: node }) };
1048
+ }
1049
+
1050
+ /** Resolves a negation: what it negates, any value, and answers a Boolean, true where that value is falsey. */
1051
+ #resolveNot(node, pointer, scope) {
1052
+ // An equality is negated as `!=` writes it, so the member a refusal gives is negated too, and it is recorded where it stands, as a node resolved alone is.
1053
+ if (node.operand.kind === 'equality') {
1054
+ this.#pointers.set(node.operand, `${pointer}/operand`);
1055
+ }
1056
+
1057
+ const operand = node.operand.kind === 'equality' ? this.#resolveEquality(node.operand, `${pointer}/operand`, scope, true) : this.#resolveTested(node.operand, `${pointer}/operand`, scope);
1058
+
1059
+ this.#requireValue(operand, "What 'not' negates", `${pointer}/operand`);
1060
+
1061
+ return { type: Type.createNamedType('Boolean'), kind: 'value', isFixed: false };
1062
+ }
1063
+
1064
+ /**
1065
+ * The signature of the indexer the selection chose that the access's key selects: the one signature of an indexer that has one, and otherwise the one whose key type the key's type is assignable to, a key of null
1066
+ * or a never the first's; a key none takes, and one of Any where several stand, is refused (NEXO3084), never falling through to another member. The key is resolved once, its records kept, and its result is
1067
+ * handed to the signature's invocation, which takes it rather than resolving the key again, so keys nested within keys resolve in time linear in their depth.
1068
+ */
1069
+ #selectSignature(declaration, node, pointer, scope, receiverType) {
1070
+ const signatures = this.#registry.getIndexerSignatures(declaration);
1071
+
1072
+ if (signatures.length === 1) {
1073
+ return declaration;
1074
+ }
1075
+
1076
+ const keyPointer = `${pointer}/argument`;
1077
+ const typeArguments = (receiverType === null ? null : this.#typeChecker.inferReceiverTypeArguments(declaration, receiverType)) ?? new Map();
1078
+ const keyTypeOf = signature => this.#registry.getDeclaredType(signature, signature.parameters[0].type).resolveTypeParameters(typeArguments);
1079
+
1080
+ // A key written in a form a structure's type reaches constructs the one structure among the signatures' keys it fits, and selects its signature.
1081
+ if (LanguageResolver.#mayConstruct(node.argument)) {
1082
+ const constructing = signatures.filter(signature => {
1083
+ const keyType = keyTypeOf(signature);
1084
+
1085
+ if (this.#constructibleTypeOf(keyType.toNonNullable()) === null) {
1086
+ return false;
1087
+ }
1088
+
1089
+ const fits = this.#resolveSpeculatively(() => this.#resolveExpecting(node.argument, keyPointer, scope, keyType), { keepsSuccess: false });
1090
+
1091
+ return fits.error === undefined && this.#typeChecker.isAssignable(fits.result.type.toNonNullable(), keyType.toNonNullable());
1092
+ });
1093
+
1094
+ if (constructing.length === 1) {
1095
+ return constructing[0];
1096
+ }
1097
+
1098
+ if (constructing.length > 1) {
1099
+ const subject = declaration.name === undefined ? `The unnamed indexer of ${receiverType?.toNonNullable()}` : `The indexer '${declaration.name}'`;
1100
+
1101
+ this.#fail('NEXO3089', `${subject} is given an object that constructs ${constructing.map(keyTypeOf).map(String).join(', ').replace(/, ([^,]*)$/, ' and $1')} alike, which its signatures take apart; give it a value of one of them`, keyPointer);
1102
+ }
1103
+ }
1104
+
1105
+ const outcome = this.#resolveSpeculatively(() => this.#resolveNode(node.argument, keyPointer, scope), { keepsSuccess: true });
1106
+
1107
+ if (outcome.error !== undefined) {
1108
+ throw outcome.error;
1109
+ }
1110
+
1111
+ this.#resolvedArguments.set(node.argument, { scope, pointer: keyPointer, result: outcome.result });
1112
+
1113
+ const keyType = outcome.result.type;
1114
+
1115
+ this.#refuseFunctionKey(outcome.result, keyPointer);
1116
+
1117
+ if (keyType.kind === 'null' || keyType.kind === 'never') {
1118
+ return signatures[0];
1119
+ }
1120
+
1121
+ const taking = signatures.filter(signature => keyType.kind !== 'any' && this.#typeChecker.isAssignable(keyType.toNonNullable(), keyTypeOf(signature).toNonNullable()));
1122
+
1123
+ if (taking.length === 1) {
1124
+ return taking[0];
1125
+ }
1126
+
1127
+ const subject = declaration.name === undefined ? `The unnamed indexer of ${receiverType?.toNonNullable()}` : `The indexer '${declaration.name}'`;
1128
+ const given = keyType.kind === 'any' ? 'its key is of Any, which selects no signature until its value is known' : `is given ${keyType}`;
1129
+
1130
+ this.#fail('NEXO3084', `${subject} takes ${signatures.map(keyTypeOf).map(String).join(', ').replace(/, ([^,]*)$/, ' or $1')}, and ${given}`, keyPointer);
1131
+ }
1132
+
1133
+ /**
1134
+ * Refuses a position that is none (NEXO3085): an index's offset is a `Number`, and a range's boundary a `Number` or an `Index`, neither nullable; a never is never evaluated, and a value of Any is checked as it
1135
+ * is evaluated.
1136
+ */
1137
+ #requirePosition(result, what, pointer, allowsIndex) {
1138
+ this.#requireValue(result, what, pointer);
1139
+
1140
+ const { type } = result;
1141
+ const takes = allowsIndex ? 'a number or an index' : 'a number';
1142
+
1143
+ if (type.kind === 'never' || (type.kind === 'any' && !type.isNullable)) {
1144
+ return;
1145
+ }
1146
+
1147
+ if (type.isNullable || type.kind === 'null') {
1148
+ this.#fail('NEXO3085', `${what} can be null, and is ${takes}`, pointer);
1149
+ }
1150
+
1151
+ const isNamed = name => type.kind === 'named' && type.moduleName === null && type.name === name;
1152
+
1153
+ if (!isNamed('Number') && !(allowsIndex && isNamed('Index'))) {
1154
+ this.#fail('NEXO3085', `${what} is ${takes}, and this one is ${type}`, pointer);
1155
+ }
1156
+ }
1157
+
1158
+ /**
1159
+ * Refuses what the operation, arithmetic unless another is named, takes no part in: what is no value, a value that can be null, which a fallback or a test removes first,
1160
+ * and a value of any type but a number, which nothing converts to one but `to`.
1161
+ */
1162
+ #requireNumber(result, what, pointer, operation = 'arithmetic') {
1163
+ this.#requireValue(result, what, pointer);
1164
+
1165
+ const { type } = result;
1166
+
1167
+ if (type.kind === 'never') {
1168
+ return;
1169
+ }
1170
+
1171
+ if (type.isNullable || type.kind === 'null') {
1172
+ this.#fail(operation === 'arithmetic' ? 'NEXO3032' : 'NEXO3039', `${what} can be null, and ${operation} takes numbers alone: give it a fallback, as in (… ?? 0), or test it first`, pointer);
1173
+ }
1174
+
1175
+ // A value of Any is a number or not as it is evaluated, which checks it then.
1176
+ if (type.kind === 'any') {
1177
+ return;
1178
+ }
1179
+
1180
+ if (type.kind !== 'named' || type.name !== 'Number') {
1181
+ this.#fail(operation === 'arithmetic' ? 'NEXO3067' : 'NEXO3078', `${what} is a number, and this one is ${type}; a conversion makes a number of text, as in … to Number`, pointer);
1182
+ }
1183
+ }
1184
+
1185
+ /** Refuses what an interpolation inserts no text for: what is no value, a value that can be null, which a fallback or a test removes first, and a value of any type but a string or a number, a Boolean's text given by `to`. */
1186
+ #requireInterpolationValue(result, pointer) {
1187
+ const what = 'An interpolation';
1188
+
1189
+ this.#requireValue(result, what, pointer);
1190
+
1191
+ const { type } = result;
1192
+
1193
+ if (type.kind === 'never') {
1194
+ return;
1195
+ }
1196
+
1197
+ if (type.isNullable || type.kind === 'null') {
1198
+ this.#fail('NEXO3038', `${what} can be null: give it a fallback, as in (… ?? "…"), or test it first`, pointer);
1199
+ }
1200
+
1201
+ if (type.kind === 'any') {
1202
+ return;
1203
+ }
1204
+
1205
+ if (type.kind !== 'named' || (type.name !== 'String' && type.name !== 'Number')) {
1206
+ const hint = type.kind === 'named' && type.name === 'Boolean' ? '; give its text with … to String, or choose it with a conditional' : '';
1207
+
1208
+ this.#fail('NEXO3077', `${what} inserts a string or a number, and this one is ${type}${hint}`, pointer);
1209
+ }
1210
+ }
1211
+
1212
+ /** Refuses what is no value where one is used, a host call, which ends the script alone, and a value whose type no receiver gives, which tells nothing of what it holds. */
1213
+ #requireValue(result, what, pointer) {
1214
+ if (result.kind !== 'value') {
1215
+ this.#fail('NEXO3048', `${what} is a value, and this one is ${LanguageResolver.#descriptionOf(result.kind)}`, pointer);
1216
+ }
1217
+
1218
+ const held = LanguageResolver.#typesOf(result).find(type => LanguageResolver.#holdsTypeParameter(type));
1219
+
1220
+ if (held !== undefined) {
1221
+ this.#fail('NEXO3028', `${what} produces ${held}, a type no receiver gives`, pointer);
1222
+ }
1223
+ }
1224
+
1225
+ /**
1226
+ * Resolves a body: each local against the scope and the locals before it, a value, and its result against them all, a value too, its type the result's, or the host call the script ends in where the body
1227
+ * stands in the script's terminal position. It is fixed only where every local and its result are, so a read a local makes, used or not, is never discarded.
1228
+ */
1229
+ #resolveBody(node, pointer, scope, isTerminal = false) {
1230
+ let locals = scope.locals;
1231
+ let isFixed = true;
1232
+
1233
+ node.locals.forEach((local, index) => {
1234
+ const valuePointer = `${pointer}/locals/${index}/value`;
1235
+ const localScope = { ...scope, locals };
1236
+
1237
+ // A function is resolved where it is called, against that call's arguments, so its local holds the scope it was declared in.
1238
+ if (local.value.kind === 'function') {
1239
+ locals = locals.with(local.name, LanguageResolver.#functionLocalOf(local.value, valuePointer, localScope));
1240
+
1241
+ return;
1242
+ }
1243
+
1244
+ const result = this.#resolveNode(local.value, valuePointer, localScope);
1245
+
1246
+ if (result.kind !== 'value') {
1247
+ this.#fail('NEXO3048', `The local '${local.name}' is ${LanguageResolver.#descriptionOf(result.kind)}, and a local is a value`, valuePointer);
1248
+ }
1249
+
1250
+ isFixed &&= result.isFixed;
1251
+ locals = locals.with(local.name, { result, initializer: local.value, scope: localScope });
1252
+ });
1253
+
1254
+ const resultPointer = `${pointer}/result`;
1255
+ const resultScope = { ...scope, locals };
1256
+ const result = isTerminal ? this.#resolveTerminal(node.result, resultPointer, resultScope) : this.#resolveNode(node.result, resultPointer, resultScope);
1257
+
1258
+ // A body gives a value, as a field holds one, and returns a host call only where it stands in the script's terminal position.
1259
+ if (result.kind !== 'value' && !(isTerminal && result.kind === 'hostCall')) {
1260
+ this.#fail('NEXO3048', result.kind === 'hostCall' ? 'A host call ends the script, and this body gives its result to the expression around it, which takes a value' : `A body gives a value, and its result is ${LanguageResolver.#descriptionOf(result.kind)}`, resultPointer);
1261
+ }
1262
+
1263
+ return isFixed && result.isFixed ? result : { ...result, isFixed: false };
1264
+ }
1265
+
1266
+ /** The local a function declaration gives its name: the function, where it is declared, and the scope it captures, its locals and its implicit parameter. */
1267
+ static #functionLocalOf(node, pointer, scope) {
1268
+ return { function: node, pointer, scope, result: { type: Type.never, kind: 'function', isFixed: false }, value: node };
1269
+ }
1270
+
1271
+ /**
1272
+ * Resolves a call of a local function, or of a local or a parameter holding a function reference: its arguments against the caller's scope, as many as each function it may be declares,
1273
+ * and then, for each one, a copy of its body of its own, against the scope that function captured and its parameters, typed as this call's arguments, their results merged.
1274
+ * A local call settles no fixed argument.
1275
+ */
1276
+ #resolveFunctionCall(node, pointer, scope) {
1277
+ const local = scope.locals.get(node.callee.name);
1278
+
1279
+ if (local === undefined) {
1280
+ const form = scope.contextType === null ? null : this.#memberForm(scope.contextType, node.callee.name);
1281
+
1282
+ this.#fail('NEXO3033', `No function named '${node.callee.name}' is in scope${form === null ? '' : `; write '${form}' for the member of the implicit parameter`}`, pointer);
1283
+ }
1284
+
1285
+ // A local a test narrowed is called as the test leaves it, so one tested not to be null holds a function.
1286
+ const calleeType = local.function !== undefined ? null : (scope.facts.get(local) ?? local.result?.type);
1287
+ const functions = local.function !== undefined ? [local] : (calleeType?.kind === 'function' ? calleeType.functions : null);
1288
+
1289
+ if (functions === null) {
1290
+ this.#fail('NEXO3068', `'${node.callee.name}' is a local holding a value, and no function, so it is read without parentheses`, pointer);
1291
+ }
1292
+
1293
+ if (local.function === undefined && calleeType.isNullable) {
1294
+ this.#fail('NEXO3068', `'${node.callee.name}' may hold null, and only a function is called: bind one to call in its place first, as let call = ${node.callee.name} ?? other`, pointer);
1295
+ }
1296
+
1297
+ // A call of one a reference to a module's function alone is the call its qualified form makes, and of one bound to a known receiver the call its extension access makes, whatever its arguments' kinds.
1298
+ if (functions.length === 1 && functions[0].reference !== undefined) {
1299
+ const [target] = functions;
1300
+
1301
+ return target.isBound === true ? this.#resolveBoundReferenceCall(node, pointer, scope, target) : this.#resolveReferenceCall(node, pointer, scope, local, target.reference);
1302
+ }
1303
+
1304
+ // A call of several possible functions gives them one argument list, which their one contract reads where one of them takes an argument of another kind than a value.
1305
+ if (functions.length > 1 && functions.some(target => target.reference !== undefined && !LanguageResolver.#takesValuesAlone(target.reference))) {
1306
+ return this.#resolveSharedContractCall(node, pointer, scope, functions);
1307
+ }
1308
+
1309
+ for (const target of functions) {
1310
+ if (target.reference !== undefined) {
1311
+ continue;
1312
+ }
1313
+
1314
+ const { parameters } = target.function;
1315
+
1316
+ if (node.arguments.length !== parameters.length) {
1317
+ this.#fail('NEXO3069', `'${node.callee.name}' takes ${parameters.length === 1 ? 'one argument' : `${parameters.length} arguments`}, and is given ${node.arguments.length}`, pointer);
1318
+ }
1319
+ }
1320
+
1321
+ const results = node.arguments.map((argument, index) => {
1322
+ const argumentPointer = `${pointer}/arguments/${index}`;
1323
+ const result = this.#resolveNode(argument, argumentPointer, scope);
1324
+
1325
+ if (result.kind !== 'value') {
1326
+ this.#fail('NEXO3048', `An argument of '${node.callee.name}' is ${LanguageResolver.#descriptionOf(result.kind)}, and an argument is a value`, argumentPointer);
1327
+ }
1328
+
1329
+ return result;
1330
+ });
1331
+
1332
+ // A reference bound to its receiver takes it as the reference captured it, before the values the call gives.
1333
+ const targets = functions.map((target, index) => (target.reference !== undefined
1334
+ ? this.#resolveModuleFunctionCall(target.reference, node, target.isBound === true ? [{ type: target.receiverType, kind: 'value', isFixed: false }, ...results] : results, pointer, target.isBound === true)
1335
+ : this.#resolveScriptFunctionCall(target, node.callee.name, results, node.arguments, scope, functions.length === 1 ? `${pointer}/body` : `${pointer}/bodies/${index}`, pointer)));
1336
+ const type = targets.reduce((merged, target) => (merged === null ? null : this.#typeChecker.merge(merged, target.type)), Type.never);
1337
+
1338
+ if (type === null) {
1339
+ this.#fail('NEXO3070', `'${node.callee.name}' may be any of ${targets.length} functions, whose results are of no one type: ${targets.map(target => target.type).join(', ')}`, pointer);
1340
+ }
1341
+
1342
+ // The callee is the local the call reads its function reference from, which evaluation selects among the functions it may be.
1343
+ this.#resolutions.set(pointer, {
1344
+ kind: 'invocation',
1345
+ functions: targets.map(({ function: declared, body, parameters, reference, argumentMap, resultType }) => (reference === undefined ? { function: declared, body, parameters } : { reference, argumentMap, resultType })),
1346
+ callee: node.callee,
1347
+ });
1348
+
1349
+ return { type, kind: 'value', isFixed: false };
1350
+ }
1351
+
1352
+ /**
1353
+ * Resolves the syntax of a call through a known reference to a module's function, the one a local holds: the call its qualified form makes, its arguments interpreted as that form's are, its receiver argument first where its declaration has
1354
+ * a receiver, a null one answering null, and its own arguments after it, each of the kind its parameter declares.
1355
+ */
1356
+ #resolveReferenceCall(node, pointer, scope, local, declaration) {
1357
+ const written = this.#referenceNameOf(declaration);
1358
+
1359
+ if (!this.#registry.takesReceiver(declaration)) {
1360
+ return this.#storeResolution(pointer, this.#resolveInvocation(declaration, node, pointer, scope, null, written));
1361
+ }
1362
+
1363
+ return this.#storeResolution(pointer, this.#resolveQualifiedCall(node, pointer, scope, [declaration], {
1364
+ subject: `'${node.callee.name}'`,
1365
+ missingReceiver: `'${node.callee.name}' holds a reference to '${written}', which takes its receiver as its first argument`,
1366
+ receiverOf: `'${node.callee.name}' holds a reference to '${written}', which takes a receiver of`,
1367
+ nameOf: () => written,
1368
+ }));
1369
+ }
1370
+
1371
+ /**
1372
+ * Resolves a call of a local that may hold several functions, one of which takes an argument of another kind than a value: the one argument contract every function shares reads the call's arguments, as a call
1373
+ * of any one of them would, and evaluation invokes the function the local holds. They share one where every one is a reference to a module's function, all bound to their receivers or all taking their receiver
1374
+ * as the call's first argument, and agree on their argument form, on each parameter's name, kind, requirement, null policy, default and type, a callback's parameter types among them, once their receivers bind
1375
+ * what their types write, and on type parameters of their own, which only one declaration binds. Where they read the arguments differently, the call is refused, naming where they part (NEXO3079): no one argument
1376
+ * list fits functions whose contracts read it differently.
1377
+ */
1378
+ #resolveSharedContractCall(node, pointer, scope, functions) {
1379
+ const written = node.callee.name;
1380
+ // A reference bound to its receiver is named by its receiver's type, and any other as its qualified form names it.
1381
+ const nameOf = target => {
1382
+ if (target.reference === undefined) {
1383
+ return 'a function the script declares';
1384
+ }
1385
+
1386
+ return `'${target.isBound === true ? `${target.receiverType}.${target.reference.name}` : this.#referenceNameOf(target.reference)}'`;
1387
+ };
1388
+ const refuse = (first, other, difference) => this.#fail('NEXO3079', `'${written}' may hold ${nameOf(first)} or ${nameOf(other)}, which read its arguments differently: ${difference}; call each through a local that holds it alone`, pointer);
1389
+ const scriptFunction = functions.find(target => target.reference === undefined);
1390
+ const [firstTarget] = functions.filter(target => target.reference !== undefined && !LanguageResolver.#takesValuesAlone(target.reference));
1391
+
1392
+ if (scriptFunction !== undefined) {
1393
+ refuse(scriptFunction, firstTarget, `a function the script declares takes values alone, and ${nameOf(firstTarget)} an argument of another kind`);
1394
+ }
1395
+
1396
+ const isBound = firstTarget.isBound === true;
1397
+ const takesReceiver = !isBound && this.#registry.takesReceiver(firstTarget.reference);
1398
+
1399
+ for (const target of functions) {
1400
+ if ((target.isBound === true) !== isBound || (!isBound && this.#registry.takesReceiver(target.reference) !== takesReceiver)) {
1401
+ refuse(firstTarget, target, 'one takes its receiver as its first argument, and the other has it already, or takes none');
1402
+ }
1403
+
1404
+ if (isBound && target.receiverType.kind === 'any') {
1405
+ refuse(firstTarget, target, 'a receiver of Any selects its member only as it is evaluated');
1406
+ }
1407
+ }
1408
+
1409
+ if (takesReceiver && node.arguments.length === 0) {
1410
+ this.#fail('NEXO3080', `'${written}' holds a reference to a function that takes its receiver as its first argument, and is given none`, pointer);
1411
+ }
1412
+
1413
+ // A receiver argument is resolved once, before the contract reads the call's own arguments, and binds what every declaration's receiver type writes.
1414
+ const receiverArgument = takesReceiver ? this.#resolveNode(node.arguments[0], `${pointer}/arguments/0`, scope) : null;
1415
+ const contracts = functions.map(target => {
1416
+ const receiverType = isBound ? target.receiverType : (receiverArgument?.type.toNonNullable() ?? null);
1417
+ const isKnown = receiverType !== null && !LanguageResolver.#isNullOrNever(receiverType);
1418
+
1419
+ return { target, declaration: target.reference, typeArguments: (isKnown ? this.#typeChecker.inferReceiverTypeArguments(target.reference, receiverType) : null) ?? new Map() };
1420
+ });
1421
+ const [first] = contracts.filter(contract => contract.target === firstTarget);
1422
+
1423
+ for (const contract of contracts) {
1424
+ const difference = this.#describeContractDifference(first, contract, nameOf);
1425
+
1426
+ if (difference !== null) {
1427
+ refuse(first.target, contract.target, difference);
1428
+ }
1429
+ }
1430
+
1431
+ const receiver = isBound
1432
+ ? { node: node.callee, type: firstTarget.receiverType, isNullable: false }
1433
+ : (takesReceiver ? { node: node.arguments[0], type: receiverArgument.type.toNonNullable(), isNullable: receiverArgument.type.isNullable || receiverArgument.type.kind === 'null' } : null);
1434
+ const resolved = this.#resolveInvocation(first.declaration, node, pointer, scope, receiver, written);
1435
+ const alternatives = contracts.map(({ declaration, typeArguments }) => ({
1436
+ declaration,
1437
+ type: this.#registry.getDeclaredType(declaration, declaration.result).resolveTypeParameters(typeArguments),
1438
+ parameterNames: declaration.parameters.map(parameter => parameter.name),
1439
+ }));
1440
+ const merged = alternatives.reduce((type, alternative) => (type === null ? null : this.#typeChecker.merge(type, alternative.type)), Type.never);
1441
+
1442
+ if (merged === null) {
1443
+ this.#fail('NEXO3070', `'${written}' may be any of ${alternatives.length} functions, whose results are of no one type: ${alternatives.map(alternative => alternative.type).join(', ')}`, pointer);
1444
+ }
1445
+
1446
+ // The callee is the local the call reads its function reference from, which evaluation invokes the declaration of, its arguments named by that declaration's parameters.
1447
+ this.#resolutions.set(pointer, { ...resolved.resolution, alternatives, ...(isBound ? { readsBoundReceiver: true } : {}) });
1448
+
1449
+ return { type: resolved.result.type.isNullable ? merged.toNullable() : merged, kind: 'value', isFixed: false };
1450
+ }
1451
+
1452
+ /**
1453
+ * Where two functions a local may hold read a call's arguments differently, said for a message, or null where their contracts agree: their argument forms, each parameter's name, where the options form
1454
+ * names it, its kind, its requirement, its null policy, its default and its type, a callback's parameter types among them, once each receiver binds what its type writes; and type parameters of their own, or a
1455
+ * parameter selecting a type, which only one declaration shares with itself.
1456
+ */
1457
+ #describeContractDifference(first, other, nameOf) {
1458
+ const [a, b] = [first.declaration, other.declaration];
1459
+ const [nameA, nameB] = [first.target, other.target].map(nameOf);
1460
+ const ownsTypes = declaration => (declaration.typeParameters ?? []).length > 0 || declaration.parameters.some(parameter => parameter.fixed === true);
1461
+
1462
+ if (a !== b && (ownsTypes(a) || ownsTypes(b))) {
1463
+ return `${ownsTypes(a) ? nameA : nameB} binds type parameters of its own from what it is given`;
1464
+ }
1465
+
1466
+ if ((a.arguments ?? 'positional') !== (b.arguments ?? 'positional') || a.parameters.length !== b.parameters.length) {
1467
+ return `${nameA} takes ${a.parameters.length} arguments, ${a.arguments ?? 'positional'}, and ${nameB} ${b.parameters.length}, ${b.arguments ?? 'positional'}`;
1468
+ }
1469
+
1470
+ const typesOf = (declaration, typeArguments, parameter) => [parameter.type, ...(parameter.parameterTypes ?? [])].map(notation => this.#registry.getDeclaredType(declaration, notation).resolveTypeParameters(typeArguments));
1471
+
1472
+ for (const [index, parameter] of a.parameters.entries()) {
1473
+ const counterpart = b.parameters[index];
1474
+ const typesA = typesOf(a, first.typeArguments, parameter);
1475
+ const typesB = typesOf(b, other.typeArguments, counterpart);
1476
+ const where = `its argument '${parameter.name}'`;
1477
+
1478
+ if (parameter.kind !== counterpart.kind) {
1479
+ return `${nameA} takes ${where} as ${parameter.kind === 'value' ? 'a value' : `an ${parameter.kind}`}, and ${nameB} as ${counterpart.kind === 'value' ? 'a value' : `an ${counterpart.kind}`}`;
1480
+ }
1481
+
1482
+ if (typesA.length !== typesB.length || typesA.some((type, at) => !type.equals(typesB[at]))) {
1483
+ const typeText = ([result, ...parameterTypes]) => (parameter.kind === 'function' ? `a function of ${parameterTypes.join(', ')} answering ${result}` : `${result}`);
1484
+
1485
+ return `${nameA} takes ${where} as ${typeText(typesA)}, and ${nameB} as ${typeText(typesB)}`;
1486
+ }
1487
+
1488
+ const isNamed = (a.arguments ?? 'positional') === 'options' && parameter.name !== counterpart.name;
1489
+ const differs = ['required', 'nulls', 'context'].some(key => parameter[key] !== counterpart[key]) || JSON.stringify(parameter.default) !== JSON.stringify(counterpart.default);
1490
+
1491
+ if (isNamed || differs) {
1492
+ return `${nameA} and ${nameB} declare ${where} apart, by its name, its requirement, its null policy or its default`;
1493
+ }
1494
+ }
1495
+
1496
+ return null;
1497
+ }
1498
+
1499
+ /** Whether the declaration takes its arguments as values alone, by position, which a call of a value that may be one of several functions gives each of them. */
1500
+ static #takesValuesAlone(declaration) {
1501
+ return declaration.arguments !== 'options' && declaration.parameters.every(parameter => parameter.kind === 'value');
1502
+ }
1503
+
1504
+ /**
1505
+ * Resolves a call through a known reference bound to its receiver, the one a local holds: the call its extension access makes, on the receiver the reference captured, which the local holds, its arguments each of the
1506
+ * kind its parameter declares, so `keep(it.length > 1)` after `let keep = words:keep` reads its implicit body as `words:keep(it.length > 1)` does.
1507
+ */
1508
+ #resolveBoundReferenceCall(node, pointer, scope, target) {
1509
+ const declaration = target.reference;
1510
+ const resolved = this.#resolveInvocation(declaration, node, pointer, scope, { node: node.callee, type: target.receiverType, isNullable: false }, this.#qualifiedNameOf(declaration, target.receiverType));
1511
+
1512
+ return this.#storeResolution(pointer, { ...resolved, resolution: { ...resolved.resolution, readsBoundReceiver: true } });
1513
+ }
1514
+
1515
+ /**
1516
+ * Resolves a call of the module function the declaration is, among the functions a function reference may be, from the results of its arguments, which are evaluated as values, as a call of any of them takes them: its receiver argument
1517
+ * first where its declaration has a receiver, then a value for each of its own parameters, each a value parameter its argument fits. It determines their compatibility and answers the reference, its argument map, the result type its implementation answers for this call, its type parameters bound by the receiver,
1518
+ * and the call's type, that result type made nullable where its receiver argument, or an argument whose parameter propagates null, can be null.
1519
+ */
1520
+ #resolveModuleFunctionCall(declaration, node, results, pointer, isBound = false) {
1521
+ const written = this.#referenceNameOf(declaration);
1522
+ const takesReceiver = this.#registry.takesReceiver(declaration);
1523
+ // A bound reference's receiver is the one it captured, which no argument of the call writes, so the call's own arguments are counted from its first.
1524
+ const offset = takesReceiver && !isBound ? 1 : 0;
1525
+ const own = takesReceiver ? results.slice(1) : results;
1526
+ const { parameters } = declaration;
1527
+ const required = parameters.filter(parameter => parameter.required).length;
1528
+
1529
+ if (parameters.some(parameter => parameter.kind !== 'value') || declaration.arguments === 'options') {
1530
+ this.#fail('NEXO3079', `'${node.callee.name}' may hold a reference to '${written}', whose arguments are no values alone, and a call of a value that may be one of several functions gives each values; call it through '${written}(…)'`, pointer);
1531
+ }
1532
+
1533
+ const takes = `${takesReceiver ? 'its receiver and ' : ''}${parameters.length === 1 ? 'one argument' : `${parameters.length} arguments`}`;
1534
+
1535
+ if ((takesReceiver && results.length === 0) || own.length < required) {
1536
+ this.#fail('NEXO3080', `'${node.callee.name}' may hold a reference to '${written}', which takes ${takes}, and is given ${node.arguments.length}`, pointer);
1537
+ }
1538
+
1539
+ if (own.length > parameters.length) {
1540
+ this.#fail('NEXO3058', `'${node.callee.name}' may hold a reference to '${written}', which takes ${takes}, and is given ${node.arguments.length}`, pointer);
1541
+ }
1542
+
1543
+ let isNullable = false;
1544
+ let typeArguments = new Map();
1545
+
1546
+ if (takesReceiver) {
1547
+ const receiverType = results[0].type;
1548
+ const inferred = LanguageResolver.#isNullOrNever(receiverType.toNonNullable()) ? typeArguments : this.#typeChecker.inferReceiverTypeArguments(declaration, receiverType.toNonNullable());
1549
+
1550
+ typeArguments = inferred ?? typeArguments;
1551
+
1552
+ // A reference bound to a value of Any had its receiver checked as it was created, as an extension access on Any is.
1553
+ if (inferred === null && !(isBound && receiverType.kind === 'any')) {
1554
+ this.#fail('NEXO3042', `'${node.callee.name}' may hold a reference to '${written}', which takes a receiver of ${this.#registry.getReceiverTypes(declaration).join(', ')}, and is given ${receiverType.toNonNullable()}`, isBound ? pointer : `${pointer}/arguments/0`);
1555
+ }
1556
+
1557
+ isNullable = receiverType.isNullable || receiverType.kind === 'null';
1558
+ }
1559
+
1560
+ // Its own type parameters are bound by the values it is given, as a call's value arguments bind them.
1561
+ const evidence = new Map();
1562
+
1563
+ own.forEach((result, index) => {
1564
+ if (result.type.kind !== 'function') {
1565
+ this.#typeChecker.extractTypeArgumentEvidence(this.#registry.getDeclaredType(declaration, parameters[index].type), result.type, evidence);
1566
+ }
1567
+ });
1568
+ this.#bindTypeArguments(declaration, evidence, typeArguments, pointer);
1569
+
1570
+ own.forEach((result, index) => {
1571
+ const parameter = parameters[index];
1572
+ const expected = this.#registry.getDeclaredType(declaration, parameter.type).resolveTypeParameters(typeArguments);
1573
+
1574
+ this.#requireArgumentType(result, expected, parameter, declaration, `${pointer}/arguments/${index + offset}`);
1575
+
1576
+ if (LanguageResolver.#propagatesNull(result, parameter)) {
1577
+ isNullable = true;
1578
+ }
1579
+ });
1580
+
1581
+ const declared = this.#registry.getDeclaredType(declaration, declaration.result).resolveTypeParameters(typeArguments);
1582
+
1583
+ return { reference: declaration, type: isNullable ? declared.toNullable() : declared, resultType: declared, argumentMap: LanguageResolver.#createArgumentMap(declaration, own.length) };
1584
+ }
1585
+
1586
+ /**
1587
+ * The argument map of a call of a reference to a module's function given the number of its own arguments, those after its receiver where it has one: the parameter each of them binds, in order, with whether a null it is given makes the call
1588
+ * answer null without invoking the implementation, and the default each parameter left out takes, so evaluation binds the arguments without reading the parameters again.
1589
+ */
1590
+ static #createArgumentMap(declaration, count) {
1591
+ const { parameters } = declaration;
1592
+
1593
+ return {
1594
+ supplied: parameters.slice(0, count).map(parameter => ({ name: parameter.name, propagatesNull: parameter.nulls === 'propagate' })),
1595
+ defaults: parameters.slice(count).map(parameter => ({ name: parameter.name, value: parameter.default })),
1596
+ };
1597
+ }
1598
+
1599
+ /**
1600
+ * Resolves one call of a script function: copies its body for the call, so the same function serves calls of different types, registers the copy under the copy's pointer, where each resolution in it is kept and a failure in it is
1601
+ * located back to the body it copies, and resolves the copy against the scope the function captured
1602
+ * and its parameters, each holding what the call gives it. It refuses a call reaching a function whose body is being resolved, directly or through a function passed to it (NEXO3034), and a body that gives no value
1603
+ * (NEXO3048).
1604
+ */
1605
+ #resolveScriptFunctionCall(target, name, argumentResults, argumentNodes, callerScope, bodyCopyPointer, callPointer) {
1606
+ const { parameters, body } = target.function;
1607
+
1608
+ if (this.#functionsBeingResolved.has(target.function)) {
1609
+ this.#fail('NEXO3034', `'${name}' would call a function within its own call, through a function passed to it: a function never calls itself, directly or through another`, callPointer);
1610
+ }
1611
+
1612
+ // A call whose arguments resolve as an earlier call's did, to equal types and the same fixed values, resolves the body alike, so it shares that call's copy, which this resolution still holds.
1613
+ const resolvedBodies = this.#resolvedBodies.get(target.function) ?? [];
1614
+ const shared = resolvedBodies.find(resolved => resolved.scope === target.scope && this.#functionBodyCopies[resolved.copyIndex]?.node === resolved.body && LanguageResolver.#resolveAlike(resolved.argumentResults, argumentResults));
1615
+
1616
+ if (shared !== undefined) {
1617
+ return { function: target.function, body: shared.body, parameters, type: shared.type };
1618
+ }
1619
+
1620
+ let locals = target.scope.locals;
1621
+
1622
+ parameters.forEach((parameter, index) => {
1623
+ locals = locals.with(parameter, { result: argumentResults[index], value: argumentNodes?.[index] ?? null, scope: callerScope });
1624
+ });
1625
+
1626
+ const bodyCopy = DefinitionValidator.copyOf(body);
1627
+ const copyIndex = this.#functionBodyCopies.length;
1628
+
1629
+ this.#functionBodyCopies.push({ node: bodyCopy, pointer: bodyCopyPointer, bodyPointer: `${target.pointer}/body` });
1630
+ this.#functionsBeingResolved.add(target.function);
1631
+
1632
+ let result;
1633
+
1634
+ try {
1635
+ result = this.#resolveNode(bodyCopy, bodyCopyPointer, { ...target.scope, locals });
1636
+ } finally {
1637
+ this.#functionsBeingResolved.delete(target.function);
1638
+ }
1639
+
1640
+ if (result.kind !== 'value') {
1641
+ this.#fail('NEXO3048', `'${name}' gives ${LanguageResolver.#descriptionOf(result.kind)}, and a function gives a value`, bodyCopyPointer);
1642
+ }
1643
+
1644
+ resolvedBodies.push({ scope: target.scope, argumentResults, body: bodyCopy, copyIndex, type: result.type });
1645
+ this.#resolvedBodies.set(target.function, resolvedBodies);
1646
+
1647
+ return { function: target.function, body: bodyCopy, parameters, type: result.type };
1648
+ }
1649
+
1650
+ /**
1651
+ * The bodies each function a script declares was resolved for, by the function: the scope it captured, the results of the arguments its call gave it, its body's copy, where the copy stands among the body copies, and
1652
+ * its type. A later call of the function in that scope, its arguments resolving alike, shares the copy rather than resolving the body again, so a chain of functions each calling the one before resolves in time
1653
+ * linear in its length. An entry is valid while the body copies hold its copy where it was recorded, which a speculative reading discarded no longer does; it lasts as long as this resolution.
1654
+ * @type {WeakMap<object, { scope: object, argumentResults: object[], body: object, copyIndex: number, type: Type }[]>}
1655
+ */
1656
+ #resolvedBodies = new WeakMap();
1657
+
1658
+ /** Whether two calls' argument results resolve a body alike: each of one kind, of an equal type, and fixed to the same value or neither fixed. */
1659
+ static #resolveAlike(results, others) {
1660
+ return results.length === others.length && results.every((result, index) => {
1661
+ const other = others[index];
1662
+
1663
+ return result.kind === other.kind && result.isFixed === other.isFixed && (!result.isFixed || Object.is(result.value, other.value)) && result.type.equals(other.type);
1664
+ });
1665
+ }
1666
+
1667
+ /** The functions whose bodies are being resolved, each for a call within which none is called again. @type {Set<object>} */
1668
+ #functionsBeingResolved = new Set();
1669
+
1670
+ /** Resolves a local: what the one in scope with its name resolved to where it was written. A name no local in scope has is refused, with the form of the implicit parameter's member of that name where it has one. */
1671
+ #resolveLocal(node, pointer, scope) {
1672
+ const local = scope.locals.get(node.name);
1673
+
1674
+ // A function's name read without a call is a function reference, which a local holds, an argument passes and a function returns.
1675
+ if (local?.function !== undefined) {
1676
+ this.#resolutions.set(pointer, { kind: 'local', name: node.name, initializer: local.initializer });
1677
+
1678
+ return { type: Type.createFunctionType([local]), kind: 'value', isFixed: false };
1679
+ }
1680
+
1681
+ if (local?.optionKind !== undefined) {
1682
+ this.#fail('NEXO3046', LanguageResolver.#unreadableOptionMessageOf(node.name, local.optionKind), pointer);
1683
+ }
1684
+
1685
+ if (local === undefined) {
1686
+ const form = scope.contextType === null ? null : this.#memberForm(scope.contextType, node.name);
1687
+
1688
+ this.#fail('NEXO2012', `No local, object field, conditional binding or function parameter named '${node.name}' is in scope${form === null ? '' : `; write '${form}' for the member of the implicit parameter`}`, pointer);
1689
+ }
1690
+
1691
+ // A conditional binding's value is evaluated once, by its conditional, which holds it in a frame its consequent reads as any local's; each records the expression its value came from.
1692
+ this.#resolutions.set(pointer, { kind: 'local', name: node.name, initializer: local.initializer });
1693
+
1694
+ const narrowed = scope.facts.get(local);
1695
+
1696
+ return narrowed === undefined ? local.result : { ...local.result, type: narrowed };
1697
+ }
1698
+
1699
+ /** The names a property access joins with the bare name a subtraction takes from it, by the access, which a failure to find its member suggests. @type {WeakMap<object, string>} */
1700
+ #joinedNames = new WeakMap();
1701
+
1702
+ /**
1703
+ * For a property access no member is named as, which a subtraction takes a bare name from, the suggestion of the member the two names joined in camelCase name, as `client` and `size` join as `clientSize`,
1704
+ * where the receiver's type has one of that name, compared ignoring case and written as the member spells it; nothing for any other access.
1705
+ */
1706
+ /**
1707
+ * The hint an access on a number literal gives where its name is an exponent's, `1.e3`, which a number writes directly after its digits, `1e3`, since a dot after the digits begins an access; none otherwise, the
1708
+ * name then a member's no module declares.
1709
+ */
1710
+ #exponentHintOf(access) {
1711
+ if (access.receiver.kind !== 'literal' || typeof access.receiver.value !== 'number' || !/^[eE][0-9]*$/.test(access.name)) {
1712
+ return '';
1713
+ }
1714
+
1715
+ const digits = DefinitionWriter.quote(access.receiver, this.#namespaceNames);
1716
+
1717
+ return `: a number's exponent follows its digits directly, as ${digits}${access.name.length === 1 ? `${access.name}3` : access.name} writes it, and a dot after them begins an access`;
1718
+ }
1719
+
1720
+ #joinedNameHintOf(node, receiverType) {
1721
+ const joined = this.#joinedNames.get(node)?.toLowerCase();
1722
+
1723
+ if (joined === undefined || LanguageResolver.#isNullOrNever(receiverType)) {
1724
+ return '';
1725
+ }
1726
+
1727
+ const name = this.#registry.receiverMemberNames.find(candidate => candidate.toLowerCase() === joined && this.#memberForm(receiverType, candidate) !== null);
1728
+
1729
+ if (name === undefined) {
1730
+ return '';
1731
+ }
1732
+
1733
+ // The access written with the member's name, less that name, is its receiver as the access writes it, grouped where it needs to be and ending in `?` where the access is optional.
1734
+ const receiver = DefinitionWriter.quote({ ...node, name }, this.#namespaceNames).slice(0, -(name.length + 1));
1735
+
1736
+ return `. Did you mean '${this.#memberForm(receiverType, name, receiver)}'?`;
1737
+ }
1738
+
1739
+ /** How the member of the type with the name is written after the receiver, `it` unless another is given, or null where the type has none. */
1740
+ #memberForm(type, name, receiver = 'it') {
1741
+ if ((type.kind === 'object' ? type.getObjectFieldType(name) : (this.#registry.getDataShape(type)?.get(name) ?? null)) !== null) {
1742
+ return `${receiver}.${name}`;
1743
+ }
1744
+
1745
+ const selection = this.#memberSelector.select(this.#receiverMembersNamed(name), type);
1746
+
1747
+ if (selection.kind !== 'selected') {
1748
+ return null;
1749
+ }
1750
+
1751
+ const { declaration } = selection;
1752
+
1753
+ if (declaration.kind === 'property') {
1754
+ return `${receiver}.${name}`;
1755
+ }
1756
+
1757
+ // A function taking no argument is written with its empty parentheses, one taking some with the arguments to give.
1758
+ return declaration.kind === 'indexer' ? `${receiver}.${name}[…]` : `${receiver}.${name}(${(declaration.parameters ?? []).length === 0 ? '' : '…'})`;
1759
+ }
1760
+
1761
+ #resolveLiteral(node) {
1762
+ const { value } = node;
1763
+ const type = value === null ? Type.null : Type.createPrimitiveType(value);
1764
+
1765
+ return { type, kind: 'value', isFixed: true, value };
1766
+ }
1767
+
1768
+ #resolveParameter(node, pointer, scope) {
1769
+ if (!this.#bindings.has(node.name) && DefinitionValidator.isWrittenAlone(node) && this.#registry.getModule(node.name) !== undefined && node.name !== Vocabulary.moduleName) {
1770
+ this.#fail('NEXO3003', `'${node.name}' names a registered module, which the script does not import, and no script parameter of that name is bound: write 'import ${node.name}' at the script's start to read its namespace`, pointer);
1771
+ }
1772
+
1773
+ // A name a text wrote alone, which no local in scope has, reads the script parameter of its name, and one the host did not bind may be a member written without its receiver; one written with `~`, or a definition's, names a script parameter alone, which reads no local.
1774
+ if (!this.#bindings.has(node.name) && !DefinitionValidator.isWrittenAlone(node)) {
1775
+ const local = scope.locals.has(node.name) ? `, and '~${node.name}' reads a script parameter alone: write '${Names.needsPrefix(node.name, this.#namespaceNames) ? '@' : ''}${node.name}' for the local, object field, conditional binding or function parameter of that name` : '';
1776
+
1777
+ this.#fail('NEXO3051', `No script parameter is named '${node.name}'${local}`, pointer);
1778
+ }
1779
+
1780
+ if (!this.#bindings.has(node.name)) {
1781
+ const form = scope.contextType === null ? null : this.#memberForm(scope.contextType, node.name);
1782
+
1783
+ this.#fail('NEXO3009', `No local in scope or script parameter is named '${node.name}'${form === null ? '' : `; write '${form}' for the member of the implicit parameter`}`, pointer);
1784
+ }
1785
+
1786
+ return { type: scope.facts.get(`@${node.name}`) ?? this.#bindings.getType(node.name), kind: 'value', isFixed: true, value: this.#bindings.readValue(node.name) };
1787
+ }
1788
+
1789
+ /**
1790
+ * Resolves a property access, a call of a member or an indexer on its receiver, the access's receiver resolved where it stands unless its resolution is given, as an indexer gives the receiver it has resolved already;
1791
+ * the optional form, which a message gives where the receiver can be null, is the node written with it unless another is given. A name of an extension access is read with the access's other names first, unless
1792
+ * it has been; an access selecting an extension selects among its members alone, its selection located at the name of the extension, which the access's reading gives.
1793
+ */
1794
+ #resolveMemberAccess(node, pointer, scope, { receiver: given, optionalForm = LanguageResolver.#asOptional(node), extensionPointer = LanguageResolver.#extensionPointerOf(node, pointer), isRead = false } = {}) {
1795
+ const access = LanguageResolver.#accessOf(node);
1796
+
1797
+ if (!isRead && LanguageResolver.#isWrittenExtensionAccess(access)) {
1798
+ return this.#resolveExtensionNames(node, pointer, scope);
1799
+ }
1800
+
1801
+ // Validation refuses an extension access on a namespace, so one read through a namespace is an ordinary access.
1802
+ if (access.receiver.kind === 'namespace') {
1803
+ return this.#resolveThroughNamespace(node, pointer, scope);
1804
+ }
1805
+
1806
+ const receiver = given ?? this.#resolveNode(access.receiver, LanguageResolver.#receiverPointerOf(node, pointer), scope);
1807
+ const isUnnamedIndexer = node.kind === 'indexerAccess' && node.name === undefined;
1808
+ const written = isUnnamedIndexer ? '[…]' : typeof access.name === 'string' ? access.name : `.[${DefinitionWriter.quote(access.name, this.#namespaceNames)}]`;
1809
+
1810
+ if (receiver.kind !== 'value') {
1811
+ this.#fail('NEXO3048', `'${written}' follows ${LanguageResolver.#descriptionOf(receiver.kind)}, which takes no members`, pointer);
1812
+ }
1813
+
1814
+ // A member requires a receiver that cannot be null, and an optional one reads it where it is not null, ending its chain with null where it is.
1815
+ if (receiver.type.isNullable) {
1816
+ if (access.optional !== true) {
1817
+ const follows = receiver.type.kind === 'null' ? 'null' : `${receiver.type}, which can be null`;
1818
+
1819
+ this.#fail(access.extension === undefined ? 'NEXO3008' : 'NEXO3063', `'${written}' follows ${follows}: write '${DefinitionWriter.quote(optionalForm, this.#namespaceNames)}' to read it where it is not`, pointer);
1820
+ }
1821
+
1822
+ this.#optionalChains.at(-1).shortCircuits = true;
1823
+ }
1824
+
1825
+ const receiverType = access.optional === true ? receiver.type.toNonNullable() : receiver.type;
1826
+
1827
+ if (access.extension !== undefined) {
1828
+ const resolved = this.#resolveThroughExtension(node, pointer, scope, receiverType, extensionPointer);
1829
+
1830
+ // The member an access's names read may be a later name than the one its `?:` is written before, so its resolution says the access is optional.
1831
+ return this.#storeResolution(pointer, access.optional === true ? { ...resolved, resolution: { ...resolved.resolution, isOptional: true } } : resolved);
1832
+ }
1833
+
1834
+ if (isUnnamedIndexer) {
1835
+ return this.#resolveUnnamedIndexerAccess(node, pointer, scope, receiverType);
1836
+ }
1837
+
1838
+ if (typeof access.name !== 'string') {
1839
+ return this.#resolveComputedPropertyAccess(node, pointer, scope, receiverType);
1840
+ }
1841
+
1842
+ return this.#storeResolution(pointer, this.#resolveMemberOnReceiverType(node, pointer, scope, receiverType));
1843
+ }
1844
+
1845
+ /**
1846
+ * Resolves an indexer. Where what is before its brackets is a member access, by a written name, of a receiver whose type has a named indexer of that name, it is that named indexer, the access giving its receiver
1847
+ * and its name and never read alone; once a named indexer applies, a key it refuses is its own error, and never makes the brackets another reading. Optional brackets add nothing to a named indexer, which has
1848
+ * no value between its name and its key, so only its access's own marker makes its receiver optional. Otherwise it is the unnamed indexer of the type what is before its brackets answers, which optional
1849
+ * brackets skip where that is null.
1850
+ */
1851
+ #resolveIndexerAccess(node, pointer, scope, { access = node.receiver, accessReceiver, extensionPointer } = {}) {
1852
+ if (accessReceiver === undefined && LanguageResolver.#isWrittenExtensionAccess(access)) {
1853
+ return this.#resolveExtensionNames(node, pointer, scope);
1854
+ }
1855
+
1856
+ if (access.kind === 'memberAccess' && typeof access.name === 'string' && access.receiver.kind !== 'namespace' && access.extension !== undefined) {
1857
+ return this.#resolveExtensionIndexerAccess(node, pointer, scope, access, accessReceiver, extensionPointer);
1858
+ }
1859
+
1860
+ if (access.kind !== 'memberAccess' || typeof access.name !== 'string' || access.receiver.kind === 'namespace' || access.extension !== undefined) {
1861
+ return this.#resolveMemberAccess(node, pointer, scope);
1862
+ }
1863
+
1864
+ // The access's receiver is resolved once, and given to whichever reading applies, so a chain of indexers resolves each part once; the access is recorded where it stands, since neither reading resolves it as a node alone.
1865
+ const accessPointer = `${pointer}/receiver`;
1866
+
1867
+ this.#pointers.set(node.receiver, accessPointer);
1868
+ const receiver = accessReceiver ?? this.#resolveNode(access.receiver, `${accessPointer}/receiver`, scope);
1869
+ const kind = this.#memberKindNamedBy(access, receiver);
1870
+
1871
+ // A function's arguments are written in parentheses, so brackets after its name are a call written as an indexer.
1872
+ if (kind === 'function') {
1873
+ this.#fail('NEXO3054', `'${access.name}' uses call syntax: write '${DefinitionWriter.quote(LanguageResolver.#createCall(access, [node.argument]), this.#namespaceNames)}'`, pointer);
1874
+ }
1875
+
1876
+ if (kind !== 'indexer') {
1877
+ const accessResult = this.#resolveMemberAccess(access, accessPointer, scope, { receiver, isRead: true });
1878
+
1879
+ return this.#resolveMemberAccess(node, pointer, scope, { receiver: accessResult });
1880
+ }
1881
+
1882
+ // The named indexer's own node, which no definition holds: its receiver the access's, its name the access's, optional where the access is.
1883
+ const named = { kind: 'indexerAccess', receiver: access.receiver, name: access.name, argument: node.argument, ...(access.optional === true ? { optional: true } : {}) };
1884
+ return this.#resolveMemberAccess(named, pointer, scope, { receiver, optionalForm: { ...node, receiver: { ...access, optional: true } } });
1885
+ }
1886
+
1887
+ /**
1888
+ * Resolves brackets after an extension access's member, the access as its names read, given its receiver resolved: where the member the access selects for the receiver, among the extension it names or the
1889
+ * extensions the script's imports make available, is a named indexer, the brackets are its key, the access giving its receiver, its name and its selection and never read alone; and otherwise they index what the
1890
+ * member answers, as brackets after any member's answer do.
1891
+ */
1892
+ #resolveExtensionIndexerAccess(node, pointer, scope, access, accessReceiver, extensionPointer) {
1893
+ const accessPointer = `${pointer}/receiver`;
1894
+ const extension = access.extension.name === undefined ? null : this.#resolveExtension(access.extension, extensionPointer);
1895
+ const candidates = extension === null ? this.#imported.getAvailable(this.#membersReadBy(access)) : this.#registry.getReceiverMembers(access.name).filter(declaration => this.#registry.getContainingType(declaration) === extension);
1896
+ // The member the access selects for its receiver decides how its brackets read, as it does after a dot: a named indexer's key, or a key of what the member answers.
1897
+ const kind = this.#memberKindNamedBy(access, accessReceiver, candidates);
1898
+
1899
+ // A function's arguments are written in parentheses, so brackets after its name are a call written as an indexer.
1900
+ if (kind === 'function') {
1901
+ this.#fail('NEXO3054', `'${access.name}' uses call syntax: write '${DefinitionWriter.quote(LanguageResolver.#createCall(access, [node.argument]), this.#namespaceNames)}'`, pointer);
1902
+ }
1903
+
1904
+ if (kind !== 'indexer') {
1905
+ const accessResult = this.#resolveMemberAccess(access, accessPointer, scope, { receiver: accessReceiver, extensionPointer });
1906
+
1907
+ return this.#resolveMemberAccess(node, pointer, scope, { receiver: accessResult });
1908
+ }
1909
+
1910
+ const named = { kind: 'indexerAccess', receiver: access.receiver, name: access.name, argument: node.argument, extension: access.extension, ...(access.optional === true ? { optional: true } : {}) };
1911
+
1912
+ return this.#resolveMemberAccess(named, pointer, scope, { receiver: accessReceiver, optionalForm: { ...node, receiver: { ...access, optional: true } }, extensionPointer });
1913
+ }
1914
+
1915
+ /**
1916
+ * Resolves an extension access as its names read, and the ordinary accesses of its member's answer its later names write: the node is the access's last name, a call of it, or brackets after it. Its receiver is
1917
+ * resolved once; its names are read as one way their declarations allow, the one way that fits (NEXO3091 where several do); and its member and each ordinary access after it are resolved in turn, each given the
1918
+ * answer before it, the last as the node holds it. Its first names, which name an extension and a module, are read as names alone, and a member's selection is located at the name of the extension it names.
1919
+ */
1920
+ #resolveExtensionNames(node, pointer, scope) {
1921
+ const holder = node.kind === 'call' ? 'callee' : node.kind === 'indexerAccess' ? 'receiver' : null;
1922
+ const names = [];
1923
+
1924
+ // Each name is recorded where it stands, since the access each step resolves in its place is made for the step.
1925
+ for (let access = holder === null ? node : node[holder], at = holder === null ? pointer : `${pointer}/${holder}`; ; access = access.receiver, at = `${at}/receiver`) {
1926
+ this.#pointers.set(access, at);
1927
+ names.unshift({ access, pointer: at });
1928
+
1929
+ if (access.continues !== true) {
1930
+ break;
1931
+ }
1932
+ }
1933
+
1934
+ const [first] = names;
1935
+ const receiverNode = first.access.receiver;
1936
+ const optional = first.access.optional === true ? { optional: true } : {};
1937
+
1938
+ const receiver = this.#resolveNode(receiverNode, `${first.pointer}/receiver`, scope);
1939
+ const receiverType = receiver.kind === 'value' ? receiver.type.toNonNullable() : null;
1940
+ const length = this.#readExtensionNames(node, names.map(name => name.access.name), receiverNode, receiverType, first, pointer);
1941
+ const selection = length === 1 ? {} : length === 2 ? { name: names[0].access.name } : { module: names[0].access.name, name: names[1].access.name };
1942
+ const steps = [
1943
+ { access: { kind: 'memberAccess', receiver: receiverNode, name: names[length - 1].access.name, ...optional, extension: selection }, pointer: names[length - 1].pointer, extensionPointer: length === 1 ? `${first.pointer}/extension` : names[length - 2].pointer },
1944
+ // A later name an ordinary access reads keeps its mark, so a message writes it as the access does.
1945
+ ...names.slice(length).map(name => ({ access: { kind: 'memberAccess', receiver: name.access.receiver, name: name.access.name, continues: true }, pointer: name.pointer })),
1946
+ ];
1947
+ let result = receiver;
1948
+
1949
+ for (const step of steps.slice(0, -1)) {
1950
+ result = this.#resolveMemberAccess(step.access, step.pointer, scope, { receiver: result, extensionPointer: step.extensionPointer, isRead: true });
1951
+ }
1952
+
1953
+ const { access, extensionPointer } = steps.at(-1);
1954
+
1955
+ switch (holder) {
1956
+ case 'callee':
1957
+ return this.#resolveMemberAccess({ ...node, callee: access }, pointer, scope, { receiver: result, extensionPointer, isRead: true });
1958
+ case 'receiver':
1959
+ return this.#resolveIndexerAccess(node, pointer, scope, { access, accessReceiver: result, extensionPointer });
1960
+ default:
1961
+ return this.#resolveMemberAccess(access, pointer, scope, { receiver: result, extensionPointer, isRead: true });
1962
+ }
1963
+ }
1964
+
1965
+ /**
1966
+ * How many of an extension access's names its member is reached by: one, the member, an extension member of its name applying to the receiver; two, the member of the extension the first names; or three, the
1967
+ * member of the extension the second names in the module the first names, a local name the script's imports give a module. Each way is taken where its names name what its roles need, never a local or a
1968
+ * parameter; one that fits is the access's, several are refused as ambiguous (NEXO3091), and where none fits, the access is refused as the way its first name comes closest to reads it.
1969
+ */
1970
+ #readExtensionNames(node, names, receiverNode, receiverType, first, pointer) {
1971
+ const [member, extension, moduleMember] = names;
1972
+ const isKnown = receiverType !== null && !LanguageResolver.#isNullOrNever(receiverType) && receiverType.kind !== 'any';
1973
+ const applies = declaration => !isKnown || this.#typeChecker.resolveApplyingReceiverType(declaration, receiverType) !== undefined;
1974
+ const ways = [];
1975
+
1976
+ const available = this.#memberSelector.getAccessible(this.#imported.getAvailable(this.#membersReadBy({ name: member, extension: {} })));
1977
+
1978
+ if (available.some(applies)) {
1979
+ // The extension of the member the selector takes, where it takes one, which names this way unambiguously.
1980
+ const selection = isKnown ? this.#memberSelector.select(available, receiverType) : null;
1981
+
1982
+ ways.push({ length: 1, extension: selection?.kind === 'selected' ? this.#registry.getContainingType(selection.declaration) : undefined });
1983
+ }
1984
+
1985
+ if (names.length >= 2) {
1986
+ const declaring = this.#findExtensions(member).find(candidate => this.#declaresMember(candidate, extension));
1987
+
1988
+ if (declaring !== undefined) {
1989
+ ways.push({ length: 2, extension: declaring });
1990
+ }
1991
+ }
1992
+
1993
+ if (names.length >= 3) {
1994
+ const declaring = this.#findModuleExtension(member, extension);
1995
+
1996
+ if (declaring !== undefined && this.#declaresMember(declaring, moduleMember)) {
1997
+ ways.push({ length: 3, extension: declaring });
1998
+ }
1999
+ }
2000
+
2001
+ if (ways.length > 1) {
2002
+ this.#refuseAmbiguousNames(node, names, receiverNode, receiverType, first, ways, pointer);
2003
+ }
2004
+
2005
+ if (ways.length === 1) {
2006
+ return ways[0].length;
2007
+ }
2008
+
2009
+ // None fits: a module's name leads to its extension's errors, an extension's anywhere to that extension's, and any other first name, followed by more, to naming none, unless an extension member of it applies.
2010
+ if (names.length >= 3 && this.#imported.getNamespaceModule(member) !== undefined) {
2011
+ return 3;
2012
+ }
2013
+
2014
+ if (names.length >= 2 && (this.#imported.getImportedExtension(member) !== undefined || this.#registry.moduleNames.some(moduleName => this.#registry.getModuleType(moduleName, member)?.kind === 'extension'))) {
2015
+ return 2;
2016
+ }
2017
+
2018
+ if (names.length >= 2 && !this.#memberSelector.getAccessible(this.#membersReadBy({ name: member, extension: {} })).some(applies)) {
2019
+ this.#resolveExtension({ name: member }, first.pointer, isKnown ? `, and no extension member of ${receiverType} has it` : '');
2020
+ }
2021
+
2022
+ return 1;
2023
+ }
2024
+
2025
+ /**
2026
+ * Refuses an extension access its names read more than one way (NEXO3091), giving the text that writes each: for a first name that is the member, a group ending the access at it and the member named through its
2027
+ * extension; for an extension's member, the extension named through its module.
2028
+ */
2029
+ #refuseAmbiguousNames(node, names, receiverNode, receiverType, first, ways, pointer) {
2030
+ const receiver = DefinitionWriter.quote(receiverNode, this.#namespaceNames);
2031
+ const colon = first.access.optional === true ? '?:' : ':';
2032
+ const suffix = node.kind === 'call' ? (node.arguments.length === 0 ? '()' : '(…)') : node.kind === 'indexerAccess' ? '[…]' : '';
2033
+ const after = length => `${names.slice(length).map(name => `.${name}`).join('')}${suffix}`;
2034
+ const written = `${receiver}${colon}${names.join('.')}${suffix}`;
2035
+ const readings = ways.map(({ length, extension }) => {
2036
+ switch (length) {
2037
+ case 1: {
2038
+ const named = extension === undefined ? '' : ` or '${receiver}${colon}${this.#formatExtensionName(extension)}.${names[0]}${after(1)}'`;
2039
+
2040
+ return `as the extension member '${names[0]}'${receiverType === null ? '' : ` of ${receiverType}`}, written '(${receiver}${colon}${names[0]})${after(1)}'${named}`;
2041
+ }
2042
+ case 2:
2043
+ return `as the member '${names[1]}' of the extension '${names[0]}', written '${receiver}${colon}${this.#formatQualifiedExtensionName(extension)}.${names[1]}${after(2)}'`;
2044
+ default:
2045
+ return `as the member '${names[2]}' of the extension '${names[1]}' of the module '${names[0]}', written '(${receiver}${colon}${names.slice(0, 3).join('.')})${after(3)}'`;
2046
+ }
2047
+ });
2048
+
2049
+ this.#fail('NEXO3091', `'${written}' reads ${ways.length} ways, ${readings.join(', or ')}: write the one it means`, pointer);
2050
+ }
2051
+
2052
+ /** The public extensions a name names unqualified, as an extension access reads one: the one a type import gives it, and otherwise those of its name among Nexo's own module and the modules the script imports. */
2053
+ #findExtensions(name) {
2054
+ const imported = this.#imported.getImportedExtension(name);
2055
+
2056
+ if (imported !== undefined) {
2057
+ return [imported];
2058
+ }
2059
+
2060
+ return [Vocabulary.moduleName, ...this.#imported.importedModuleNames].map(moduleName => this.#registry.getModuleType(moduleName, name)).filter(declaration => declaration?.kind === 'extension' && !this.#registry.isInternal(declaration));
2061
+ }
2062
+
2063
+ /** The public extension of the name the module a local name the script's imports give declares, or undefined where the name gives no module or the module declares none. */
2064
+ #findModuleExtension(localName, name) {
2065
+ const moduleName = this.#imported.getNamespaceModule(localName);
2066
+ const declaration = moduleName === undefined ? undefined : this.#registry.getModuleType(moduleName, name);
2067
+
2068
+ return declaration?.kind === 'extension' && !this.#registry.isInternal(declaration) ? declaration : undefined;
2069
+ }
2070
+
2071
+ /** Whether the extension declares a member of the name. */
2072
+ #declaresMember(extension, name) {
2073
+ return this.#registry.getReceiverMembers(name).some(declaration => this.#registry.getContainingType(declaration) === extension);
2074
+ }
2075
+
2076
+ /** An extension's name qualified by the local name its module's import gives, or by its module's own name where the script imports it under none. */
2077
+ #formatQualifiedExtensionName(extension) {
2078
+ const moduleName = this.#registry.getDeclaringModuleName(extension);
2079
+
2080
+ return `${this.#imported.getNamespaceName(moduleName) ?? moduleName}.${extension.name}`;
2081
+ }
2082
+
2083
+ /**
2084
+ * The extension an access selects explicitly, read as a script's type name is, ordinary types taking no part: where its module's local name qualifies it, that module's public extension of the name; otherwise the
2085
+ * extension a type import gives the name, and else the one public extension of the name among Nexo's own and the modules the script imports, several refused with their qualified names (NEXO3061). One only
2086
+ * modules the script does not import declare is refused giving the import (NEXO3005), an internal one as internal (NEXO3023), and a name no reachable extension has as naming none (NEXO3026), with what the caller
2087
+ * adds, saying so where an ordinary type has it.
2088
+ */
2089
+ #resolveExtension(selection, pointer, hint = '') {
2090
+ const written = selection.module === undefined ? selection.name : `${selection.module}.${selection.name}`;
2091
+ const ordinaryType = `; '${written}' is an ordinary type, which no extension access names`;
2092
+
2093
+ if (selection.module !== undefined) {
2094
+ const moduleName = this.#imported.getNamespaceModule(selection.module);
2095
+ const declaration = this.#registry.getModuleType(moduleName, selection.name);
2096
+
2097
+ if (declaration?.kind !== 'extension') {
2098
+ this.#fail('NEXO3026', `'${written}' names no extension${declaration === undefined ? '' : ordinaryType}`, pointer);
2099
+ }
2100
+
2101
+ if (this.#registry.isInternal(declaration)) {
2102
+ this.#failInternal(`The extension '${written}'`, moduleName, pointer);
2103
+ }
2104
+
2105
+ return declaration;
2106
+ }
2107
+
2108
+ const imported = this.#imported.getImportedExtension(selection.name);
2109
+
2110
+ if (imported !== undefined) {
2111
+ return imported;
2112
+ }
2113
+
2114
+ const reached = [Vocabulary.moduleName, ...this.#imported.importedModuleNames].map(moduleName => this.#registry.getModuleType(moduleName, selection.name)).filter(declaration => declaration?.kind === 'extension');
2115
+ const extensions = reached.filter(declaration => !this.#registry.isInternal(declaration));
2116
+
2117
+ if (extensions.length > 1) {
2118
+ this.#fail('NEXO3061', `'${written}' names an extension several modules the script imports declare, so it is named qualified: ${extensions.map(declaration => `'${this.#formatExtensionName(declaration)}'`).join(' or ')}`, pointer);
2119
+ }
2120
+
2121
+ if (extensions.length === 1) {
2122
+ return extensions[0];
2123
+ }
2124
+
2125
+ if (reached.length > 0) {
2126
+ this.#failInternal(`The extension '${written}'`, this.#registry.getDeclaringModuleName(reached[0]), pointer);
2127
+ }
2128
+
2129
+ const unimported = this.#registry.moduleNames.filter(moduleName => {
2130
+ const declaration = this.#registry.getModuleType(moduleName, selection.name);
2131
+
2132
+ return declaration?.kind === 'extension' && !this.#registry.isInternal(declaration) && !this.#imported.importsModule(moduleName);
2133
+ });
2134
+
2135
+ if (unimported.length > 0) {
2136
+ this.#fail('NEXO3005', `The extension '${written}' is declared only by ${unimported.map(module => `'${module}'`).join(' and ')}, which the script does not import: write ${unimported.map(module => `'import ${module}'`).join(' or ')} at the script's start`, pointer);
2137
+ }
2138
+
2139
+ const isOrdinaryType = Vocabulary.ownTypeNames.includes(selection.name) || this.#registry.resolveTypeName(selection.name, this.#imported.typeNameScope).types.length > 0;
2140
+
2141
+ this.#fail('NEXO3026', `'${written}' names no extension the script reaches${hint}${isOrdinaryType ? ordinaryType : ''}`, pointer);
2142
+ }
2143
+
2144
+ /** How the script writes the extension's name: the local name a type import gives it, else qualified by the local name its module's import gives, else its own name for one of Nexo's, and qualified by its module otherwise. */
2145
+ #formatExtensionName(extension) {
2146
+ const moduleName = this.#registry.getDeclaringModuleName(extension);
2147
+ const localName = this.#imported.getLocalNameOf(extension);
2148
+
2149
+ if (localName !== undefined) {
2150
+ return localName;
2151
+ }
2152
+
2153
+ if (moduleName === Vocabulary.moduleName) {
2154
+ return extension.name;
2155
+ }
2156
+
2157
+ return `${this.#imported.getNamespaceName(moduleName) ?? moduleName}.${extension.name}`;
2158
+ }
2159
+
2160
+ /**
2161
+ * Resolves an extension access on a receiver of the type, its null policy applied. Naming no extension, it selects among the extension members of its name the script's imports make available, the most specific
2162
+ * extension's; naming one, among that extension's members of its name alone. A member none of them declares is refused (NEXO3062), never falling through to the owner's members or to
2163
+ * another extension's. A function read without a call is a reference bound to its receiver. It answers the result, the resolution and the use, which the caller stores.
2164
+ */
2165
+ #resolveThroughExtension(node, pointer, scope, receiverType, extensionPointer) {
2166
+ return LanguageResolver.#accessOf(node).extension.name === undefined
2167
+ ? this.#resolveExtensionMember(node, pointer, scope, receiverType)
2168
+ : this.#resolveThroughNamedExtension(node, pointer, scope, receiverType, extensionPointer);
2169
+ }
2170
+
2171
+ /**
2172
+ * Resolves an extension access naming no extension: the selector chooses among the extension members of its name the script's imports make available that apply to the receiver, the most specific, several none of which
2173
+ * is more specific refused as ambiguous (NEXO3024), giving the access naming each extension; one only modules the script does not import declare refused giving the import (NEXO3005), and none at all (NEXO3062),
2174
+ * giving the owner's member of its name where one applies. A receiver of unknown type chooses none, and a value of Any selects among them as it is evaluated, as ordinary access does.
2175
+ */
2176
+ #resolveExtensionMember(node, pointer, scope, receiverType) {
2177
+ const access = LanguageResolver.#accessOf(node);
2178
+ const declared = this.#membersReadBy(access);
2179
+
2180
+ if (LanguageResolver.#isNullOrNever(receiverType)) {
2181
+ return this.#resolveOnUnknownType(node, pointer, scope, receiverType, declared);
2182
+ }
2183
+
2184
+ if (receiverType.kind === 'any') {
2185
+ return this.#resolveDeferredAccess(node, pointer, scope);
2186
+ }
2187
+
2188
+ const selection = this.#memberSelector.select(this.#imported.getAvailable(declared), receiverType);
2189
+ const subject = `'${access.name}' on ${receiverType.toNonNullable()}`;
2190
+
2191
+ if (selection.kind === 'none') {
2192
+ this.#refuseUnimported(declared, receiverType, subject, pointer);
2193
+
2194
+ const form = this.#memberForm(receiverType, access.name, DefinitionWriter.quote(access.receiver, this.#namespaceNames));
2195
+
2196
+ this.#fail('NEXO3062', `No extension the script reaches declares '${access.name}' for ${receiverType.toNonNullable()}${form === null ? '' : `: write '${form}' for its owner's member`}`, pointer);
2197
+ }
2198
+
2199
+ if (selection.kind === 'internal') {
2200
+ this.#failInternal(subject, this.#registry.getDeclaringModuleName(selection.declarations[0]), pointer);
2201
+ }
2202
+
2203
+ if (selection.kind === 'ambiguous') {
2204
+ this.#fail('NEXO3024', `'${access.name}' ${this.#formatCompetingDeclarations(selection.declarations, receiverType, access.receiver)}`, pointer);
2205
+ }
2206
+
2207
+ const { declaration } = selection;
2208
+ const name = this.#qualifiedNameOf(declaration, receiverType);
2209
+
2210
+ if (node.kind === 'memberAccess' && declaration.kind === 'function') {
2211
+ return this.#resolveBoundFunctionReference(declaration, node, pointer, receiverType, name);
2212
+ }
2213
+
2214
+ this.#requireDeclaredForm(declaration, node, pointer);
2215
+
2216
+ return this.#resolveInvocation(declaration, node, pointer, scope, { node: access.receiver, type: receiverType, isNullable: receiverType.isNullable }, name);
2217
+ }
2218
+
2219
+ /**
2220
+ * Resolves an extension access naming its extension: among the extension's members of the access's name and form alone, a member it does not declare refused (NEXO3062). The selector chooses for the receiver, one
2221
+ * outside the extension's target refused (NEXO3027); a receiver of unknown type chooses none, as it does in ordinary lookup; a value of Any invokes the one member the extension declares, its receiver checked against
2222
+ * the target as it is evaluated.
2223
+ */
2224
+ #resolveThroughNamedExtension(node, pointer, scope, receiverType, extensionPointer) {
2225
+ const access = LanguageResolver.#accessOf(node);
2226
+ const extension = this.#resolveExtension(access.extension, extensionPointer);
2227
+ // A message names the extension as the access writes it.
2228
+ const written = access.extension.module === undefined ? access.extension.name : `${access.extension.module}.${access.extension.name}`;
2229
+ const declared = this.#registry.getReceiverMembers(access.name).filter(declaration => this.#registry.getContainingType(declaration) === extension);
2230
+
2231
+ if (declared.length === 0) {
2232
+ this.#fail('NEXO3062', `The extension '${written}' declares no member '${access.name}'`, pointer);
2233
+ }
2234
+
2235
+ if (LanguageResolver.#isNullOrNever(receiverType)) {
2236
+ return this.#resolveOnUnknownType(node, pointer, scope, receiverType, declared);
2237
+ }
2238
+
2239
+ const isAny = receiverType.kind === 'any';
2240
+ const selection = isAny ? { kind: 'selected', declaration: declared[0] } : this.#memberSelector.select(declared, receiverType);
2241
+
2242
+ if (selection.kind === 'internal' || (isAny && this.#memberSelector.getAccessible(declared).length === 0)) {
2243
+ this.#failInternal(`'${written}.${access.name}'`, this.#registry.getDeclaringModuleName(extension), pointer);
2244
+ }
2245
+
2246
+ if (selection.kind !== 'selected') {
2247
+ this.#fail('NEXO3027', `The extension '${written}' extends ${this.#registry.getReceiverTypes(declared[0]).join(', ')}, and ${receiverType.toNonNullable()} does not satisfy it`, pointer);
2248
+ }
2249
+
2250
+ const { declaration } = selection;
2251
+ const name = `${written}.${declaration.name}`;
2252
+
2253
+ if (node.kind === 'memberAccess' && declaration.kind === 'function') {
2254
+ return this.#resolveBoundFunctionReference(declaration, node, pointer, receiverType, name);
2255
+ }
2256
+
2257
+ this.#requireDeclaredForm(declaration, node, pointer);
2258
+
2259
+ const resolved = this.#resolveInvocation(declaration, node, pointer, scope, { node: access.receiver, type: receiverType, isNullable: receiverType.isNullable }, name);
2260
+
2261
+ // A value of Any is checked against the extension's target as the access evaluates it, which no resolution can do before.
2262
+ return isAny ? { ...resolved, resolution: { ...resolved.resolution, checksReceiver: true } } : resolved;
2263
+ }
2264
+
2265
+ /**
2266
+ * Resolves a function an extension access reads without a call: a reference bound to its receiver, which evaluation evaluates and captures once, as it creates the reference, and which takes the function's own arguments
2267
+ * alone, its receiver's type binding the type parameters its extension's target writes. A host call has none, since its host invokes it (NEXO3076).
2268
+ */
2269
+ #resolveBoundFunctionReference(declaration, node, pointer, receiverType, name) {
2270
+ if (NexoModule.isInvokedByHost(declaration)) {
2271
+ this.#fail('NEXO3076', `'${name}' is a host call, which its host invokes, so it has no reference`, pointer);
2272
+ }
2273
+
2274
+ const type = Type.createFunctionType([Object.freeze({ reference: declaration, isBound: true, receiverType: receiverType.toNonNullable() })]);
2275
+ const resolution = { kind: 'boundFunctionReference', declaration, receiverNode: node.receiver, ...(receiverType.kind === 'any' ? { checksReceiver: true } : {}) };
2276
+
2277
+ return { result: { type, kind: 'value', isFixed: false }, resolution, use: { declaration, qualifiedName: name, pointer } };
2278
+ }
2279
+
2280
+ /**
2281
+ * The kind of the member the access names on its receiver's type, `indexer` for a named indexer and `function` for a function, among the declarations of its name for that type, or, on a receiver of unknown type,
2282
+ * `indexer` where any declaration of its name is a named indexer; null where none is either, as a property's or a field's name is, given the access's receiver as it resolved. It decides how brackets after the access read.
2283
+ */
2284
+ #memberKindNamedBy(access, receiver, candidates = this.#receiverMembersNamed(access.name)) {
2285
+ const declarations = candidates.filter(declaration => declaration.kind !== 'property');
2286
+
2287
+ if (declarations.length === 0 || receiver.kind !== 'value') {
2288
+ return null;
2289
+ }
2290
+
2291
+ const receiverType = access.optional === true ? receiver.type.toNonNullable() : receiver.type;
2292
+
2293
+ if (LanguageResolver.#isNullOrNever(receiverType) || receiverType.kind === 'any') {
2294
+ return declarations.some(declaration => declaration.kind === 'indexer') ? 'indexer' : null;
2295
+ }
2296
+
2297
+ // An extension access reads extension members alone, never an object's field or a structure's data.
2298
+ if (access.extension === undefined && (receiverType.kind === 'objectRoot' || (receiverType.kind === 'object' ? receiverType.getObjectFieldType(access.name) : (this.#registry.getDataShape(receiverType)?.get(access.name) ?? null)) !== null)) {
2299
+ return null;
2300
+ }
2301
+
2302
+ // The member of the name the receiver selects decides, a property among them, which hides an indexer of its name above it.
2303
+ const selection = this.#memberSelector.select(candidates, receiverType);
2304
+
2305
+ return selection.kind !== 'selected' || selection.declaration.kind === 'property' ? null : selection.declaration.kind;
2306
+ }
2307
+
2308
+ /**
2309
+ * Resolves an unnamed indexer: the one indexer declared without a name for the receiver's type, resolved as an invocation of it given its key.
2310
+ * A receiver of unknown type chooses none, and takes the key every unnamed indexer could; a receiver whose type has none is refused, with the computed property access its key may mean.
2311
+ */
2312
+ #resolveUnnamedIndexerAccess(node, pointer, scope, receiverType) {
2313
+ const unnamedIndexers = this.#registry.getUnnamedIndexers();
2314
+
2315
+ if (LanguageResolver.#isNullOrNever(receiverType)) {
2316
+ return this.#storeResolution(pointer, this.#resolveOnUnknownType(node, pointer, scope, receiverType, unnamedIndexers));
2317
+ }
2318
+
2319
+ if (receiverType.kind === 'any') {
2320
+ return this.#storeResolution(pointer, this.#resolveDeferredAccess(node, pointer, scope));
2321
+ }
2322
+
2323
+ if (receiverType.kind === 'objectRoot') {
2324
+ return this.#storeResolution(pointer, this.#resolveObjectRootField(node, pointer, receiverType));
2325
+ }
2326
+
2327
+ const selection = this.#memberSelector.select(this.#imported.getAvailable(unnamedIndexers), receiverType);
2328
+
2329
+ if (selection.kind === 'none') {
2330
+ this.#refuseUnimported(unnamedIndexers, receiverType, `The unnamed indexer of ${receiverType.toNonNullable()}`, pointer);
2331
+ }
2332
+
2333
+ if (selection.kind === 'internal') {
2334
+ this.#failInternal(`The unnamed indexer of ${receiverType.toNonNullable()}`, this.#registry.getDeclaringModuleName(selection.declarations[0]), pointer);
2335
+ }
2336
+
2337
+ if (selection.kind === 'none') {
2338
+ // A string key may name a property of the receiver, which a computed property access reads.
2339
+ const key = node.argument.kind === 'literal' ? node.argument.value : null;
2340
+ const isNamed = typeof key === 'string' && ((receiverType.kind === 'object' ? receiverType.getObjectFieldType(key) : (this.#registry.getDataShape(receiverType)?.get(key) ?? null)) !== null || this.#receiverMembersNamed(key).some(declaration => declaration.kind === 'property' && this.#typeChecker.matchesReceiver(declaration, receiverType)));
2341
+ const computed = isNamed ? DefinitionWriter.quote({ kind: 'memberAccess', receiver: node.receiver, name: node.argument, ...(node.optional === true ? { optional: true } : {}) }, this.#namespaceNames) : null;
2342
+
2343
+ this.#fail('NEXO3012', `${receiverType.toNonNullable()} has no unnamed indexer${computed === null ? '' : `: write '${computed}' for a property read by a computed name`}`, pointer);
2344
+ }
2345
+
2346
+ if (selection.kind === 'ambiguous') {
2347
+ this.#fail('NEXO3024', `The unnamed indexers ${this.#formatCompetingDeclarations(selection.declarations, receiverType, node.receiver)}`, pointer);
2348
+ }
2349
+
2350
+ const { declaration } = selection;
2351
+ const receiver = { node: node.receiver, type: receiverType, isNullable: receiverType.isNullable };
2352
+
2353
+ return this.#storeResolution(pointer, this.#resolveInvocation(declaration, node, pointer, scope, receiver, this.#qualifiedNameOf(declaration, receiverType)));
2354
+ }
2355
+
2356
+ /**
2357
+ * Resolves a computed property access: the names its expression can give, which preparation determines, each a property of the receiver, their types of one type as a conditional's branches are.
2358
+ * The resolution keeps each name's, so evaluation reads the one the name selects; on a receiver of unknown type, whether each is a property and whether their types agree are checked once its type is known.
2359
+ * Each name's use is recorded as soon as the name is resolved, after any its resolution records, and the access stores its own resolution alone.
2360
+ */
2361
+ #resolveComputedPropertyAccess(node, pointer, scope, receiverType) {
2362
+ const namePointer = `${pointer}/name`;
2363
+
2364
+ // A number names no property, and is the key an unnamed indexer takes.
2365
+ if (node.name.kind === 'literal' && typeof node.name.value === 'number') {
2366
+ this.#fail('NEXO3055', `A computed property access reads a property by its name, a string, and a number is an indexer's key: write '${DefinitionWriter.quote({ kind: 'indexerAccess', receiver: node.receiver, argument: node.name, ...(node.optional === true ? { optional: true } : {}) }, this.#namespaceNames)}'`, namePointer);
2367
+ }
2368
+ const name = this.#resolveNode(node.name, namePointer, scope);
2369
+ const possible = name.kind === 'value' ? this.#possibleNamesOf(node.name, scope) : null;
2370
+
2371
+ if (possible === null) {
2372
+ this.#fail('NEXO3014', `A computed property access reads a name determined when the expression is resolved, which a string literal, null, a script parameter, a local or a conditional binding holding one, or a conditional, a '??', an 'and' or an 'or' of them gives, and '${DefinitionWriter.quote(node.name, this.#namespaceNames)}' is none of them`, namePointer);
2373
+ }
2374
+
2375
+ if (possible.canBeOther) {
2376
+ this.#fail('NEXO3013', `A computed property access reads a string, and '${DefinitionWriter.quote(node.name, this.#namespaceNames)}' can be a value that is not one`, namePointer);
2377
+ }
2378
+
2379
+ if (possible.canBeFalse) {
2380
+ this.#fail('NEXO3013', `A computed property access reads a string, and '${DefinitionWriter.quote(node.name, this.#namespaceNames)}' can be false`, namePointer);
2381
+ }
2382
+
2383
+ if (possible.canBeNull || possible.names.size === 0) {
2384
+ this.#fail('NEXO3013', `A computed property access reads a string, and '${DefinitionWriter.quote(node.name, this.#namespaceNames)}' can be null`, namePointer);
2385
+ }
2386
+
2387
+ const resolutionsByName = new Map();
2388
+ const types = [];
2389
+
2390
+ for (const candidate of possible.names) {
2391
+ const access = { kind: 'memberAccess', receiver: node.receiver, name: candidate, ...(node.optional === true ? { optional: true } : {}) };
2392
+ const { result, resolution, use } = this.#resolveMemberOnReceiverType(access, pointer, scope, receiverType, true);
2393
+
2394
+ types.push(result.type);
2395
+ resolutionsByName.set(candidate, resolution);
2396
+
2397
+ if (use !== null) {
2398
+ this.#usedDeclarations.push(use);
2399
+ }
2400
+ }
2401
+
2402
+ let type = types.reduce((merged, candidateType) => (merged === null ? null : this.#typeChecker.merge(merged, candidateType)), Type.never);
2403
+
2404
+ if (type === null) {
2405
+ if (!LanguageResolver.#isNullOrNever(receiverType)) {
2406
+ this.#fail('NEXO3015', `The properties '${DefinitionWriter.quote(node.name, this.#namespaceNames)}' can name are of one type, and these are ${types.join(', ')}`, namePointer);
2407
+ }
2408
+
2409
+ type = Type.null;
2410
+ }
2411
+
2412
+ this.#resolutions.set(pointer, { kind: 'computedPropertyAccess', resolutionsByName });
2413
+
2414
+ return { type, kind: 'value', isFixed: false };
2415
+ }
2416
+
2417
+ /**
2418
+ * The values a computed property access's expression can give, determined through these forms alone: a string literal, null, a bound parameter, a local whose value is one of them, a conditional binding's name, truthy, whose value is,
2419
+ * and a conditional, a `??`, an `and` and an `or` of them, each by the rules that evaluate it: a conditional gives its branches', a `??` its value's and, where its value can be null, its fallback's, and an `and` or an `or` the part of each operand before its last that decides it, falsey for `and` and truthy for `or`, and its last's.
2420
+ * A condition or an operand whose value is fixed decides when the expression is resolved, so what evaluation would not reach gives nothing; the falsey part of an `and`'s operand that names nothing is told from its type.
2421
+ * The names are the strings it can give, and null, false and any other value are tracked beside them, so a `??` can remove null; any other expression gives no values that can be determined, and answers null.
2422
+ * @returns {{ names: Set<string>, canBeNull: boolean, canBeFalse: boolean, canBeOther: boolean } | null}
2423
+ */
2424
+ #possibleNamesOf(node, scope) {
2425
+ switch (node.kind) {
2426
+ case 'literal':
2427
+ return LanguageResolver.#possibleOf(node.value);
2428
+ case 'scriptParameter':
2429
+ return LanguageResolver.#possibleOf(this.#bindings.readValue(node.name));
2430
+ case 'local': {
2431
+ // A function parameter and a function a script declares hold no initializer whose values can be determined.
2432
+ const local = scope.locals.get(node.name);
2433
+ const possible = local?.initializer === undefined ? null : this.#possibleNamesOf(local.initializer, local.scope);
2434
+
2435
+ // A conditional binding's name is used where its value is truthy.
2436
+ return possible !== null && local.conditional !== undefined ? LanguageResolver.#partOfPossible(possible, true) : possible;
2437
+ }
2438
+ case 'conditional': {
2439
+ const tested = this.#decidingResults.get(node.binding === undefined ? node.condition : node.binding.value);
2440
+ const consequentScope = node.binding === undefined ? scope : LanguageResolver.#consequentScopeOf(node, scope, null);
2441
+ const branches = [[node.consequent, consequentScope], [node.alternative, scope]];
2442
+ const reached = tested?.isFixed ? [branches[LanguageResolver.#isTruthy(tested.value) ? 0 : 1]] : branches;
2443
+ const sides = reached.map(([branch, branchScope]) => this.#possibleNamesOf(branch, branchScope));
2444
+
2445
+ return sides.includes(null) ? null : sides.reduce(LanguageResolver.#unionOfPossible);
2446
+ }
2447
+ case 'coalescing': {
2448
+ const value = this.#possibleNamesOf(node.value, scope);
2449
+
2450
+ if (value === null || !value.canBeNull) {
2451
+ return value;
2452
+ }
2453
+
2454
+ const fallback = this.#possibleNamesOf(node.fallback, scope);
2455
+
2456
+ return fallback === null ? null : LanguageResolver.#unionOfPossible({ ...value, canBeNull: false }, fallback);
2457
+ }
2458
+ case 'and':
2459
+ case 'or': {
2460
+ const decidesWhenTruthy = node.kind === 'or';
2461
+ const last = node.operands.length - 1;
2462
+ let possible = LanguageResolver.#possibleOf(undefined);
2463
+
2464
+ for (let index = 0; index < last; index++) {
2465
+ const operand = node.operands[index];
2466
+ const result = this.#decidingResults.get(operand);
2467
+
2468
+ // A fixed operand decides, giving its value, or passes evaluation on, giving nothing.
2469
+ if (result?.isFixed) {
2470
+ if (LanguageResolver.#isTruthy(result.value) === decidesWhenTruthy) {
2471
+ return LanguageResolver.#unionOfPossible(possible, LanguageResolver.#possibleOf(result.value));
2472
+ }
2473
+
2474
+ continue;
2475
+ }
2476
+
2477
+ const operandPossible = this.#possibleNamesOf(operand, scope);
2478
+
2479
+ if (operandPossible !== null) {
2480
+ possible = LanguageResolver.#unionOfPossible(possible, LanguageResolver.#partOfPossible(operandPossible, decidesWhenTruthy));
2481
+ } else if (!decidesWhenTruthy && result !== undefined) {
2482
+ possible = LanguageResolver.#unionOfPossible(possible, LanguageResolver.#falseyPossibleOf(LanguageResolver.#valuesWhere(operand, result, false)));
2483
+ } else {
2484
+ return null;
2485
+ }
2486
+ }
2487
+
2488
+ const lastPossible = this.#possibleNamesOf(node.operands[last], scope);
2489
+
2490
+ return lastPossible === null ? null : LanguageResolver.#unionOfPossible(possible, lastPossible);
2491
+ }
2492
+ default:
2493
+ return null;
2494
+ }
2495
+ }
2496
+
2497
+ /** The values a value gives: a string, its name, null, false, or any other value; undefined gives none. */
2498
+ static #possibleOf(value) {
2499
+ return {
2500
+ names: typeof value === 'string' ? new Set([value]) : new Set(),
2501
+ canBeNull: value === null,
2502
+ canBeFalse: value === false,
2503
+ canBeOther: value !== undefined && value !== null && value !== false && typeof value !== 'string',
2504
+ };
2505
+ }
2506
+
2507
+ /** The values two sets of them give together. */
2508
+ static #unionOfPossible(first, second) {
2509
+ return {
2510
+ names: new Set([...first.names, ...second.names]),
2511
+ canBeNull: first.canBeNull || second.canBeNull,
2512
+ canBeFalse: first.canBeFalse || second.canBeFalse,
2513
+ canBeOther: first.canBeOther || second.canBeOther,
2514
+ };
2515
+ }
2516
+
2517
+ /** The truthy part of the values, its names and any other value, or the falsey part, null and false. */
2518
+ static #partOfPossible(possible, isTruthy) {
2519
+ return isTruthy
2520
+ ? { names: possible.names, canBeNull: false, canBeFalse: false, canBeOther: possible.canBeOther }
2521
+ : { names: new Set(), canBeNull: possible.canBeNull, canBeFalse: possible.canBeFalse, canBeOther: false };
2522
+ }
2523
+
2524
+ /** The falsey values a value can give, of its falsey values' types, which no name is: false where one is a Boolean or of `Any`, and null where one can be null. */
2525
+ static #falseyPossibleOf(values) {
2526
+ return {
2527
+ names: new Set(),
2528
+ canBeNull: values.some(({ type }) => type.kind === 'null' || type.isNullable),
2529
+ canBeFalse: values.some(({ type }) => type.kind === 'any' || (type.kind === 'named' && type.name === 'Boolean')),
2530
+ canBeOther: false,
2531
+ };
2532
+ }
2533
+
2534
+ /**
2535
+ * Resolves the member the node names on a receiver of the type, which its receiver's null policy has already been applied to, the name one a computed property access settled to where it is computed. It answers the
2536
+ * access's result, its resolution and the use of its declaration, none for a stored value or a receiver of unknown type, and records neither; resolving its arguments records theirs, as resolving any node does.
2537
+ */
2538
+ #resolveMemberOnReceiverType(node, pointer, scope, receiverType, isComputed = false) {
2539
+ const access = LanguageResolver.#accessOf(node);
2540
+
2541
+ if (LanguageResolver.#isNullOrNever(receiverType)) {
2542
+ return this.#resolveOnUnknownType(node, pointer, scope, receiverType);
2543
+ }
2544
+
2545
+ if (receiverType.kind === 'any') {
2546
+ return this.#resolveDeferredAccess(node, pointer, scope);
2547
+ }
2548
+
2549
+ if (receiverType.kind === 'objectRoot') {
2550
+ return this.#resolveObjectRootField(node, pointer, receiverType);
2551
+ }
2552
+
2553
+ // An object type's object field is read by its name, as the object expression or the binding that gave the object holds it.
2554
+ const fieldType = receiverType.getObjectFieldType(access.name);
2555
+
2556
+ if (fieldType !== null) {
2557
+ this.#requireReadWithoutArgument(node, pointer, `an object field of ${receiverType.toNonNullable()}`);
2558
+
2559
+ return { result: { type: receiverType.isNullable ? fieldType.toNullable() : fieldType, kind: 'value', isFixed: false }, resolution: { kind: 'objectField', name: access.name }, use: null };
2560
+ }
2561
+
2562
+ const declared = this.#membersReadBy(access);
2563
+
2564
+ if (declared.length === 0) {
2565
+ this.#refuseExtensionMemberRead(node, pointer, receiverType, isComputed);
2566
+ this.#fail(isComputed ? 'NEXO3056' : 'NEXO3001', `'${access.name}' is no member of the vocabulary${isComputed ? '' : this.#exponentHintOf(access)}${this.#joinedNameHintOf(node, receiverType)}`, pointer);
2567
+ }
2568
+
2569
+ const candidates = this.#imported.getAvailable(declared);
2570
+ const selection = this.#memberSelector.select(candidates, receiverType);
2571
+
2572
+ if (selection.kind === 'none') {
2573
+ this.#refuseExtensionMemberRead(node, pointer, receiverType, isComputed);
2574
+ this.#refuseUnimported(declared, receiverType, `'${access.name}' on ${receiverType.toNonNullable()}`, pointer);
2575
+ }
2576
+
2577
+ if (selection.kind === 'internal') {
2578
+ this.#failInternal(`'${access.name}' on ${receiverType.toNonNullable()}`, this.#registry.getDeclaringModuleName(selection.declarations[0]), pointer);
2579
+ }
2580
+
2581
+ if (selection.kind === 'none') {
2582
+ const reached = this.#memberSelector.getAccessible(candidates);
2583
+ const types = reached.flatMap(candidate => this.#registry.getReceiverTypes(candidate));
2584
+
2585
+ this.#fail(isComputed ? 'NEXO3056' : 'NEXO3001', `'${access.name}' is not available on ${receiverType.toNonNullable()}${types.length === 0 ? '' : `; it is available on ${types.join(', ')}`}`, pointer);
2586
+ }
2587
+
2588
+ if (selection.kind === 'ambiguous') {
2589
+ this.#fail('NEXO3024', `'${access.name}' ${this.#formatCompetingDeclarations(selection.declarations, receiverType, access.receiver)}`, pointer);
2590
+ }
2591
+
2592
+ const { declaration } = selection;
2593
+
2594
+ // A property of a structured type's data shape is read from the data, never invoked, and is used as every declaration selected is.
2595
+ if (this.#registry.isDataProperty(declaration)) {
2596
+ this.#requireReadWithoutArgument(node, pointer, `a property of ${receiverType.toNonNullable()}'s data`);
2597
+
2598
+ const type = this.#registry.getDeclaredType(declaration, declaration.result);
2599
+ const result = { type: receiverType.isNullable ? type.toNullable() : type, kind: 'value', isFixed: false };
2600
+
2601
+ return { result, resolution: { kind: 'dataProperty', declaration, name: access.name }, use: { declaration, qualifiedName: this.#qualifiedNameOf(declaration, receiverType), pointer } };
2602
+ }
2603
+
2604
+ this.#requireDeclaredForm(declaration, node, pointer, isComputed);
2605
+
2606
+ const receiver = { node: access.receiver, type: receiverType, isNullable: receiverType.isNullable };
2607
+
2608
+ return this.#resolveInvocation(declaration, node, pointer, scope, receiver, this.#qualifiedNameOf(declaration, receiverType));
2609
+ }
2610
+
2611
+ /**
2612
+ * Refuses an extension member read by a written name after a dot, where no member `.` reads applies to the receiver and an extension member of its name, accessible to scripts, does (NEXO3090), giving the
2613
+ * extension access to write, whether or not the script imports its module, which that access then asks for.
2614
+ */
2615
+ #refuseExtensionMemberRead(node, pointer, receiverType, isComputed) {
2616
+ const access = LanguageResolver.#accessOf(node);
2617
+ const extensionMembers = this.#memberSelector.getAccessible(this.#membersReadBy({ name: access.name, extension: {} }));
2618
+
2619
+ if (isComputed || !extensionMembers.some(declaration => this.#typeChecker.resolveApplyingReceiverType(declaration, receiverType) !== undefined)) {
2620
+ return;
2621
+ }
2622
+
2623
+ const extensionAccess = node.kind === 'call' ? { ...node, callee: { ...access, extension: {} } } : { ...access, extension: {} };
2624
+
2625
+ this.#fail('NEXO3090', `'${access.name}' is an extension member, which ':' reads: write '${DefinitionWriter.quote(extensionAccess, this.#namespaceNames)}'`, pointer);
2626
+ }
2627
+
2628
+ /**
2629
+ * Resolves an access on a value of Any, whose member is selected when that value is evaluated, by its actual type, through the selector, as an access its receiver's type selects during resolution is: a property read
2630
+ * whatever its name, since a plain object's fields are named by its data, and a call or an indexer of a member some accessible declaration of its name, or an unnamed indexer, takes in that form. Its arguments are read
2631
+ * without the declaration, each a value, never an implicit body or a function, whose reading would depend on the declaration (NEXO3029). It answers a value of Any, and records no use, since it chooses no declaration
2632
+ * before it is evaluated; its resolution names the access, null for an unnamed indexer, and keeps its candidates, which the resolution reports. One whose every candidate is a host call is refused (NEXO3092).
2633
+ */
2634
+ #resolveDeferredAccess(node, pointer, scope) {
2635
+ const access = LanguageResolver.#accessOf(node);
2636
+ const isUnnamedIndexer = node.kind === 'indexerAccess' && access.name === undefined;
2637
+ const written = isUnnamedIndexer ? '[…]' : access.name;
2638
+ const declared = isUnnamedIndexer ? this.#registry.getUnnamedIndexers() : this.#membersReadBy(access);
2639
+ const accessible = this.#memberSelector.getAccessible(this.#imported.getAvailable(declared));
2640
+ const candidates = isUnnamedIndexer ? accessible : accessible.filter(candidate => FORM_NODE_KINDS[candidate.kind] === node.kind);
2641
+
2642
+ if (node.kind !== 'memberAccess' && candidates.length === 0) {
2643
+ if (declared.length === 0) {
2644
+ this.#fail('NEXO3001', `'${written}' is no member of the vocabulary`, pointer);
2645
+ }
2646
+
2647
+ if (accessible.length === 0) {
2648
+ this.#refuseUnimported(declared, null, `'${written}'`, pointer);
2649
+ }
2650
+
2651
+ if (accessible.length === 0) {
2652
+ this.#failInternal(`'${written}'`, this.#registry.getDeclaringModuleName(declared[0]), pointer);
2653
+ }
2654
+
2655
+ this.#requireDeclaredForm(accessible[0], node, pointer);
2656
+ }
2657
+
2658
+ // A host call is selected during resolution alone, so an access whose every candidate is one could never select a member as it is evaluated.
2659
+ if (candidates.length > 0 && candidates.every(candidate => NexoModule.isInvokedByHost(candidate))) {
2660
+ this.#fail('NEXO3092', `'${written}' is a host call wherever it applies, which its host invokes and resolution alone selects, and its receiver is a value of Any: make its receiver known, as a cast does, or select its extension explicitly`, pointer);
2661
+ }
2662
+
2663
+ const argumentNodes = node.kind === 'call' ? node.arguments : node.kind === 'indexerAccess' ? [node.argument] : [];
2664
+ const args = argumentNodes.map((argument, index) => this.#resolveDeferredArgument(argument, node.kind === 'indexerAccess' ? `${pointer}/argument` : `${pointer}/arguments/${index}`, scope, written));
2665
+
2666
+ const readsExtensionMembers = access.extension === undefined ? {} : { readsExtensionMembers: true };
2667
+
2668
+ return { result: { type: Type.any, kind: 'value', isFixed: false }, resolution: { kind: 'deferred', name: isUnnamedIndexer ? null : access.name, candidates, receiverNode: access.receiver, arguments: args, ...readsExtensionMembers }, use: null };
2669
+ }
2670
+
2671
+ /** Resolves an argument of a deferred access, read without the declaration it will select: a value, refused where it reads an implicit parameter of its own or gives a function (NEXO3029). */
2672
+ #resolveDeferredArgument(argument, pointer, scope, written) {
2673
+ // Whether the argument reads its own implicit parameter, which its own context key identifies, as a body would.
2674
+ const argumentBody = { key: {}, isRead: false };
2675
+ const result = this.#resolveNode(argument, pointer, { ...scope, contextType: Type.any, contextKey: argumentBody.key, argumentBody });
2676
+
2677
+ if (result.kind !== 'value') {
2678
+ this.#fail('NEXO3048', `An argument of '${written}' is ${LanguageResolver.#descriptionOf(result.kind)}, and an argument is a value`, pointer);
2679
+ }
2680
+
2681
+ if (argumentBody.isRead || LanguageResolver.#heldReferenceOf(result.type) === 'function') {
2682
+ this.#fail('NEXO3029', `'${written}' is selected when its receiver, a value of Any, is evaluated, so its arguments are read without its declaration, each a value, and this one is ${argumentBody.isRead ? 'an implicit body' : 'a function'}, which only a declaration gives a reading: make its receiver known, as a cast does, to give it one`, pointer);
2683
+ }
2684
+
2685
+ return { node: argument, pointer };
2686
+ }
2687
+
2688
+ /** Refuses a call or an indexer of what is read by its name alone, an object field or a property of a structured type's data, which the description names, giving the access to write. */
2689
+ #requireReadWithoutArgument(node, pointer, description) {
2690
+ if (node.kind !== 'memberAccess') {
2691
+ this.#fail('NEXO3017', `'${LanguageResolver.#accessOf(node).name}' is ${description} and takes no argument: write '${DefinitionWriter.quote(LanguageResolver.#createPropertyAccess(node), this.#namespaceNames)}'`, pointer);
2692
+ }
2693
+ }
2694
+
2695
+ /**
2696
+ * Resolves an invocation of the declaration on its receiver, a node with its type and whether it can be null, or on none for a module member: the arguments its node gives it, those after its receiver where the node's first argument is that,
2697
+ * its type parameters bound by its receiver's type, its own by its value arguments and then its callbacks' results, and the type a fixed argument selects. It answers its result, nullable where its receiver or an argument whose
2698
+ * parameter propagates null can be null, its resolution and the use of its declaration, under the name its messages give it, and stores neither: whoever stores the resolution records the use.
2699
+ */
2700
+ #resolveInvocation(selected, node, pointer, scope, receiver, name) {
2701
+ const receiverType = receiver?.type ?? null;
2702
+ const declaration = this.#selectSignature(selected, node, pointer, scope, receiverType);
2703
+ // A qualified call's receiver is its first argument, and its own arguments follow it.
2704
+ const offset = receiver !== null && receiver.node === node.arguments?.[0] ? 1 : 0;
2705
+ // The declaration was selected for its receiver, so its receiver type always applies and binds what it writes; every value argument's binding is collected before any callback is resolved, whatever their order.
2706
+ const typeArguments = new Map(receiverType === null ? [] : (this.#typeChecker.inferReceiverTypeArguments(declaration, receiverType) ?? []));
2707
+
2708
+ this.#bindTypeArguments(declaration, this.#collectValueArgumentEvidence(declaration, node, pointer, scope, offset), typeArguments, pointer);
2709
+
2710
+ const resolved = this.#resolveArguments(declaration, node, pointer, scope, receiverType, typeArguments, offset);
2711
+
2712
+ // A callback's result binds what nothing before it bound, once every callback is resolved, so no callback's result establishes another's inputs.
2713
+ this.#bindTypeArguments(declaration, resolved.callbackEvidence, typeArguments, pointer);
2714
+
2715
+ const resultTypeArguments = new Map(typeArguments);
2716
+
2717
+ if (resolved.selected !== undefined) {
2718
+ resultTypeArguments.set('@selected', resolved.selected.type);
2719
+ }
2720
+
2721
+ // A host call that supplies nothing has no value to type, so it is typed as a never, which no value has.
2722
+ const declared = declaration.result === undefined ? Type.never : this.#registry.getDeclaredType(declaration, declaration.result).resolveTypeParameters(resultTypeArguments);
2723
+ const unbound = receiverType !== null && LanguageResolver.#isNullOrNever(receiverType) ? null : LanguageResolver.#typeParameterOf(declared);
2724
+
2725
+ // A type parameter of the result that neither the receiver nor an argument binds leaves the result a type no value has, which the call is refused for; a receiver of unknown type binds none, and is never evaluated.
2726
+ if (unbound !== null) {
2727
+ this.#fail('NEXO3028', `'${name ?? declaration.name}' produces ${declared}, and nothing it is given binds its type parameter ${unbound}`, pointer);
2728
+ }
2729
+
2730
+ const resolution = {
2731
+ kind: 'invocation',
2732
+ declaration,
2733
+ ...(receiver === null ? {} : { receiverNode: receiver.node, receiverType }),
2734
+ type: declared,
2735
+ arguments: resolved.arguments,
2736
+ selected: resolved.selected,
2737
+ buildsObjectRoot: resolved.buildsObjectRoot,
2738
+ };
2739
+ const type = receiver?.isNullable === true || resolved.isNullable ? declared.toNullable() : declared;
2740
+
2741
+ // A dictionary a call answers keys its entries by a key type, which a type parameter its arguments bind may leave it without.
2742
+ if (declared.isDictionary && !this.#registry.isKeyType(declared.typeArguments[0])) {
2743
+ this.#fail('NEXO3083', `'${name}' answers a dictionary keyed by ${declared.typeArguments[0]}, and a dictionary's keys are scalars, references or null`, pointer);
2744
+ }
2745
+
2746
+ this.#requireKeyType(declaration, typeArguments, name, pointer);
2747
+
2748
+ return { result: { type, kind: LanguageResolver.#getResultKind(declaration), isFixed: false, ...(declaration.result === undefined ? { suppliesNothing: true } : {}) }, resolution, use: { declaration, qualifiedName: name, pointer } };
2749
+ }
2750
+
2751
+ /**
2752
+ * Refuses a member of Nexo's own that compares values by key equality, its items or the keys its expression gives, where the call binds them to a type no key is of, an object, a list, a structured value or a value of
2753
+ * Any, which key equality compares none of (NEXO3083); a receiver of unknown type binds none, and is checked once its type is known.
2754
+ */
2755
+ #requireKeyType(declaration, typeArguments, name, pointer) {
2756
+ const typeParameter = Vocabulary.getKeyTypeParameter(declaration);
2757
+ const compared = typeParameter === null ? undefined : typeArguments.get(typeParameter);
2758
+
2759
+ if (compared === undefined || this.#registry.isKeyType(compared)) {
2760
+ return;
2761
+ }
2762
+
2763
+ const written = name ?? declaration.name;
2764
+ const what = typeParameter === 'K' ? `the keys its expression gives, of ${compared},` : `the items of a List<${compared}>`;
2765
+ const instead = typeParameter === 'K' ? 'give a key of one of those' : declaration.name === 'distinct' ? 'compare them by a key, as distinctBy(it.id) does' : `compare them by a key, as ${declaration.name === 'contains' ? 'any' : 'indexWhere'}(it.id == value.id) does`;
2766
+
2767
+ this.#fail('NEXO3083', `'${written}' compares ${what} by key equality, which compares numbers, strings, Booleans, references and null alone: ${instead}`, pointer);
2768
+ }
2769
+
2770
+ /**
2771
+ * The evidence the member's value arguments give the type parameters their parameters' types write, each argument resolved as it will be, against the scope of where it is written, and nothing of it kept: an options
2772
+ * object's value options in scope by their names after their values, and its other options, which give no single value, refusing a read. An argument its resolution refuses gives none, and is refused where it is resolved.
2773
+ */
2774
+ #collectValueArgumentEvidence(declaration, node, pointer, scope, offset) {
2775
+ const evidence = new Map();
2776
+
2777
+ if ((declaration.typeParameters ?? []).length === 0) {
2778
+ return evidence;
2779
+ }
2780
+
2781
+ const supplied = this.#suppliedArguments(declaration, node, pointer, offset);
2782
+ const optionsObject = declaration.arguments === 'options' && supplied.size > 0 ? (node.kind === 'indexerAccess' ? node.argument : node.arguments[offset]) : null;
2783
+ const objectExpression = optionsObject !== null && scope.objectRoot === null ? optionsObject : null;
2784
+ let argumentScope = scope;
2785
+
2786
+ if (objectExpression !== null) {
2787
+ this.#objectRootFields.set(objectExpression, new Map());
2788
+ argumentScope = { ...scope, objectRoot: LanguageResolver.#createObjectRoot(objectExpression) };
2789
+ }
2790
+
2791
+ for (const [parameterName, given] of supplied) {
2792
+ const parameter = declaration.parameters.find(candidate => candidate.name === parameterName);
2793
+
2794
+ if (parameter.kind !== 'value') {
2795
+ if (optionsObject !== null) {
2796
+ argumentScope = { ...argumentScope, locals: argumentScope.locals.with(parameter.name, { optionKind: parameter.kind === 'function' ? 'implicitBody' : parameter.kind, result: null }) };
2797
+ }
2798
+
2799
+ continue;
2800
+ }
2801
+
2802
+ const declaredType = this.#registry.getDeclaredType(declaration, parameter.type);
2803
+ // An argument no structure is expected of resolves alike for its evidence and its invocation, so its records are kept and its result handed on; one that may construct is resolved again.
2804
+ const handsOn = this.#constructibleTypeOf(declaredType.toNonNullable()) === null;
2805
+ const outcome = this.#resolveSpeculatively(() => this.#resolveExpecting(given.node, given.pointer, argumentScope, declaredType), { keepsSuccess: handsOn });
2806
+
2807
+ if (outcome.error !== undefined) {
2808
+ continue;
2809
+ }
2810
+
2811
+ if (handsOn) {
2812
+ this.#resolvedArguments.set(given.node, { scope: argumentScope, pointer: given.pointer, result: outcome.result });
2813
+ }
2814
+
2815
+ if (outcome.result.type.kind !== 'function') {
2816
+ this.#typeChecker.extractTypeArgumentEvidence(this.#registry.getDeclaredType(declaration, parameter.type), outcome.result.type, evidence);
2817
+ }
2818
+
2819
+ if (optionsObject !== null) {
2820
+ argumentScope = { ...argumentScope, locals: argumentScope.locals.with(parameter.name, { result: outcome.result, initializer: given.node, scope: argumentScope }) };
2821
+ }
2822
+
2823
+ if (objectExpression !== null) {
2824
+ this.#objectRootFields.get(objectExpression).set(parameter.name, { initializer: given.node });
2825
+ argumentScope = { ...argumentScope, objectRoot: LanguageResolver.#withRootField(argumentScope.objectRoot, parameter.name, outcome.result.type) };
2826
+ }
2827
+ }
2828
+
2829
+ return evidence;
2830
+ }
2831
+
2832
+ /** Binds each of the member's own type parameters that nothing bound yet and the evidence gives a type, refusing candidates of different types (NEXO3064), which no binding reconciles. */
2833
+ #bindTypeArguments(declaration, evidence, typeArguments, pointer) {
2834
+ for (const parameterName of declaration.typeParameters ?? []) {
2835
+ if (typeArguments.has(parameterName) || !evidence.has(parameterName)) {
2836
+ continue;
2837
+ }
2838
+
2839
+ const resolved = this.#typeChecker.resolveTypeArgument(evidence.get(parameterName));
2840
+
2841
+ if (resolved?.candidates !== undefined) {
2842
+ this.#fail('NEXO3064', `'${NexoModule.writtenNameOf(declaration)}' is given ${resolved.candidates.join(' and ')} for its type parameter ${parameterName}, which binds one type, never a type they share`, pointer);
2843
+ }
2844
+
2845
+ if (resolved !== null) {
2846
+ typeArguments.set(parameterName, resolved.type);
2847
+ }
2848
+ }
2849
+ }
2850
+
2851
+ /** Stores what the node at the pointer resolved to, an access's resolution or an invocation's, and records the use of its declaration, where it chose one, answering its result. */
2852
+ #storeResolution(pointer, { result, resolution, use }) {
2853
+ this.#resolutions.set(pointer, resolution);
2854
+
2855
+ if (use !== null) {
2856
+ this.#usedDeclarations.push(use);
2857
+ }
2858
+
2859
+ return result;
2860
+ }
2861
+
2862
+ /**
2863
+ * Resolves a member read or a call through a module's namespace, the module the local name its imports give names: a module member, read or called; a reference to one of the module's functions, read without a call,
2864
+ * which the callback contract it is passed to selects where one is given; or a qualified form, a member the module declares on a type, called with its receiver first and its own arguments after it. A message writes the
2865
+ * namespace by its local name, as the script does, and names the module by its own.
2866
+ */
2867
+ #resolveThroughNamespace(node, pointer, scope, contract = null) {
2868
+ // A reference passed as an argument reaches here alone, so it is recorded where it stands, as a node resolved alone is.
2869
+ this.#pointers.set(node, pointer);
2870
+
2871
+ const access = LanguageResolver.#accessOf(node);
2872
+ const moduleName = this.#imported.getNamespaceModule(access.receiver.name);
2873
+ const written = `${access.receiver.name}.${access.name}`;
2874
+ const declared = this.#registry.getMembersDeclaredBy(moduleName, access.name);
2875
+ const namedDeclarations = this.#memberSelector.getAccessible(declared);
2876
+
2877
+ if (declared.length > 0 && namedDeclarations.length === 0) {
2878
+ this.#failInternal(`'${written}'`, moduleName, pointer);
2879
+ }
2880
+
2881
+ const declarations = namedDeclarations.filter(declaration => declaration.kind !== 'indexer');
2882
+
2883
+ if (declarations.length === 0) {
2884
+ this.#fail(namedDeclarations.length > 0 ? 'NEXO3011' : 'NEXO3001', namedDeclarations.length > 0 ? `'${access.name}' is an indexer, which no namespace qualifies: write its receiver and '${this.#memberSelector.isExtensionMember(namedDeclarations[0]) ? ':' : '.'}${access.name}[…]'` : `The module '${moduleName}' declares no member '${access.name}'`, pointer);
2885
+ }
2886
+
2887
+ const moduleMemberDeclaration = declarations.find(declaration => !this.#registry.takesReceiver(declaration));
2888
+ const receiverMemberDeclarations = declarations.filter(declaration => this.#registry.takesReceiver(declaration));
2889
+ const readings = [
2890
+ ...(moduleMemberDeclaration === undefined ? [] : [() => this.#resolveModuleMember(node, pointer, scope, moduleMemberDeclaration, written, contract)]),
2891
+ ...(receiverMemberDeclarations.length === 0 ? [] : [() => (node.kind === 'memberAccess' ? this.#resolveFunctionReference(node, pointer, receiverMemberDeclarations, contract) : this.#resolveQualifiedForm(node, pointer, scope, receiverMemberDeclarations, written))]),
2892
+ ];
2893
+
2894
+ if (readings.length === 1) {
2895
+ return readings[0]();
2896
+ }
2897
+
2898
+ // A module member and a member of its name on a receiver are both reached through the namespace, so the one reading resolution and type checking accept is taken, and one both accept is ambiguous.
2899
+ const outcomes = readings.map(read => this.#resolveSpeculatively(read, { keepsSuccess: true }));
2900
+ const accepted = outcomes.filter(outcome => outcome.error === undefined);
2901
+
2902
+ if (accepted.length > 1) {
2903
+ this.#fail('NEXO3024', `'${written}' is both the module member and the qualified form of the member '${access.name}' of a receiver, and this access fits both, so which applies is ambiguous; the receiver form, receiver.${access.name}, reaches the member of the receiver`, pointer);
2904
+ }
2905
+
2906
+ if (accepted.length === 0) {
2907
+ throw outcomes[0].error;
2908
+ }
2909
+
2910
+ return accepted[0].result;
2911
+ }
2912
+
2913
+ /**
2914
+ * How a message names the competing declarations an access can't choose among, after the access: their receiver types, none more specific than another, which the receiver satisfies, where one module declares them;
2915
+ * and the modules that declare them where several do, with the extension access that selects each, its receiver as the access writes it, where every one of them is an extension's.
2916
+ */
2917
+ #formatCompetingDeclarations(declarations, receiverType, receiverNode) {
2918
+ const modules = [...new Set(declarations.map(declaration => this.#registry.getDeclaringModuleName(declaration)))];
2919
+
2920
+ if (modules.length > 1) {
2921
+ const extensions = declarations.map(declaration => this.#registry.getContainingType(declaration));
2922
+ const receiver = DefinitionWriter.quote(receiverNode, this.#namespaceNames);
2923
+ const accesses = declarations.every(declaration => this.#memberSelector.isExtensionMember(declaration))
2924
+ ? declarations.map((declaration, index) => `'${receiver}:${this.#formatExtensionName(extensions[index])}.${declaration.name}'`)
2925
+ : [];
2926
+
2927
+ return `on ${receiverType.toNonNullable()} is ambiguous between the modules ${modules.map(name => `'${name}'`).join(' and ')}${accesses.length === 0 ? '' : `: write ${accesses.join(' or ')} to select one`}`;
2928
+ }
2929
+
2930
+ const types = declarations.flatMap(declaration => this.#registry.getReceiverTypes(declaration));
2931
+
2932
+ return `is declared for ${types.join(' and ')}, which ${receiverType.toNonNullable()} satisfies, and none of them is more specific than ${types.length === 2 ? 'the other' : 'every other'}, so which applies is ambiguous`;
2933
+ }
2934
+
2935
+ /** Refuses what a script reaches of an internal declaration, which its declaring module's own declarations alone name, as if no declaration of its name were accessible (NEXO3023). */
2936
+ #failInternal(subject, moduleName, pointer) {
2937
+ this.#fail('NEXO3023', `${subject} is internal to the module '${moduleName}', which no script reaches`, pointer);
2938
+ }
2939
+
2940
+ /**
2941
+ * Resolves a possible reading, answering what it resolves to, or the failure that refuses it where that is a NexoError; any other failure propagates. A refused reading leaves the resolutions, the declaration uses,
2942
+ * the body copies and the argument bodies read as they were, and so does a successful one unless `keepsSuccess` keeps its records, its result answered either way. Restoring those four stores is all it promises, since resolution writes
2943
+ * nothing else a later reading could take for its own: the functions being resolved and the optional chains are removed as each resolution ends, whether it succeeds or fails; an object expression's object-root
2944
+ * fields are reset before they are filled, and read only within it; a structure expected of a node lasts only while the resolution that expects it does; the result a computed property access follows is read only once
2945
+ * its own resolution wrote it; the name an access joins comes from its syntax alone;
2946
+ * and the module-function cache keeps one entry for each declaration, whichever reading made it, so references to one declaration stay one function.
2947
+ * @param {() => object} resolve The resolution of the reading.
2948
+ * @param {{ keepsSuccess: boolean }} options Whether a successful reading's records are kept.
2949
+ * @returns {{ result: object } | { error: NexoError }}
2950
+ */
2951
+ #resolveSpeculatively(resolve, { keepsSuccess }) {
2952
+ const resolutions = new Map(this.#resolutions);
2953
+ const usedCount = this.#usedDeclarations.length;
2954
+ const bodyCopyCount = this.#functionBodyCopies.length;
2955
+ const readBodyCount = this.#readArgumentBodies.length;
2956
+ let isKept = false;
2957
+
2958
+ try {
2959
+ const result = resolve();
2960
+
2961
+ isKept = keepsSuccess;
2962
+
2963
+ return { result };
2964
+ } catch (error) {
2965
+ if (!(error instanceof NexoError)) {
2966
+ throw error;
2967
+ }
2968
+
2969
+ return { error };
2970
+ } finally {
2971
+ if (!isKept) {
2972
+ this.#resolutions = resolutions;
2973
+ this.#usedDeclarations.length = usedCount;
2974
+ this.#functionBodyCopies.length = bodyCopyCount;
2975
+
2976
+ for (const argumentBody of this.#readArgumentBodies.splice(readBodyCount)) {
2977
+ argumentBody.isRead = false;
2978
+ }
2979
+ }
2980
+ }
2981
+ }
2982
+
2983
+ /** Resolves a module member, which has no receiver: a property read through its namespace, a function called through it, or a function read without a call, a reference to it. */
2984
+ #resolveModuleMember(node, pointer, scope, declaration, written, contract) {
2985
+ if (declaration.kind === 'property') {
2986
+ if (node.kind === 'call') {
2987
+ this.#fail('NEXO3017', `'${written}' is a property, read as ${written}, and takes no argument`, pointer);
2988
+ }
2989
+
2990
+ const type = this.#registry.getDeclaredType(declaration, declaration.result);
2991
+
2992
+ this.#usedDeclarations.push({ declaration, qualifiedName: written, pointer });
2993
+ this.#resolutions.set(pointer, { kind: 'invocation', declaration, type, arguments: [] });
2994
+
2995
+ return { type, kind: 'value', isFixed: false };
2996
+ }
2997
+
2998
+ if (node.kind === 'memberAccess') {
2999
+ return this.#resolveFunctionReference(node, pointer, [declaration], contract);
3000
+ }
3001
+
3002
+ return this.#storeResolution(pointer, this.#resolveInvocation(declaration, node, pointer, scope, null, written));
3003
+ }
3004
+
3005
+ /** Resolves a qualified form: the module's member on a type, called with its receiver as its first argument, as a qualified call. */
3006
+ #resolveQualifiedForm(node, pointer, scope, declarations, written) {
3007
+ return this.#storeResolution(pointer, this.#resolveQualifiedCall(node, pointer, scope, declarations, {
3008
+ subject: `'${written}'`,
3009
+ missingReceiver: `'${written}' is the qualified form of a member of a receiver, which takes the receiver as its first argument, as ${written}(receiver)`,
3010
+ receiverOf: `'${written}' takes a receiver of`,
3011
+ nameOf: (declaration, receiverType) => this.#qualifiedNameOf(declaration, receiverType),
3012
+ }));
3013
+ }
3014
+
3015
+ /**
3016
+ * Resolves a qualified call: a member on a type called with its receiver as its first argument, which is evaluated first and whose null answers null without the member's own arguments being evaluated, and its own arguments after it,
3017
+ * as a qualified form calls one of a module's members and a call of a reference to a module's function calls the one it holds. The receiver's type selects among the declarations; a receiver of unknown type selects none, and is checked as any
3018
+ * such receiver is. Each message names the call as its wording says; it answers the call's result, its resolution and the use of the declaration it selects, none for a receiver of unknown type, which the caller stores and records.
3019
+ */
3020
+ #resolveQualifiedCall(node, pointer, scope, declarations, wording) {
3021
+ if (node.arguments.length === 0) {
3022
+ this.#fail('NEXO3080', wording.missingReceiver, pointer);
3023
+ }
3024
+
3025
+ const receiver = this.#resolveReceiverArgument(node, pointer, scope, wording.subject);
3026
+ const receiverType = receiver.type.toNonNullable();
3027
+ const isReceiverNullable = receiver.type.isNullable || receiver.type.kind === 'null';
3028
+
3029
+ if (LanguageResolver.#isNullOrNever(receiverType)) {
3030
+ return this.#resolveOnUnknownType(node, pointer, scope, receiver.type.kind === 'null' ? Type.null : receiverType, declarations, 1);
3031
+ }
3032
+
3033
+ const selection = this.#memberSelector.select(declarations, receiverType);
3034
+
3035
+ if (selection.kind === 'none') {
3036
+ this.#fail('NEXO3042', `${wording.receiverOf} ${declarations.flatMap(declaration => this.#registry.getReceiverTypes(declaration)).join(', ')}, and is given ${receiverType}`, `${pointer}/arguments/0`);
3037
+ }
3038
+
3039
+ // A reference's declaration or a module's, reached through its namespace, is internal to none of them, so no other selection is left.
3040
+ if (selection.kind === 'ambiguous') {
3041
+ this.#fail('NEXO3024', `${wording.subject} ${this.#formatCompetingDeclarations(selection.declarations, receiverType, node.arguments[0])}`, pointer);
3042
+ }
3043
+
3044
+ const { declaration } = selection;
3045
+
3046
+ const receiverArgument = { node: node.arguments[0], type: receiverType, isNullable: isReceiverNullable };
3047
+
3048
+ return this.#resolveInvocation(declaration, node, pointer, scope, receiverArgument, wording.nameOf(declaration, receiverType));
3049
+ }
3050
+
3051
+ /** Resolves the receiver a qualified call or a call of a reference to a module's function gives as its first argument, a value of any type, which the call evaluates before its own arguments. */
3052
+ #resolveReceiverArgument(node, pointer, scope, written) {
3053
+ const receiverPointer = `${pointer}/arguments/0`;
3054
+ const receiver = this.#resolveNode(node.arguments[0], receiverPointer, scope);
3055
+
3056
+ if (receiver.kind !== 'value') {
3057
+ this.#fail('NEXO3048', `The receiver ${written} is given is ${LanguageResolver.#descriptionOf(receiver.kind)}, and a receiver is a value`, receiverPointer);
3058
+ }
3059
+
3060
+ if (LanguageResolver.#heldReferenceOf(receiver.type) === 'function') {
3061
+ this.#fail('NEXO3042', `The receiver ${written} is given is a function, which has no members`, receiverPointer);
3062
+ }
3063
+
3064
+ return receiver;
3065
+ }
3066
+
3067
+ /**
3068
+ * Resolves a reference to a module's function: one of the module's functions the evaluator invokes, read without a call, which takes its receiver first, where it has one, and its own arguments after it.
3069
+ * Passed to a function parameter, it names the declaration whose receiver and parameters take the arguments the callback contract declares; anywhere else, the module's one function of its name. Creating it reads nothing and invokes nothing.
3070
+ */
3071
+ #resolveFunctionReference(node, pointer, declarations, contract) {
3072
+ const written = `${node.receiver.name}.${node.name}`;
3073
+ const functions = declarations.filter(declaration => declaration.kind === 'function' && !NexoModule.isInvokedByHost(declaration));
3074
+
3075
+ if (functions.length === 0) {
3076
+ const [declaration] = declarations;
3077
+ const message = declaration.kind === 'property'
3078
+ ? `'${written}' is a property, read with its receiver as ${written}(receiver), and only a function has a reference`
3079
+ : `'${written}' is a host call, which its host invokes, so it has no reference`;
3080
+
3081
+ this.#fail('NEXO3076', message, pointer);
3082
+ }
3083
+
3084
+ const selected = contract === null ? functions : functions.filter(declaration => this.#takesContract(declaration, contract));
3085
+ const alternatives = `write an arrow function, whose receiver's type selects one, as x => x.${node.name}(), or an implicit body, as it.${node.name}()`;
3086
+
3087
+ if (selected.length === 0) {
3088
+ this.#fail('NEXO3037', `The function given as '${contract.parameterName}' of '${contract.memberName}' is called with ${contract.parameterTypes.join(', ')} and answers ${contract.resultType ?? 'any value'}, and '${written}' names no function that takes those or answers that: it names ${functions.map(declaration => this.#signatureOf(declaration)).join(', ')}`, pointer);
3089
+ }
3090
+
3091
+ if (selected.length > 1) {
3092
+ this.#fail('NEXO3075', `'${written}' names ${selected.length} functions${contract === null ? '' : ` that the contract of '${contract.memberName}' allows`}, so which it references is ambiguous; ${alternatives}`, pointer);
3093
+ }
3094
+
3095
+ const [declaration] = selected;
3096
+
3097
+ this.#usedDeclarations.push({ declaration, qualifiedName: written, pointer });
3098
+ this.#resolutions.set(pointer, { kind: 'functionReference', declaration });
3099
+
3100
+ return { type: Type.createFunctionType([this.#getModuleFunctionEntry(declaration)]), kind: 'value', isFixed: false };
3101
+ }
3102
+
3103
+ /**
3104
+ * The function-type entry of each module function, indexed declaration → entry, for as long as the resolver: the one entry a function type's `functions` holds for the declaration, so references to one declaration are one
3105
+ * function, as two reads of one local function's name are. @type {WeakMap<object, { reference: object }>}
3106
+ */
3107
+ #moduleFunctionEntryCache = new WeakMap();
3108
+
3109
+ /** Gets the shared function-type entry for a module function declaration, creating it at the first request: repeated references to the same declaration receive the same entry. */
3110
+ #getModuleFunctionEntry(declaration) {
3111
+ let entry = this.#moduleFunctionEntryCache.get(declaration);
3112
+
3113
+ if (entry === undefined) {
3114
+ entry = Object.freeze({ reference: declaration });
3115
+ this.#moduleFunctionEntryCache.set(declaration, entry);
3116
+ }
3117
+
3118
+ return entry;
3119
+ }
3120
+
3121
+ /** How a message names a reference's declaration: its module's name and its own, as its reference is written. */
3122
+ #referenceNameOf(declaration) {
3123
+ return `${this.#registry.getDeclaringModuleName(declaration)}.${declaration.name}`;
3124
+ }
3125
+
3126
+ /** How a message names a function's signature: its receiver's types where it has one, the types of its parameters, and its result. */
3127
+ #signatureOf(declaration) {
3128
+ const parameters = [...(this.#registry.takesReceiver(declaration) ? [this.#registry.getReceiverTypes(declaration).join(' | ')] : []), ...declaration.parameters.map(parameter => parameter.type)];
3129
+
3130
+ return `(${parameters.join(', ')}) => ${declaration.result}`;
3131
+ }
3132
+
3133
+ /**
3134
+ * Whether the declaration takes the arguments the callback contract passes and answers a result it takes: its receiver the first, where it has one, of the type the contract gives it, and then one positional value for each of its parameters,
3135
+ * those it requires at least. A type of an unknown receiver, a never or a type parameter no receiver bound, fits any.
3136
+ */
3137
+ #takesContract(declaration, contract) {
3138
+ const { parameterTypes, resultType } = contract;
3139
+ const takesReceiver = this.#registry.takesReceiver(declaration);
3140
+ const typeArguments = this.#contractTypeArgumentsOf(declaration, contract);
3141
+
3142
+ if (typeArguments === null) {
3143
+ return false;
3144
+ }
3145
+
3146
+ const own = takesReceiver ? parameterTypes.slice(1) : parameterTypes;
3147
+ const { parameters } = declaration;
3148
+
3149
+ if (parameters.some(parameter => parameter.kind !== 'value') || (parameters.length > 0 && declaration.arguments === 'options')) {
3150
+ return false;
3151
+ }
3152
+
3153
+ if (own.length < parameters.filter(parameter => parameter.required).length || own.length > parameters.length) {
3154
+ return false;
3155
+ }
3156
+
3157
+ const fitsParameters = own.every((type, index) => LanguageResolver.#fitsAny(type) || this.#typeChecker.isAssignable(type, this.#registry.getDeclaredType(declaration, parameters[index].type).resolveTypeParameters(typeArguments)));
3158
+ const declared = this.#registry.getDeclaredType(declaration, declaration.result).resolveTypeParameters(typeArguments);
3159
+
3160
+ // A declared result of Any is checked as the callback answers it; a parameter is never, since nothing checks what a reference is called with.
3161
+ return fitsParameters && (resultType === null || LanguageResolver.#fitsAny(resultType) || this.#typeChecker.assignabilityOf(declared, resultType) !== 'refused');
3162
+ }
3163
+
3164
+ /**
3165
+ * The arguments of the type parameters the callback contract binds for the declaration: those its receiver binds, none where the declaration takes no receiver or the contract's receiver type fits any, and its own,
3166
+ * which the arguments the contract passes after its receiver bind, as a call's value arguments do. It answers null where the contract gives no receiver, one that can be null, or one the declaration does not apply to,
3167
+ * and where it gives an own parameter candidates of different types.
3168
+ */
3169
+ #contractTypeArgumentsOf(declaration, contract) {
3170
+ const takesReceiver = this.#registry.takesReceiver(declaration);
3171
+ let typeArguments = new Map();
3172
+
3173
+ if (takesReceiver) {
3174
+ const [receiverType] = contract.parameterTypes;
3175
+
3176
+ if (receiverType === undefined || receiverType.isNullable) {
3177
+ return null;
3178
+ }
3179
+
3180
+ typeArguments = LanguageResolver.#fitsAny(receiverType) ? new Map() : this.#typeChecker.inferReceiverTypeArguments(declaration, receiverType);
3181
+
3182
+ if (typeArguments === null) {
3183
+ return null;
3184
+ }
3185
+ }
3186
+
3187
+ const evidence = new Map();
3188
+
3189
+ (takesReceiver ? contract.parameterTypes.slice(1) : contract.parameterTypes).forEach((type, index) => {
3190
+ const parameter = declaration.parameters[index];
3191
+
3192
+ if (parameter !== undefined && !LanguageResolver.#fitsAny(type)) {
3193
+ this.#typeChecker.extractTypeArgumentEvidence(this.#registry.getDeclaredType(declaration, parameter.type), type, evidence);
3194
+ }
3195
+ });
3196
+
3197
+ for (const parameterName of (declaration.typeParameters ?? []).filter(name => !typeArguments.has(name) && evidence.has(name))) {
3198
+ const resolved = this.#typeChecker.resolveTypeArgument(evidence.get(parameterName));
3199
+
3200
+ if (resolved.candidates !== undefined) {
3201
+ return null;
3202
+ }
3203
+
3204
+ typeArguments.set(parameterName, resolved.type);
3205
+ }
3206
+
3207
+ return typeArguments;
3208
+ }
3209
+
3210
+ /** Whether a type a callback contract gives fits any: a never, the type of an unknown receiver's arguments, or a type parameter no receiver bound. */
3211
+ static #fitsAny(type) {
3212
+ return type.kind === 'never' || type.kind === 'typeParameter';
3213
+ }
3214
+
3215
+ /**
3216
+ * Resolves a name applied to a receiver of unknown type, which chooses no declaration among the candidates, those of its name or, for an unnamed indexer, every unnamed indexer. A property is accepted whatever its name, since a bound object's fields are named by its data; a call or an indexer must be a member's, written in the form and given the arguments one of its declarations takes. The declarations that take it agree on who invokes it, the evaluator or the host.
3217
+ * Its type is the result type they share, made nullable, or left to the optional chain an optional access of null ends, and null where they share none or the name is no member's. Nothing applied to such a receiver is evaluated, so its arguments leave no resolution behind.
3218
+ * It answers its result and its resolution, with no use, since it chooses no declaration, and records neither.
3219
+ */
3220
+ #resolveOnUnknownType(node, pointer, scope, receiverType, declared = this.#membersReadBy(LanguageResolver.#accessOf(node)), offset = 0) {
3221
+ const access = LanguageResolver.#accessOf(node);
3222
+ const isAccess = node.kind === 'memberAccess';
3223
+ const candidates = this.#memberSelector.getAccessible(this.#imported.getAvailable(declared));
3224
+
3225
+ if (!isAccess && candidates.length === 0) {
3226
+ this.#refuseUnimported(declared, null, `'${access.name ?? '[…]'}'`, pointer);
3227
+ }
3228
+
3229
+ if (!isAccess && declared.length > 0 && candidates.length === 0) {
3230
+ this.#failInternal(`'${access.name}'`, this.#registry.getDeclaringModuleName(declared[0]), pointer);
3231
+ }
3232
+
3233
+ // A qualified call, of a qualified form or of a reference to a module's function, calls a property and a function alike, its receiver its first argument.
3234
+ const candidatesInForm = offset > 0 ? candidates : candidates.filter(candidate => FORM_NODE_KINDS[candidate.kind] === node.kind);
3235
+
3236
+ if (!isAccess && candidates.length === 0) {
3237
+ this.#fail('NEXO3001', `'${access.name}' is no member of the vocabulary`, pointer);
3238
+ }
3239
+
3240
+ // A structured type's data property among the candidates is read by its name alone, whichever type a receiver will have.
3241
+ if (!isAccess && candidatesInForm.length === 0 && candidates.every(candidate => this.#registry.isDataProperty(candidate))) {
3242
+ this.#requireReadWithoutArgument(node, pointer, "a property of a structured type's data");
3243
+ }
3244
+
3245
+ if (!isAccess && candidatesInForm.length === 0) {
3246
+ this.#requireDeclaredForm(candidates[0], node, pointer);
3247
+ }
3248
+
3249
+ const fitting = [];
3250
+ let failure = null;
3251
+
3252
+ // Each candidate's arguments are resolved to determine compatibility and any selected type; their resolution records, declaration uses and body copies are discarded.
3253
+ for (const declaration of candidatesInForm) {
3254
+ const outcome = this.#resolveSpeculatively(() => this.#resolveArguments(declaration, node, pointer, scope, receiverType, new Map(), offset), { keepsSuccess: false });
3255
+
3256
+ if (outcome.error === undefined) {
3257
+ fitting.push({ declaration, resolved: outcome.result });
3258
+ } else {
3259
+ failure ??= outcome.error;
3260
+ }
3261
+ }
3262
+
3263
+ if (!isAccess && fitting.length === 0) {
3264
+ throw failure;
3265
+ }
3266
+
3267
+ // An access may name a field of data, which is a value.
3268
+ const kinds = new Set([...fitting.map(({ declaration }) => LanguageResolver.#getResultKind(declaration)), ...(isAccess ? ['value'] : [])]);
3269
+
3270
+ if (kinds.size > 1) {
3271
+ this.#fail('NEXO3025', `'${access.name}' follows a value whose type is unknown, and its declarations disagree on who invokes it, the evaluator or the host, so whether the script ends in a host call is ambiguous`, pointer);
3272
+ }
3273
+
3274
+ const resultTypes = [
3275
+ ...fitting.filter(({ declaration }) => declaration.result !== undefined).map(({ declaration, resolved }) => this.#registry.getDeclaredType(declaration, declaration.result).resolveTypeParameters(new Map(resolved.selected === undefined ? [] : [['@selected', resolved.selected.type]]))),
3276
+ ];
3277
+ const shared = resultTypes.reduce((merged, type) => (merged === null ? null : this.#typeChecker.merge(merged, type)), Type.never);
3278
+ // A member of a never is never evaluated, since no value has that type, so it answers what its declarations share as they declare it, and where none says what it would answer, nothing yet: a never, which fits wherever a value of any type is required.
3279
+ const isNever = receiverType.kind === 'never';
3280
+ // An optional access of null ends its chain, which is nullable for it, so its own type is the one its declarations share.
3281
+ const isShortCircuited = access.optional === true && receiverType.kind === 'null';
3282
+ const unknown = isNever ? Type.never : Type.null;
3283
+ const type = shared === null || shared.kind === 'never' || LanguageResolver.#holdsTypeParameter(shared) ? unknown : isNever || isShortCircuited ? shared : shared.toNullable();
3284
+
3285
+ // A qualified call's receiver is its first argument, and any other member's the node it targets.
3286
+ const resolution = { kind: 'invocation', candidates: fitting.map(({ declaration }) => declaration), receiverNode: offset > 0 ? node.arguments[0] : access.receiver, receiverType, type, arguments: [] };
3287
+
3288
+ // Host calls that agree on their host supplying nothing take no result projection, whichever of them a receiver's type will choose.
3289
+ const suppliesNothing = fitting.length > 0 && fitting.every(({ declaration }) => declaration.result === undefined);
3290
+
3291
+ return { result: { type, kind: [...kinds][0], isFixed: false, ...(suppliesNothing ? { suppliesNothing: true } : {}) }, resolution, use: null };
3292
+ }
3293
+
3294
+ /** Refuses a member written in a form its declaration does not take, with the form it does: a property is accessed, and a function called or named as its declaration says. */
3295
+ #requireDeclaredForm(declaration, node, pointer, isComputed = false) {
3296
+ const { kind } = declaration;
3297
+
3298
+ if (FORM_NODE_KINDS[kind] === node.kind) {
3299
+ return;
3300
+ }
3301
+
3302
+ const described = { property: 'is a property', function: 'uses call syntax', indexer: 'is an indexer' }[kind];
3303
+ // A computed property's name that settles to an indexer's names it rather than a property, and one that settles to a function's names no property at all.
3304
+ const code = kind === 'indexer'
3305
+ ? (isComputed ? 'NEXO3016' : 'NEXO3011')
3306
+ : isComputed ? 'NEXO3056' : kind === 'function' && node.kind === 'indexerAccess' ? 'NEXO3054' : 'NEXO3017';
3307
+ const access = LanguageResolver.#accessOf(node);
3308
+
3309
+ // An indexer takes one key in brackets, which a node written another way may not give.
3310
+ if (kind === 'indexer' && node.arguments?.length !== 1) {
3311
+ const joiner = access.extension === undefined ? '.' : ':';
3312
+
3313
+ this.#fail(code, `'${access.name}' ${described}: write '${DefinitionWriter.quote(access.receiver, this.#namespaceNames)}${access.optional === true ? '?' : ''}${joiner}${access.name}[…]'`, pointer);
3314
+ }
3315
+
3316
+ this.#fail(code, `'${access.name}' ${described}: write '${DefinitionWriter.quote(LanguageResolver.#writtenAs(node, kind), this.#namespaceNames)}'`, pointer);
3317
+ }
3318
+
3319
+ /** The node written as a member of the kind is: an access of its name, a call of its arguments, an indexer's one, or an indexer of its key, a call's one argument. */
3320
+ static #writtenAs(node, kind) {
3321
+ switch (kind) {
3322
+ case 'property':
3323
+ return LanguageResolver.#createPropertyAccess(node);
3324
+ case 'function':
3325
+ return LanguageResolver.#createCall(node, node.kind === 'indexerAccess' ? [node.argument] : []);
3326
+ default:
3327
+ return { kind: 'indexerAccess', receiver: LanguageResolver.#createPropertyAccess(node), argument: node.arguments[0] };
3328
+ }
3329
+ }
3330
+
3331
+ /**
3332
+ * The arguments a call or an indexer is given, as the value of each parameter they supply: in order, the parameters of a positional declaration, an indexer's key its one,
3333
+ * or the fields of the one options object an options declaration takes, each by its name; those of a qualified call follow the receiver its first argument is, the offset its own arguments begin at.
3334
+ * It refuses an argument no parameter takes: one given a declaration without parameters, one beyond its parameters, or a second argument of an options declaration (NEXO3058); an options declaration's argument that is
3335
+ * no object expression standing alone (NEXO3057); and an options object's field that names no parameter (NEXO3018).
3336
+ */
3337
+ #suppliedArguments(declaration, node, pointer, offset = 0) {
3338
+ const { parameters } = declaration;
3339
+ const supplied = new Map();
3340
+ const given = node.kind === 'indexerAccess'
3341
+ ? [{ node: node.argument, pointer: `${pointer}/argument` }]
3342
+ : (node.arguments ?? []).slice(offset).map((argument, index) => ({ node: argument, pointer: `${pointer}/arguments/${index + offset}` }));
3343
+
3344
+ if (given.length === 0) {
3345
+ return supplied;
3346
+ }
3347
+
3348
+ if (parameters.length === 0) {
3349
+ this.#fail('NEXO3058', `'${NexoModule.writtenNameOf(declaration)}' takes no argument`, given[0].pointer);
3350
+ }
3351
+
3352
+ if (declaration.arguments !== 'options') {
3353
+ if (given.length > parameters.length) {
3354
+ const count = parameters.length === 1 ? 'one argument' : `${parameters.length} arguments`;
3355
+
3356
+ this.#fail('NEXO3058', `'${NexoModule.writtenNameOf(declaration)}' takes ${parameters.some(parameter => !parameter.required) ? 'at most ' : ''}${count}, and is given ${given.length}`, given[parameters.length].pointer);
3357
+ }
3358
+
3359
+ given.forEach((argument, index) => supplied.set(parameters[index].name, argument));
3360
+
3361
+ return supplied;
3362
+ }
3363
+
3364
+ if (given.length > 1) {
3365
+ this.#fail('NEXO3058', `'${NexoModule.writtenNameOf(declaration)}' takes its arguments as the object fields of one object, as { ${parameters[0].name} = … }`, given[1].pointer);
3366
+ }
3367
+
3368
+ const [options] = given;
3369
+
3370
+ if (options.node.kind !== 'object' || options.node.context !== undefined) {
3371
+ this.#fail('NEXO3057', `'${NexoModule.writtenNameOf(declaration)}' takes its arguments as the object fields of an object, as { ${parameters[0].name} = … }`, options.pointer);
3372
+ }
3373
+
3374
+ options.node.objectFields.forEach((field, index) => {
3375
+ const parameter = parameters.find(candidate => candidate.name === field.name);
3376
+
3377
+ if (parameter === undefined) {
3378
+ this.#fail('NEXO3018', `'${NexoModule.writtenNameOf(declaration)}' has no parameter '${field.name}'; it takes ${parameters.map(candidate => candidate.name).join(', ')}`, `${options.pointer}/objectFields/${index}`);
3379
+ }
3380
+
3381
+ supplied.set(parameter.name, { node: field.value, pointer: `${options.pointer}/objectFields/${index}/value` });
3382
+ });
3383
+
3384
+ return supplied;
3385
+ }
3386
+
3387
+ #resolveArguments(declaration, node, pointer, scope, receiverType, typeArguments, offset = 0) {
3388
+ const parameters = declaration.parameters;
3389
+ const supplied = this.#suppliedArguments(declaration, node, pointer, offset);
3390
+
3391
+ const resolved = [];
3392
+ const callbackEvidence = new Map();
3393
+ let isNullable = false;
3394
+ let selected;
3395
+
3396
+ // A callback whose result writes a parameter nothing bound is given that parameter by the types its functions or its body give.
3397
+ const addCallbackEvidence = (parameter, result, callback) => {
3398
+ const declaredResult = this.#registry.getDeclaredType(declaration, parameter.type).resolveTypeParameters(typeArguments);
3399
+
3400
+ if (LanguageResolver.#holdsTypeParameter(declaredResult)) {
3401
+ const resultTypes = callback.isBody === true ? [result.type] : callback.functions.map(entry => entry.resultType);
3402
+
3403
+ resultTypes.forEach(type => this.#typeChecker.extractTypeArgumentEvidence(declaredResult, type, callbackEvidence));
3404
+ }
3405
+ };
3406
+
3407
+ // An options object is an object expression: one no other encloses builds the outermost object, whose fields are its options, and any other builds within the one enclosing it; each option is in scope after its value, as a field is.
3408
+ const optionsObject = declaration.arguments === 'options' && supplied.size > 0 ? (node.kind === 'indexerAccess' ? node.argument : node.arguments[offset]) : null;
3409
+ const objectExpression = optionsObject !== null && scope.objectRoot === null ? optionsObject : null;
3410
+ let argumentScope = scope;
3411
+
3412
+ if (objectExpression !== null) {
3413
+ this.#objectRootFields.set(objectExpression, new Map());
3414
+ argumentScope = { ...scope, objectRoot: LanguageResolver.#createObjectRoot(objectExpression) };
3415
+ }
3416
+
3417
+ // The arguments are resolved in the order they are written, an options object's fields among them, and the parameters left to their defaults after them.
3418
+ const ordered = [...[...supplied.keys()].map(name => parameters.find(parameter => parameter.name === name)), ...parameters.filter(parameter => !supplied.has(parameter.name))];
3419
+
3420
+ for (const parameter of ordered) {
3421
+ const given = supplied.get(parameter.name);
3422
+ // The type a value of Any it is given is checked against when it is evaluated.
3423
+ let checkedType;
3424
+
3425
+ if (given === undefined) {
3426
+ if (parameter.required) {
3427
+ this.#fail('NEXO3080', `'${NexoModule.writtenNameOf(declaration)}' requires the argument '${parameter.name}'`, pointer);
3428
+ }
3429
+
3430
+ resolved.push({ parameter, isDefault: true, value: parameter.default });
3431
+ continue;
3432
+ }
3433
+
3434
+ const valuePointer = given.pointer;
3435
+ const expected = this.#registry.getDeclaredType(declaration, parameter.type).resolveTypeParameters(typeArguments);
3436
+ let result;
3437
+ let callback;
3438
+
3439
+ if (parameter.kind === 'expression' || parameter.kind === 'predicate') {
3440
+ const isOfUnknownType = receiverType === null || LanguageResolver.#isNullOrNever(receiverType);
3441
+
3442
+ if (receiverType === null) {
3443
+ this.#fail('NEXO3500', `'${NexoModule.writtenNameOf(declaration)}' evaluates its expression against the items of the collection it follows, and a module member follows none`, valuePointer);
3444
+ }
3445
+
3446
+ if (receiverType.itemContextType === null && !isOfUnknownType) {
3447
+ this.#fail('NEXO3500', `'${NexoModule.writtenNameOf(declaration)}' evaluates its expression against the items of a list or the values of a dictionary, and ${receiverType.toNonNullable()} has none`, valuePointer);
3448
+ }
3449
+
3450
+ const itemType = isOfUnknownType ? Type.never : receiverType.itemContextType;
3451
+
3452
+ ({ result, callback } = this.#resolveItemArgument(given.node, valuePointer, { ...argumentScope, facts: scope.facts }, parameter, declaration, itemType, expected));
3453
+ addCallbackEvidence(parameter, result, callback);
3454
+
3455
+ // An expression or a predicate option is evaluated by the member, and has no single value: a name or a field of it after its initializer refuses a read, hiding any enclosing name of its spelling.
3456
+ if (optionsObject !== null) {
3457
+ argumentScope = { ...argumentScope, locals: argumentScope.locals.with(parameter.name, { optionKind: parameter.kind, result: null }) };
3458
+ }
3459
+
3460
+ if (objectExpression !== null) {
3461
+ this.#objectRootFields.get(objectExpression).set(parameter.name, { optionKind: parameter.kind });
3462
+ }
3463
+ } else if (parameter.kind === 'function') {
3464
+ ({ result, callback } = this.#resolveFunctionArgument(given.node, valuePointer, argumentScope, parameter, declaration, receiverType, typeArguments));
3465
+ addCallbackEvidence(parameter, result, callback);
3466
+
3467
+ // An implicit body option is called by the member against the argument it supplies, and has no single value, as an expression option has none.
3468
+ if (callback.isBody === true) {
3469
+ if (optionsObject !== null) {
3470
+ argumentScope = { ...argumentScope, locals: argumentScope.locals.with(parameter.name, { optionKind: 'implicitBody', result: null }) };
3471
+ }
3472
+
3473
+ if (objectExpression !== null) {
3474
+ this.#objectRootFields.get(objectExpression).set(parameter.name, { optionKind: 'implicitBody' });
3475
+ }
3476
+ }
3477
+ } else {
3478
+ // An argument resolved ahead, to select a signature or bind type parameters, in this scope at this place, where its parameter's type is no structure it could construct, so expecting the type
3479
+ // changes nothing, is taken as it was.
3480
+ const resolvedAhead = this.#resolvedArguments.get(given.node);
3481
+ const constructs = expected !== undefined && this.#constructibleTypeOf(expected.toNonNullable()) !== null;
3482
+
3483
+ this.#resolvedArguments.delete(given.node);
3484
+ result = resolvedAhead !== undefined && resolvedAhead.scope === argumentScope && resolvedAhead.pointer === valuePointer && !constructs
3485
+ ? resolvedAhead.result
3486
+ : this.#resolveExpecting(given.node, valuePointer, argumentScope, expected);
3487
+
3488
+ if (node.kind === 'indexerAccess') {
3489
+ this.#refuseFunctionKey(result, valuePointer);
3490
+ }
3491
+
3492
+ this.#requireValueArgument(result, parameter, declaration, valuePointer);
3493
+
3494
+ if (this.#requireArgumentType(result, expected, parameter, declaration, valuePointer, { allowsCheck: true }) === 'checked') {
3495
+ checkedType = expected;
3496
+ }
3497
+
3498
+ if (LanguageResolver.#propagatesNull(result, parameter)) {
3499
+ isNullable = true;
3500
+ }
3501
+ }
3502
+
3503
+ if (parameter.kind !== 'expression' && parameter.kind !== 'predicate' && callback?.isBody !== true) {
3504
+
3505
+ // A value or a function option is in scope after its value, through the options after it, read as it was evaluated; one of the outermost object is completed, and read through root after it as the value it was given, of that value's type.
3506
+ if (optionsObject !== null) {
3507
+ argumentScope = { ...argumentScope, locals: argumentScope.locals.with(parameter.name, { result, initializer: given.node, scope: argumentScope }) };
3508
+ }
3509
+
3510
+ if (objectExpression !== null) {
3511
+ this.#objectRootFields.get(objectExpression).set(parameter.name, { initializer: given.node });
3512
+ argumentScope = { ...argumentScope, objectRoot: LanguageResolver.#withRootField(argumentScope.objectRoot, parameter.name, result.type) };
3513
+ }
3514
+ }
3515
+
3516
+ if (parameter.fixed === true) {
3517
+ const selection = this.#resolveSelectedType(parameter, result, declaration, valuePointer);
3518
+
3519
+ selected = selection.selected ?? selected;
3520
+ }
3521
+
3522
+ resolved.push({ parameter, isDefault: false, pointer: valuePointer, node: given.node, value: parameter.fixed === true ? result.value : undefined, ...(callback === undefined ? {} : { callback }), ...(checkedType === undefined ? {} : { checkedType }) });
3523
+ }
3524
+
3525
+ return { arguments: resolved, isNullable, selected, buildsObjectRoot: objectExpression !== null, callbackEvidence };
3526
+ }
3527
+
3528
+ /**
3529
+ * Refuses a value argument its parameter can't take: a function, or a value of a type it doesn't admit, the null of a parameter that propagates null aside, which answers for the call. It answers whether the argument is
3530
+ * assignable, or a value of Any checked when it is evaluated, which a caller that records the check allows, and any other refuses.
3531
+ * @returns {'assignable' | 'checked'}
3532
+ */
3533
+ #requireArgumentType(result, expected, parameter, declaration, pointer, { allowsCheck = false } = {}) {
3534
+ const actual = result.type;
3535
+
3536
+ const held = LanguageResolver.#heldReferenceOf(actual);
3537
+
3538
+ if (held === 'function') {
3539
+ this.#fail('NEXO3071', `The argument '${parameter.name}' of '${NexoModule.writtenNameOf(declaration)}' takes a value, and a function is none: a function is passed to a local function, or to a parameter its member declares to take one`, pointer);
3540
+ }
3541
+
3542
+ if (held === 'objectRoot') {
3543
+ this.#fail('NEXO3071', `The argument '${parameter.name}' of '${NexoModule.writtenNameOf(declaration)}' takes a value, and root, the object being built, is none: read its fields, as root.name`, pointer);
3544
+ }
3545
+
3546
+ const assignability = parameter.nulls === 'propagate' ? (actual.kind === 'null' ? 'assignable' : this.#typeChecker.assignabilityOf(actual.toNonNullable(), expected.toNonNullable())) : this.#typeChecker.assignabilityOf(actual, expected);
3547
+ const isAdmitted = assignability === 'assignable' || (assignability === 'checked' && allowsCheck);
3548
+
3549
+ if (!isAdmitted) {
3550
+ const [expectedName, actualName] = Type.namesOf(expected, actual);
3551
+
3552
+ this.#fail('NEXO3042', `The argument '${parameter.name}' of '${NexoModule.writtenNameOf(declaration)}' expects ${expectedName} and finds ${actualName}`, pointer);
3553
+ }
3554
+
3555
+ return assignability;
3556
+ }
3557
+
3558
+ /** Whether the argument's null makes the call answer null: its parameter propagates null, and the argument may be null, whatever the parameter's own type admits, as evaluation answers null without invoking. */
3559
+ static #propagatesNull(result, parameter) {
3560
+ return parameter.nulls === 'propagate' && result.type.isNullable;
3561
+ }
3562
+
3563
+ #requireValueArgument(result, parameter, declaration, pointer) {
3564
+ if (result.kind !== 'value') {
3565
+ this.#fail('NEXO3048', `The argument '${parameter.name}' of '${NexoModule.writtenNameOf(declaration)}' is ${LanguageResolver.#descriptionOf(result.kind)}, and an argument is a value`, pointer);
3566
+ }
3567
+ }
3568
+
3569
+ /** Resolves the type a fixed argument selects through its parameter's type selector, which must be known before anything is evaluated. */
3570
+ #resolveSelectedType(parameter, result, declaration, pointer) {
3571
+ if (!result.isFixed || typeof result.value !== 'string') {
3572
+ this.#fail(result.isFixed ? 'NEXO3007' : 'NEXO3006', `The argument '${parameter.name}' of '${NexoModule.writtenNameOf(declaration)}' selects from the vocabulary, so it is a string the expression's resolution settles without evaluating a member, a literal, a script parameter, or a local, a conditional binding, a conditional, a '??', an 'and' or an 'or' that settles to one, never null`, pointer);
3573
+ }
3574
+
3575
+ const name = result.value;
3576
+ const mapping = this.#registry.getMapping(parameter.selector, name);
3577
+
3578
+ if (mapping === undefined) {
3579
+ this.#fail('NEXO3050', `The type selector '${parameter.selector}' maps no type to the key '${name}'`, pointer);
3580
+ }
3581
+
3582
+ return { selected: { name, type: this.#registry.getDeclaredType(mapping, mapping.type) } };
3583
+ }
3584
+
3585
+ /**
3586
+ * Resolves an object expression: the value it follows, resolved outside it, a host call's making it that call's result projection, refused where the call supplies nothing; the implicit parameter of its entries, each item of a
3587
+ * list it follows or the value, a fresh key standing for it, and the enclosing one where it follows none; the outermost object the object root refers to where no object expression encloses it; its locals in order, each against
3588
+ * the scope with those before it; and its fields in order, each a value that is data, holds neither the object root nor a function reference, and is in scope by its name for the fields after it, a field of the outermost object read
3589
+ * through the object root once completed. It answers the object's type, a list of them for a list it follows, nullable where what it follows is.
3590
+ */
3591
+ #resolveObjectExpression(node, pointer, scope) {
3592
+ const target = node.context === undefined ? null : this.#resolveNode(node.context, `${pointer}/context`, scope);
3593
+
3594
+ if (target?.suppliesNothing === true) {
3595
+ this.#fail('NEXO3049', 'The host call this object expression follows declares no result, since its host supplies no values, so it takes no result projection', pointer);
3596
+ }
3597
+
3598
+ // An object expression that follows a host call is its result projection, and the script still ends in the call.
3599
+ const kind = target?.kind === 'hostCall' ? 'hostCall' : 'value';
3600
+
3601
+ let contextType = scope.contextType;
3602
+ let contextKey = scope.contextKey;
3603
+
3604
+ if (target !== null) {
3605
+ contextKey = {};
3606
+
3607
+ const appliedType = target.type.isList ? target.type.itemType : target.type;
3608
+
3609
+ // An object expression following null, or each item of a list of nulls, is never applied, so its fields are resolved against no value.
3610
+ contextType = appliedType.kind === 'null' ? Type.never : appliedType.toNonNullable();
3611
+ }
3612
+
3613
+ // An object expression no other encloses builds the outermost object, which the object root refers to; any other builds within that object. Its receiver was resolved outside it.
3614
+ const objectExpression = scope.objectRoot === null ? node : null;
3615
+ let objectRoot = objectExpression === null ? scope.objectRoot : LanguageResolver.#createObjectRoot(objectExpression);
3616
+
3617
+ if (objectExpression !== null) {
3618
+ this.#objectRootFields.set(objectExpression, new Map());
3619
+ }
3620
+
3621
+ // Each local is resolved where it is written, against the implicit parameter and the locals before it, and every field sees them all.
3622
+ let locals = scope.locals;
3623
+ // An object expression following no value keeps the implicit parameter around it, so an implicit body it stands in reads that body's own `it`.
3624
+ const scopeHere = () => ({ ...scope, contextType, contextKey, locals, objectRoot });
3625
+
3626
+ node.locals?.forEach((local, index) => {
3627
+ const valuePointer = `${pointer}/locals/${index}/value`;
3628
+
3629
+ if (local.value.kind === 'function') {
3630
+ locals = locals.with(local.name, LanguageResolver.#functionLocalOf(local.value, valuePointer, scopeHere()));
3631
+
3632
+ return;
3633
+ }
3634
+
3635
+ const result = this.#resolveNode(local.value, valuePointer, scopeHere());
3636
+
3637
+ if (result.kind !== 'value') {
3638
+ this.#fail('NEXO3048', `The local '${local.name}' is ${LanguageResolver.#descriptionOf(result.kind)}, and a local is a value`, valuePointer);
3639
+ }
3640
+
3641
+ locals = locals.with(local.name, { result, initializer: local.value, scope: scopeHere() });
3642
+ });
3643
+
3644
+ const fieldTypes = new Map();
3645
+ const constructed = target === null ? (this.#expectedTypes.get(node) ?? null) : null;
3646
+ const shape = constructed === null ? null : this.#registry.getDataShape(constructed);
3647
+
3648
+ node.objectFields.forEach((field, index) => {
3649
+ const fieldPointer = `${pointer}/objectFields/${index}`;
3650
+ const { name } = field;
3651
+
3652
+ if (shape !== null && !shape.has(name)) {
3653
+ this.#fail('NEXO3086', `The ${constructed} written here has no property '${name}'; it holds ${[...shape.keys()].join(', ')}`, fieldPointer);
3654
+ }
3655
+
3656
+ const result = this.#resolveExpecting(field.value, `${fieldPointer}/value`, scopeHere(), shape?.get(name));
3657
+
3658
+ if (result.kind !== 'value') {
3659
+ this.#fail('NEXO3048', `The field '${name}' is ${LanguageResolver.#descriptionOf(result.kind)}, and a field is a value`, `${fieldPointer}/value`);
3660
+ }
3661
+
3662
+ const heldReference = LanguageResolver.#heldReferenceOf(result.type);
3663
+
3664
+ if (heldReference === 'objectRoot') {
3665
+ this.#fail('NEXO3047', `The field '${name}' would hold root, the object being built, which no result holds; read its fields, as root.name`, `${fieldPointer}/value`);
3666
+ }
3667
+
3668
+ if (heldReference === 'function') {
3669
+ this.#fail('NEXO3035', `The field '${name}' would hold a function, which is used within the expression and never part of its result: call it, as name(…)`, `${fieldPointer}/value`);
3670
+ }
3671
+
3672
+ if (shape === null && !this.#registry.isDataOnceChecked(result.type)) {
3673
+ this.#fail('NEXO3043', `The field '${name}' would hold ${result.type}, which is no data; read the facts it needs through an object expression`, `${fieldPointer}/value`);
3674
+ }
3675
+
3676
+ fieldTypes.set(name, result.type);
3677
+
3678
+ // A field is in scope after its value, through the fields after it, its value read as it was evaluated.
3679
+ locals = locals.with(name, { result, initializer: field.value, scope: scopeHere() });
3680
+
3681
+ // A field of the outermost object is completed, and read through a reference to the root written after it, as the value its evaluation gave.
3682
+ if (objectExpression !== null) {
3683
+ this.#objectRootFields.get(objectExpression).set(name, { initializer: field.value });
3684
+ objectRoot = LanguageResolver.#withRootField(objectRoot, name, result.type);
3685
+ }
3686
+ });
3687
+
3688
+ if (shape !== null) {
3689
+ return this.#resolveConstruction(constructed, shape, fieldTypes, pointer);
3690
+ }
3691
+
3692
+ const objectType = Type.createObjectType(fieldTypes);
3693
+ const isNullable = target?.type.isNullable === true;
3694
+
3695
+ // A list's null item projects to null at its position, so the objects of a list whose items can be null can be null too; the projection keeps its items' type, which each item's source follows.
3696
+ if (target?.type.isList === true) {
3697
+ this.#resolutions.set(pointer, { kind: 'projection', itemType: target.type.itemType });
3698
+
3699
+ return { type: Type.createListType(target.type.itemType.isNullable ? objectType.toNullable() : objectType, isNullable), kind, isFixed: false };
3700
+ }
3701
+
3702
+ return { type: isNullable ? objectType.toNullable() : objectType, kind, isFixed: false };
3703
+ }
3704
+
3705
+ /**
3706
+ * Resolves the object root, a reference to the outermost object, to the type its scope holds, which identifies the object expression that builds it and the fields completed where it is written, the fields it reads; each
3707
+ * field completed gives a new type with a field map of its own, so a reference written earlier, and a scope captured earlier, keep the fields they saw.
3708
+ */
3709
+ #resolveObjectRoot(pointer, scope) {
3710
+ const { objectRoot } = scope;
3711
+
3712
+ if (objectRoot === null) {
3713
+ this.#fail('NEXO2018', "'root' refers to the outermost object being built, and no object expression encloses it here", pointer);
3714
+ }
3715
+
3716
+ return { type: Type.createObjectRootType(objectRoot.objectExpression, new Map(objectRoot.fields.slice(0, objectRoot.count))), kind: 'value', isFixed: false };
3717
+ }
3718
+
3719
+ /**
3720
+ * The outermost object the expression builds, as a scope holds it, with none of its fields completed: the expression, its completed fields, each a name beside its type, and how many of them the scope sees, the
3721
+ * fields completed where the scope was made. Its type is made where `root` is read, rather than for every field completed, which copied every field before it and so grew with the square of an object's fields.
3722
+ */
3723
+ static #createObjectRoot(objectExpression) {
3724
+ return { objectExpression, fields: [], count: 0 };
3725
+ }
3726
+
3727
+ /**
3728
+ * The object root with the field completed after those the scope sees. A root's fields are completed in order, so it shares their list with the scope it extends, which sees no further than its count; one extended
3729
+ * from any but the latest of them copies the fields it sees.
3730
+ */
3731
+ static #withRootField({ objectExpression, fields, count }, name, type) {
3732
+ const completed = count === fields.length ? fields : fields.slice(0, count);
3733
+
3734
+ completed.push([name, type]);
3735
+
3736
+ return { objectExpression, fields: completed, count: count + 1 };
3737
+ }
3738
+
3739
+ /**
3740
+ * Resolves a field read through the object root: one of the fields completed where the reference was written, read as an object's is, with no argument; its resolution keeps the field's value, so what reads it
3741
+ * reuses the branches the field selected and compares it within its tolerance. It answers its result and its resolution, with no use, and records neither.
3742
+ */
3743
+ #resolveObjectRootField(node, pointer, receiverType) {
3744
+ if (node.kind !== 'memberAccess') {
3745
+ this.#fail(node.kind === 'call' ? 'NEXO3017' : 'NEXO3012', `'root' is read through its fields, as root.name or root.[name], and '${LanguageResolver.#accessOf(node).name ?? '[…]'}' ${node.kind === 'call' ? 'is called' : 'is an indexer'}`, pointer);
3746
+ }
3747
+
3748
+ const optionKind = this.#objectRootFields.get(receiverType.objectExpression).get(node.name)?.optionKind;
3749
+
3750
+ if (optionKind !== undefined) {
3751
+ this.#fail('NEXO3046', LanguageResolver.#unreadableOptionMessageOf(node.name, optionKind), pointer);
3752
+ }
3753
+
3754
+ const fieldType = receiverType.fieldTypes.get(node.name);
3755
+
3756
+ if (fieldType === undefined) {
3757
+ const completed = [...receiverType.fieldTypes.keys()];
3758
+
3759
+ this.#fail('NEXO3044', `'${node.name}' is no field of root completed where the reference to it is written; ${completed.length === 0 ? 'none is completed there' : `those completed are ${completed.join(', ')}`}`, pointer);
3760
+ }
3761
+
3762
+ return {
3763
+ result: { type: receiverType.isNullable ? fieldType.toNullable() : fieldType, kind: 'value', isFixed: false },
3764
+ resolution: { kind: 'objectRootField', name: node.name, initializer: this.#objectRootFields.get(receiverType.objectExpression).get(node.name).initializer },
3765
+ use: null,
3766
+ };
3767
+ }
3768
+
3769
+ /** The refusal of a read of an expression, a predicate or an implicit body option, which the member evaluates and which has no value to read. */
3770
+ static #unreadableOptionMessageOf(name, kind) {
3771
+ const what = kind === 'predicate' ? 'a predicate' : kind === 'implicitBody' ? 'an implicit body' : 'an expression';
3772
+
3773
+ return `Option '${name}' is ${what} evaluated by the member against the implicit parameter it supplies; it has no single value to read here`;
3774
+ }
3775
+
3776
+ /** What the type holds that no result may, itself, as a list's item or as an object's field: the object root, or a function reference; null for neither. */
3777
+ static #heldReferenceOf(type) {
3778
+ switch (type.kind) {
3779
+ case 'objectRoot':
3780
+ case 'function':
3781
+ return type.kind;
3782
+ case 'named':
3783
+ return type.typeArguments.reduce((held, typeArgument) => held ?? LanguageResolver.#heldReferenceOf(typeArgument), null);
3784
+ case 'object':
3785
+ return [...type.fieldTypes.values()].reduce((held, fieldType) => held ?? LanguageResolver.#heldReferenceOf(fieldType), null);
3786
+ default:
3787
+ return null;
3788
+ }
3789
+ }
3790
+
3791
+ /**
3792
+ * The callback contract a function parameter declares, its member's type parameters bound: the types of the arguments the member calls the function with, in order, and the type of the result it takes from it, any
3793
+ * value where that result writes a parameter nothing bound yet, which the callback's result binds. A type parameter of a receiver of unknown type, which nothing binds, is a never, which fits wherever a value of any type
3794
+ * stands.
3795
+ */
3796
+ #createCallbackContract(parameter, declaration, receiverType, typeArguments) {
3797
+ const isOfUnknownType = receiverType !== null && LanguageResolver.#isNullOrNever(receiverType);
3798
+ const resolve = text => {
3799
+ const type = this.#registry.getDeclaredType(declaration, text).resolveTypeParameters(typeArguments);
3800
+
3801
+ return isOfUnknownType && LanguageResolver.#holdsTypeParameter(type) ? Type.never : type;
3802
+ };
3803
+
3804
+ const resultType = resolve(parameter.type);
3805
+
3806
+ return { memberName: NexoModule.writtenNameOf(declaration), parameterName: parameter.name, parameterTypes: parameter.parameterTypes.map(resolve), resultType: LanguageResolver.#holdsTypeParameter(resultType) ? null : resultType };
3807
+ }
3808
+
3809
+ /**
3810
+ * Resolves the argument a function parameter is given, and the callback the member calls: a reference to a module's function, which the parameter's callback contract selects; for a contract of one argument, an implicit body, an argument
3811
+ * no arrow function is that reads an `it` of its own scope, which the argument the member calls it with stands for; or a function reference, each function it may be resolved against the contract.
3812
+ * An argument's own scope is told by the implicit parameter it resolves against, so an `it` of a projection or of an expression, a predicate or a function argument within it, each of which has its own, makes it no body.
3813
+ */
3814
+ #resolveFunctionArgument(node, pointer, scope, parameter, declaration, receiverType, typeArguments) {
3815
+ const contract = this.#createCallbackContract(parameter, declaration, receiverType, typeArguments);
3816
+
3817
+ if (node.kind === 'memberAccess' && node.receiver.kind === 'namespace') {
3818
+ const result = this.#resolveThroughNamespace(node, pointer, scope, contract);
3819
+
3820
+ return { result, callback: this.#resolveCallback(result, contract, pointer, scope) };
3821
+ }
3822
+
3823
+ if (contract.parameterTypes.length === 1 && node.kind !== 'function') {
3824
+ // Whether the argument reads its own implicit parameter, which its own context key identifies; it holds no syntax of the body.
3825
+ const argumentBody = { key: {}, isRead: false };
3826
+ const result = this.#resolveNode(node, pointer, { ...scope, contextType: contract.parameterTypes[0], contextKey: argumentBody.key, argumentBody });
3827
+
3828
+ this.#requireValueArgument(result, parameter, declaration, pointer);
3829
+
3830
+ if (!argumentBody.isRead) {
3831
+ return { result, callback: this.#resolveCallback(result, contract, pointer, scope) };
3832
+ }
3833
+
3834
+ if (!this.#fitsResult(result.type, contract.resultType)) {
3835
+ this.#fail('NEXO3036', `The implicit body given as '${parameter.name}' of '${NexoModule.writtenNameOf(declaration)}' gives ${result.type}, and its contract takes ${contract.resultType ?? 'any value'}`, pointer);
3836
+ }
3837
+
3838
+ return { result, callback: { isBody: true, parameterTypes: contract.parameterTypes, resultType: contract.resultType } };
3839
+ }
3840
+
3841
+ const result = this.#resolveNode(node, pointer, scope);
3842
+
3843
+ this.#requireValueArgument(result, parameter, declaration, pointer);
3844
+
3845
+ return { result, callback: this.#resolveCallback(result, contract, pointer, scope) };
3846
+ }
3847
+
3848
+ /**
3849
+ * Resolves the argument an expression or a predicate parameter is given, which is a function of each item, as a function parameter's of one argument is: a function the member calls with each item, an arrow function,
3850
+ * a function a name holds or a module's function by reference, resolved against the contract of the item's type and the expression's result, a predicate's any value; or any other argument, its body, evaluated against
3851
+ * each item, its `it`, which it reads or not, so a body reading no `it` gives each item the same value. Either is a callback of that contract, which the member calls with one item while its own invocation lasts.
3852
+ */
3853
+ #resolveItemArgument(node, pointer, scope, parameter, declaration, itemType, expected) {
3854
+ // A function of the item is called with each item as it is, null among them where the list holds nulls, while a body reads each item as a value that is not, unless its parameter accepts null items.
3855
+ // An expression whose result writes a parameter nothing bound yet takes any value, which then binds it.
3856
+ const contract = { memberName: NexoModule.writtenNameOf(declaration), parameterName: parameter.name, parameterTypes: [itemType], resultType: parameter.kind === 'expression' && !LanguageResolver.#holdsTypeParameter(expected) ? expected : null };
3857
+ let result;
3858
+
3859
+ if (node.kind === 'function') {
3860
+ result = this.#resolveNode(node, pointer, scope);
3861
+ } else if (node.kind === 'memberAccess' && node.receiver.kind === 'namespace') {
3862
+ result = this.#resolveThroughNamespace(node, pointer, scope, contract);
3863
+ } else {
3864
+ // Whether the argument reads its own implicit parameter, which its own context key identifies; it holds no syntax of the body.
3865
+ const argumentBody = { key: {}, isRead: false };
3866
+
3867
+ const bodyScope = { ...scope, contextType: parameter.nulls === 'accept' ? itemType : itemType.toNonNullable(), contextKey: argumentBody.key, argumentBody };
3868
+
3869
+ // A predicate tests the truthiness of what its body gives, so the values of a logical expression there need share no type.
3870
+ result = parameter.kind === 'predicate' ? this.#resolveTested(node, pointer, bodyScope) : this.#resolveNode(node, pointer, bodyScope);
3871
+
3872
+ if (result.type?.kind === 'function' && argumentBody.isRead) {
3873
+ this.#fail('NEXO3074', `The expression of '${NexoModule.writtenNameOf(declaration)}' reads each item as 'it' and gives a function; a function of the item is written as the argument itself, as ${declaration.name}(x => …)`, pointer);
3874
+ }
3875
+ }
3876
+
3877
+ this.#requireValueArgument(result, parameter, declaration, pointer);
3878
+
3879
+ if (result.type?.kind === 'function') {
3880
+ return { result, callback: this.#resolveCallback(result, contract, pointer, scope) };
3881
+ }
3882
+
3883
+ const unfit = LanguageResolver.#typesOf(result).find(type => !this.#fitsResult(type, contract.resultType));
3884
+
3885
+ if (unfit !== undefined) {
3886
+ this.#fail(contract.resultType === null ? 'NEXO3028' : 'NEXO3042', contract.resultType === null ? `The expression of '${NexoModule.writtenNameOf(declaration)}' produces ${unfit}, a type no receiver gives` : `The expression of '${NexoModule.writtenNameOf(declaration)}' must produce ${expected} and produces ${unfit}`, pointer);
3887
+ }
3888
+
3889
+ return { result, callback: { isBody: true, parameterTypes: contract.parameterTypes, resultType: contract.resultType } };
3890
+ }
3891
+
3892
+ /**
3893
+ * Whether a function's result of the type is one its callback contract takes: a value of the contract's result type, a value of Any among them, which the callback checks against it as it is evaluated, or, where the
3894
+ * contract takes any value, as a predicate's does, one of a type a receiver gives.
3895
+ */
3896
+ #fitsResult(type, resultType) {
3897
+ return resultType === null ? type.kind !== 'function' && !LanguageResolver.#holdsTypeParameter(type) : this.#typeChecker.assignabilityOf(type, resultType) !== 'refused';
3898
+ }
3899
+
3900
+ /**
3901
+ * Resolves a function reference given to a function parameter against its callback contract: each function it may be, a function the script declares, its body resolved for the contract's argument types and its result checked against the contract's result type,
3902
+ * or a reference to a module's function, whose declaration takes what the contract passes, with its argument map, of as many arguments as the contract passes, and the result type its implementation answers, its type parameters bound by
3903
+ * the contract's receiver. It answers the callback, those functions with the contract's types, which evaluation checks the member's arguments and the function's result against. The member calls it while its own invocation
3904
+ * lasts, with as many arguments as the contract declares.
3905
+ */
3906
+ #resolveCallback(result, contract, pointer, scope) {
3907
+ const { type } = result;
3908
+ const { memberName, parameterName, parameterTypes, resultType } = contract;
3909
+
3910
+ if (type.kind !== 'function') {
3911
+ const implicit = parameterTypes.length === 1 ? ', as an arrow function, a function a name holds, a module\'s function by reference or an implicit body gives' : '';
3912
+
3913
+ this.#fail('NEXO3073', `The parameter '${parameterName}' of '${memberName}' takes a function${implicit}, and finds ${type}`, pointer);
3914
+ }
3915
+
3916
+ if (type.isNullable) {
3917
+ this.#fail('NEXO3072', `The parameter '${parameterName}' of '${memberName}' takes a function, and this one may be null`, pointer);
3918
+ }
3919
+
3920
+ const argumentResults = parameterTypes.map(parameterType => ({ type: parameterType, kind: 'value', isFixed: false }));
3921
+
3922
+ const functions = type.functions.map((target, index) => {
3923
+ if (target.reference !== undefined) {
3924
+ // A reference bound to its receiver takes the arguments the contract passes as its own, after the receiver it captured.
3925
+ const reached = target.isBound === true ? { ...contract, parameterTypes: [target.receiverType, ...parameterTypes] } : contract;
3926
+
3927
+ if (!this.#takesContract(target.reference, reached)) {
3928
+ this.#fail('NEXO3036', `The function given as '${parameterName}' of '${memberName}' is called with ${parameterTypes.join(', ')} and answers ${resultType ?? 'any value'}, and '${this.#referenceNameOf(target.reference)}' is ${this.#signatureOf(target.reference)}`, pointer);
3929
+ }
3930
+
3931
+ const typeArguments = this.#contractTypeArgumentsOf(target.reference, reached);
3932
+ const ownCount = reached.parameterTypes.length - (this.#registry.takesReceiver(target.reference) ? 1 : 0);
3933
+
3934
+ return {
3935
+ reference: target.reference,
3936
+ argumentMap: LanguageResolver.#createArgumentMap(target.reference, ownCount),
3937
+ resultType: this.#registry.getDeclaredType(target.reference, target.reference.result).resolveTypeParameters(typeArguments),
3938
+ };
3939
+ }
3940
+
3941
+ const count = target.function.parameters.length;
3942
+
3943
+ if (count !== parameterTypes.length) {
3944
+ this.#fail('NEXO3036', `The function given as '${parameterName}' of '${memberName}' takes ${count === 1 ? 'one argument' : `${count} arguments`}, and its contract passes ${parameterTypes.length}`, pointer);
3945
+ }
3946
+
3947
+ const resolved = this.#resolveScriptFunctionCall(target, parameterName, argumentResults, null, scope, type.functions.length === 1 ? `${pointer}/callback` : `${pointer}/callbacks/${index}`, pointer);
3948
+
3949
+ if (!this.#fitsResult(resolved.type, resultType)) {
3950
+ this.#fail('NEXO3036', `The function given as '${parameterName}' of '${memberName}' gives ${resolved.type}, and its contract takes ${resultType ?? 'any value'}`, pointer);
3951
+ }
3952
+
3953
+ return { function: resolved.function, body: resolved.body, parameters: resolved.parameters, resultType: resolved.type };
3954
+ });
3955
+
3956
+ return { functions, parameterTypes, resultType };
3957
+ }
3958
+
3959
+ /** The fields of each outermost object, by the object expression that builds it and then by name, each with its initializer, which a field read through the object root follows. @type {WeakMap<object, Map<string, { initializer: object } | { optionKind: string }>>} */
3960
+ #objectRootFields = new WeakMap();
3961
+
3962
+ /** Refuses the definition with the code of the condition it fails, a structure error for a structure code and a validation error otherwise, located at the source of the pointer. */
3963
+ #fail(code, message, pointer) {
3964
+ const definitionPointer = ResolvedDefinition.resolveDefinitionPointer(pointer, this.#functionBodyCopies);
3965
+ const location = this.#locations?.locate(definitionPointer) ?? { pointer: definitionPointer };
3966
+
3967
+ throw code.startsWith('NEXO2') ? NexoErrors.structure(code, message, location) : NexoErrors.validation(code, message, location);
3968
+ }
3969
+
3970
+ /** The copy of a function's body each call resolves, with the pointer it is resolved under and the pointer of the body it copies. @type {{ node: object, pointer: string, bodyPointer: string }[]} */
3971
+ #functionBodyCopies = [];
3972
+
3973
+ /** What a resolved node of the kind is, for a message: a host call, a function a local holds, or a value. */
3974
+ static #descriptionOf(kind) {
3975
+ return kind === 'hostCall' ? 'a host call' : `a ${kind}`;
3976
+ }
3977
+
3978
+ /** The node written optionally, a call's member access its callee. */
3979
+ static #asOptional(node) {
3980
+ return node.kind === 'call' ? { ...node, callee: { ...node.callee, optional: true } } : { ...node, optional: true };
3981
+ }
3982
+
3983
+ /** The property access that writes the node's name after its receiver, optional where the node is, and an extension access where the node is one. */
3984
+ static #createPropertyAccess(node) {
3985
+ const access = LanguageResolver.#accessOf(node);
3986
+
3987
+ return { kind: 'memberAccess', receiver: access.receiver, name: access.name, ...(access.optional === true ? { optional: true } : {}), ...(access.extension === undefined ? {} : { extension: access.extension }) };
3988
+ }
3989
+
3990
+ /** Whether the access is a name of an extension access as a definition writes it, its first, which holds the colon, or a later one, which continues it, so its names are read before it is resolved. */
3991
+ static #isWrittenExtensionAccess(access) {
3992
+ return access.kind === 'memberAccess' && (access.extension === true || access.continues === true);
3993
+ }
3994
+
3995
+ /**
3996
+ * The access a node of a member makes: a property access and an indexer are their own, and a call of a member the member access its callee is, which holds its receiver, its name, whether it is optional and the
3997
+ * extension it selects.
3998
+ */
3999
+ static #accessOf(node) {
4000
+ return node.kind === 'call' ? node.callee : node;
4001
+ }
4002
+
4003
+ /** The pointer of the extension a node of a member selects, which a call holds within its callee. */
4004
+ static #extensionPointerOf(node, pointer) {
4005
+ return node.kind === 'call' ? `${pointer}/callee/extension` : `${pointer}/extension`;
4006
+ }
4007
+
4008
+ /** The pointer of the receiver of a node of a member, which a call holds within its callee. */
4009
+ static #receiverPointerOf(node, pointer) {
4010
+ return node.kind === 'call' ? `${pointer}/callee/receiver` : `${pointer}/receiver`;
4011
+ }
4012
+
4013
+ /** The call of a member a property access writes with its arguments, its receiver's optional mark kept on its callee. */
4014
+ static #createCall(access, args) {
4015
+ return { kind: 'call', callee: LanguageResolver.#createPropertyAccess(access), arguments: args };
4016
+ }
4017
+
4018
+ /** What a call of the declaration makes: a host call where its host invokes it, and a value otherwise. */
4019
+ static #getResultKind(declaration) {
4020
+ return NexoModule.isInvokedByHost(declaration) ? 'hostCall' : 'value';
4021
+ }
4022
+
4023
+ /** Whether the type is null, whose only value is null, or Never, which has no values. */
4024
+ static #isNullOrNever(type) {
4025
+ return type.kind === 'null' || type.kind === 'never';
4026
+ }
4027
+
4028
+ /** Whether the type writes a type parameter anywhere in it. */
4029
+ static #holdsTypeParameter(type) {
4030
+ return LanguageResolver.#typeParameterOf(type) !== null;
4031
+ }
4032
+
4033
+ /** The first type parameter the type writes, or null where it writes none. */
4034
+ static #typeParameterOf(type) {
4035
+ if (type.kind === 'typeParameter') {
4036
+ return type;
4037
+ }
4038
+
4039
+ if (type.typeArguments.length > 0) {
4040
+ return type.typeArguments.map(typeArgument => LanguageResolver.#typeParameterOf(typeArgument)).find(found => found !== null) ?? null;
4041
+ }
4042
+
4043
+ return type.fieldTypes === null ? null : [...type.fieldTypes.values()].map(fieldType => LanguageResolver.#typeParameterOf(fieldType)).find(found => found !== null) ?? null;
4044
+ }
4045
+
4046
+ /**
4047
+ * The member's name qualified by the receiver type the receiver applies to it by, the first its matching takes, as `node.root` and `shadow.root` are, so two members of one name are told apart; a member it applies to by
4048
+ * a type parameter alone, which takes a receiver of any type, keeps its name alone. A list is named by its type, Nexo's `List`, as `List.count` is.
4049
+ */
4050
+ #qualifiedNameOf(declaration, receiverType) {
4051
+ return LanguageResolver.#formatQualifiedName(declaration, this.#typeChecker.resolveApplyingReceiverType(declaration, receiverType));
4052
+ }
4053
+
4054
+ /** The member's name qualified by the receiver type given, its type's name, or its name alone where none is given or that type is a type parameter. */
4055
+ static #formatQualifiedName(declaration, type) {
4056
+ // An unnamed indexer is named as its access is written, brackets after its receiver.
4057
+ const isUnnamedIndexer = declaration.kind === 'indexer' && declaration.name === undefined;
4058
+
4059
+ if (type === undefined || type.kind === 'typeParameter') {
4060
+ return isUnnamedIndexer ? '[]' : declaration.name;
4061
+ }
4062
+
4063
+ const typeName = type.kind === 'named' ? type.name : type.kind;
4064
+
4065
+ return isUnnamedIndexer ? `${typeName}[]` : `${typeName}.${declaration.name}`;
4066
+ }
4067
+
4068
+ }