@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.
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +148 -6
- package/package.json +23 -3
- package/src/common/ArrayUtils.mjs +16 -0
- package/src/common/ObjectUtils.mjs +33 -0
- package/src/language/DefinitionLocations.mjs +41 -0
- package/src/language/DefinitionValidator.mjs +1265 -0
- package/src/language/DefinitionWriter.mjs +475 -0
- package/src/language/Dictionary.mjs +52 -0
- package/src/language/Evaluator.mjs +3480 -0
- package/src/language/ImportedVocabulary.mjs +137 -0
- package/src/language/Imports.mjs +141 -0
- package/src/language/Index.mjs +29 -0
- package/src/language/LanguageResolver.mjs +4068 -0
- package/src/language/Limits.mjs +78 -0
- package/src/language/LineStructure.mjs +385 -0
- package/src/language/Lowering.mjs +453 -0
- package/src/language/MemberSelector.mjs +89 -0
- package/src/language/Names.mjs +65 -0
- package/src/language/NexoError.mjs +145 -0
- package/src/language/Pair.mjs +21 -0
- package/src/language/ParameterBindings.mjs +361 -0
- package/src/language/ParsedTexts.mjs +62 -0
- package/src/language/Parser.mjs +1817 -0
- package/src/language/Range.mjs +51 -0
- package/src/language/RecentlyUsed.mjs +50 -0
- package/src/language/ResolvedDefinition.mjs +219 -0
- package/src/language/ResultPath.mjs +101 -0
- package/src/language/ScopeLocals.mjs +102 -0
- package/src/language/StructureTypes.mjs +48 -0
- package/src/language/SyntaxTree.mjs +203 -0
- package/src/language/TextLocations.mjs +185 -0
- package/src/language/TextPositions.mjs +23 -0
- package/src/language/Tokenizer.mjs +917 -0
- package/src/language/Type.mjs +401 -0
- package/src/language/TypeChecker.mjs +449 -0
- package/src/language/ValueCapture.mjs +315 -0
- package/src/language/Versions.mjs +14 -0
- package/src/language/vocabulary/Declarations.mjs +64 -0
- package/src/language/vocabulary/ItemPositions.mjs +48 -0
- package/src/language/vocabulary/ListOrdering.mjs +145 -0
- package/src/language/vocabulary/MemberFailure.mjs +21 -0
- package/src/language/vocabulary/NexoModule.mjs +1119 -0
- package/src/language/vocabulary/NexoRegistry.mjs +1540 -0
- package/src/language/vocabulary/TypeNameScope.mjs +63 -0
- package/src/language/vocabulary/Vocabulary.mjs +640 -0
- package/src/nexo.d.ts +743 -0
- package/src/nexo.js +338 -0
package/src/nexo.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nexo — an embeddable language with no domain of its own, whose host declares the typed vocabulary its scripts use: reads, validates and writes source and definitions, creates the modules that declare a vocabulary and the registries a host resolves scripts against,
|
|
3
|
+
* binds values, and evaluates a resolved script for its host, describing its result; and says which build it is and which specification it follows
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DefinitionValidator } from './language/DefinitionValidator.mjs';
|
|
7
|
+
import { DefinitionWriter } from './language/DefinitionWriter.mjs';
|
|
8
|
+
import { Evaluator } from './language/Evaluator.mjs';
|
|
9
|
+
import { Imports } from './language/Imports.mjs';
|
|
10
|
+
import { Limits } from './language/Limits.mjs';
|
|
11
|
+
import { LanguageResolver } from './language/LanguageResolver.mjs';
|
|
12
|
+
import { NexoError, NexoErrors } from './language/NexoError.mjs';
|
|
13
|
+
import { ParameterBindings } from './language/ParameterBindings.mjs';
|
|
14
|
+
import { ParsedTexts } from './language/ParsedTexts.mjs';
|
|
15
|
+
import { ResultPath } from './language/ResultPath.mjs';
|
|
16
|
+
import { Versions } from './language/Versions.mjs';
|
|
17
|
+
import { Declarations } from './language/vocabulary/Declarations.mjs';
|
|
18
|
+
import { NexoModule } from './language/vocabulary/NexoModule.mjs';
|
|
19
|
+
import { NexoRegistry } from './language/vocabulary/NexoRegistry.mjs';
|
|
20
|
+
import { Vocabulary } from './language/vocabulary/Vocabulary.mjs';
|
|
21
|
+
|
|
22
|
+
/** The parameter bindings each bindings object a registry's `createBindings` created stands for. @type {WeakMap<object, ParameterBindings>} */
|
|
23
|
+
const PARAMETER_BINDINGS = new WeakMap();
|
|
24
|
+
|
|
25
|
+
/** The imports a host supplies in a call's options, as a definition records them, checked as a definition's are; none where it supplies none. */
|
|
26
|
+
/** Refuses options holding a key the operation takes none of, so a misspelled option fails rather than being left out (NEXO2048); options that are no object are the operation's own to refuse. */
|
|
27
|
+
const requireOptions = (options, keys, operation) => {
|
|
28
|
+
if (options === null || typeof options !== 'object' || Array.isArray(options)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const unknown = Object.keys(options).find(key => !keys.includes(key));
|
|
33
|
+
|
|
34
|
+
if (unknown !== undefined) {
|
|
35
|
+
throw NexoErrors.structure('NEXO2048', `${operation} takes the options ${keys.map(key => `'${key}'`).join(' and ')}, and '${unknown}' is none of them`, {});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** A function a prepared call gives its host, made apart from its evaluator so that it holds only its holder: it calls the evaluator's callback through the evaluator's entry, and calls it alone once the call is disposed, where it fails. */
|
|
40
|
+
const createKeptFunction = holder => (...values) => (holder.enter === null ? holder.callback(...values) : holder.enter(() => holder.callback(...values)));
|
|
41
|
+
|
|
42
|
+
/** The hooks an evaluation is given, each a function of the names an evaluation calls, refused otherwise as an option is (NEXO2048). */
|
|
43
|
+
const HOOK_NAMES = Object.freeze(['beforeInvoke', 'afterInvoke', 'afterEvaluation']);
|
|
44
|
+
|
|
45
|
+
const requireHooks = (hooks, operation) => {
|
|
46
|
+
if (hooks !== null && hooks !== undefined && (typeof hooks !== 'object' || Array.isArray(hooks))) {
|
|
47
|
+
throw NexoErrors.structure('NEXO2048', `${operation} takes its hooks as an object of ${HOOK_NAMES.map(name => `'${name}'`).join(', ')}`, {});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
requireOptions(hooks, HOOK_NAMES, operation);
|
|
51
|
+
|
|
52
|
+
const notFunction = HOOK_NAMES.find(name => hooks?.[name] !== undefined && typeof hooks[name] !== 'function');
|
|
53
|
+
|
|
54
|
+
if (notFunction !== undefined) {
|
|
55
|
+
throw NexoErrors.structure('NEXO2048', `${operation} takes '${notFunction}' as a function`, {});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const suppliedImportsOf = (options, operation) => {
|
|
60
|
+
requireOptions(options, ['imports'], operation);
|
|
61
|
+
|
|
62
|
+
return Imports.createSuppliedImports(options?.imports);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The definition a script gives: its source read with the imports a host supplies and checked, or its definition checked, each the same frozen definition its canonical text would give. A definition records its imports, so
|
|
67
|
+
* the imports supplied apply to a source alone.
|
|
68
|
+
*/
|
|
69
|
+
const definitionOf = (script, suppliedImports = []) => (typeof script === 'string' ? ParsedTexts.parse(script, suppliedImports) : new DefinitionValidator().validate(script));
|
|
70
|
+
|
|
71
|
+
/** What `parse` reads, a script's source, read with the imports a host supplies. */
|
|
72
|
+
const parse = (source, options) => {
|
|
73
|
+
if (typeof source !== 'string') {
|
|
74
|
+
throw NexoErrors.structure('NEXO2645', "What parse reads is a script's source", {});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return ParsedTexts.parse(source, suppliedImportsOf(options, 'parse'));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** The canonical text of a script, laid out or compact, read with the imports its destination supplies and leaving them out. */
|
|
81
|
+
const write = (script, options, isCompact) => {
|
|
82
|
+
const supplied = suppliedImportsOf(options, isCompact ? 'quote' : 'format');
|
|
83
|
+
const definition = definitionOf(script, supplied);
|
|
84
|
+
|
|
85
|
+
return isCompact ? DefinitionWriter.quoteScript(definition, supplied) : DefinitionWriter.formatScript(definition, supplied);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Bindings, the registry's: the values bound by name, data a frozen copy of the data given and each reference itself, the type each value gives its binding or its host declares for it, in type notation, and the resolution key
|
|
90
|
+
* that says which resolutions they may reuse.
|
|
91
|
+
*/
|
|
92
|
+
const createBindings = (registry, values = {}, options = {}) => {
|
|
93
|
+
requireOptions(options, ['types'], 'createBindings');
|
|
94
|
+
|
|
95
|
+
const parameterBindings = new ParameterBindings(registry, values, options);
|
|
96
|
+
const bindings = Object.freeze({
|
|
97
|
+
resolutionKey: parameterBindings.resolutionKey,
|
|
98
|
+
has: name => parameterBindings.has(name),
|
|
99
|
+
readValue: name => parameterBindings.readValue(name),
|
|
100
|
+
getType: name => (parameterBindings.has(name) ? String(parameterBindings.getType(name)) : undefined),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
PARAMETER_BINDINGS.set(bindings, parameterBindings);
|
|
104
|
+
|
|
105
|
+
return bindings;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The parameter bindings the registry's `createBindings` created for the bindings, or undefined for any other value, plain values among them, which its caller binds; bindings another registry created are refused. It creates
|
|
110
|
+
* nothing, so it calls no module's `typeOf`.
|
|
111
|
+
*/
|
|
112
|
+
const getParameterBindings = (registry, bindings) => {
|
|
113
|
+
const parameterBindings = PARAMETER_BINDINGS.get(bindings);
|
|
114
|
+
|
|
115
|
+
if (parameterBindings === undefined) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (parameterBindings.registry !== registry) {
|
|
120
|
+
throw NexoErrors.structure('NEXO2024', 'These bindings belong to another registry, which recognized their references; bind them with this registry', {});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return parameterBindings;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/** Where a pointer stands in the source a definition was read from, or the pointer alone for a definition given directly; a pointer the definition does not hold is refused. */
|
|
127
|
+
const locate = (definition, pointer) => {
|
|
128
|
+
if (typeof pointer !== 'string' || !(pointer === '' || pointer.startsWith('/'))) {
|
|
129
|
+
throw NexoErrors.structure('NEXO2643', 'A pointer is a JSON Pointer into the definition, as /expression/objectFields/0', {});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let node = definition;
|
|
133
|
+
|
|
134
|
+
for (const segment of pointer.split('/').slice(1).map(part => part.replaceAll('~1', '/').replaceAll('~0', '~'))) {
|
|
135
|
+
// An array's parts are its entries, each at an index, so `length` names none.
|
|
136
|
+
const isPart = Array.isArray(node) ? /^(?:0|[1-9][0-9]*)$/.test(segment) && Number(segment) < node.length : Object.hasOwn(node ?? {}, segment);
|
|
137
|
+
|
|
138
|
+
if (node === null || typeof node !== 'object' || !isPart) {
|
|
139
|
+
throw NexoErrors.structure('NEXO2644', `The definition holds nothing at ${pointer}`, { pointer });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
node = node[segment];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return Object.freeze(ParsedTexts.locationsOf(definition)?.locate(pointer) ?? { pointer });
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A resolution: the script resolved against the registry with bindings of one resolution key, reused with any bindings of that key while it is current: until a module declaring a `typeOf` is registered, where
|
|
150
|
+
* it may recognize values as it is evaluated, and for good where it may not.
|
|
151
|
+
* An evaluator it creates holds one binding set and the environment the host owns, and evaluates one evaluation at a time.
|
|
152
|
+
*/
|
|
153
|
+
const resolutionOf = (registry, definition, parameterBindings) => {
|
|
154
|
+
const locations = ParsedTexts.locationsOf(definition);
|
|
155
|
+
const resolved = new LanguageResolver(registry, parameterBindings, locations).resolveDefinition(definition);
|
|
156
|
+
const { resolutionKey } = parameterBindings;
|
|
157
|
+
const { revision } = registry;
|
|
158
|
+
const useOf = ({ declaration, qualifiedName, pointer }) => Object.freeze({ declaration, name: declaration.name ?? null, qualifiedName, pointer });
|
|
159
|
+
const usedDeclarations = Object.freeze(resolved.usedDeclarations.map(useOf));
|
|
160
|
+
const deferredAccesses = Object.freeze(resolved.deferredAccesses.map(({ name, pointer }) => Object.freeze({ name, pointer })));
|
|
161
|
+
|
|
162
|
+
// A registration changes a resolution through recognition alone, so only a module declaring a `typeOf`, registered since, makes stale a resolution that may recognize values.
|
|
163
|
+
const isCurrent = () => registry.revision === revision || !resolved.mayRecognize || registry.recognitionRevision <= revision;
|
|
164
|
+
|
|
165
|
+
const requireCurrent = () => {
|
|
166
|
+
if (!isCurrent()) {
|
|
167
|
+
throw NexoErrors.structure('NEXO2040', 'A module declaring a typeOf was registered since this script was resolved, and the script may recognize values as it is evaluated, so its resolution is stale; resolve the script again', {});
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const createEvaluator = (bindings, env = {}, options = undefined) => {
|
|
172
|
+
// The limits a host sets are read before anything is bound, so options it can't take refuse the evaluator before a binding asks a module's `typeOf`.
|
|
173
|
+
const limits = Limits.hostLimitsOf(options);
|
|
174
|
+
|
|
175
|
+
// Plain values are bound here, which asks every module's `typeOf` of them, before the resolution key and the registry's revision are checked.
|
|
176
|
+
const evaluatorBindings = getParameterBindings(registry, bindings) ?? new ParameterBindings(registry, bindings ?? {});
|
|
177
|
+
|
|
178
|
+
if (evaluatorBindings.resolutionKey !== resolutionKey) {
|
|
179
|
+
throw NexoErrors.structure('NEXO2025', 'These bindings resolve the script otherwise than the bindings it was resolved with, by a type or a value that selects; resolve it with these bindings', {});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
requireCurrent();
|
|
183
|
+
|
|
184
|
+
// A declaration without an implementation is resolved against and never invoked, so a script that uses one, in a branch it may not take among them, is refused before anything is evaluated; a host call has none, its host invoking it.
|
|
185
|
+
// A structured type's data property is read from the data, and a cast operator permits its cast, so neither takes an implementation.
|
|
186
|
+
const unimplemented = usedDeclarations.find(({ declaration }) => registry.getImplementation(declaration) === undefined && !NexoModule.isInvokedByHost(declaration) && !registry.isDataProperty(declaration) && !(declaration.kind === 'operator' && declaration.name === 'cast'));
|
|
187
|
+
|
|
188
|
+
if (unimplemented !== undefined) {
|
|
189
|
+
const { declaration, qualifiedName } = unimplemented;
|
|
190
|
+
const containing = registry.getContainingType(declaration);
|
|
191
|
+
// A member within a type or an extension is located at it, as any of its module's errors is.
|
|
192
|
+
const location = { module: registry.getDeclaringModuleName(declaration), ...(containing === undefined ? {} : { type: containing.name }), declaration: declaration.name ?? '[]', declarationKey: 'impl' };
|
|
193
|
+
const subject = declaration.kind === 'operator' ? `The conversion from ${qualifiedName}` : `'${qualifiedName}'`;
|
|
194
|
+
|
|
195
|
+
throw NexoErrors.module('NEXO4001', `${subject} has no implementation, so a script that uses it is resolved and never evaluated`, location);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const evaluator = new Evaluator(registry, resolved, evaluatorBindings, env, locations, limits);
|
|
199
|
+
let isEvaluating = false;
|
|
200
|
+
|
|
201
|
+
// Each entry point checks the registry has not moved, and evaluates one evaluation at a time.
|
|
202
|
+
const enter = run => {
|
|
203
|
+
requireCurrent();
|
|
204
|
+
|
|
205
|
+
if (isEvaluating) {
|
|
206
|
+
throw NexoErrors.evaluation('NEXO5010', 'An evaluator evaluates one evaluation at a time, and this one is evaluating; evaluate with an evaluator of its own', {});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
isEvaluating = true;
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
return run();
|
|
213
|
+
} finally {
|
|
214
|
+
isEvaluating = false;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// A prepared call projects, and calls each function it keeps, through the same entry, so each checks the registry and evaluates alone, as an evaluation does.
|
|
219
|
+
const prepareHostCall = () => {
|
|
220
|
+
const call = enter(() => evaluator.prepareHostCall());
|
|
221
|
+
|
|
222
|
+
if (call.kind !== 'prepared') {
|
|
223
|
+
return call;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const kept = call.declaration.parameters.filter(parameter => parameter.kind === 'function' && typeof call.args[parameter.name] === 'function');
|
|
227
|
+
// Each kept function reaches the evaluator through a holder that disposing the call empties, so one the host keeps past it holds nothing of the evaluation, and fails as it is called.
|
|
228
|
+
const holders = kept.map(({ name }) => ({ name, callback: call.args[name], enter }));
|
|
229
|
+
const args = kept.length === 0 ? call.args : { ...call.args, ...Object.fromEntries(holders.map(holder => [holder.name, createKeptFunction(holder)])) };
|
|
230
|
+
const dispose = () => {
|
|
231
|
+
call.dispose();
|
|
232
|
+
holders.forEach(holder => (holder.enter = null));
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return Object.freeze({ ...call, args, dispose, ...(call.project === undefined ? {} : { project: (value, hooks = null) => (requireHooks(hooks, 'project'), enter(() => call.project(value, hooks))) }) });
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return Object.freeze({
|
|
239
|
+
evaluate: (hooks = null) => (requireHooks(hooks, 'evaluate'), enter(() => evaluator.evaluate(hooks))),
|
|
240
|
+
prepareHostCall,
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const { hostCall } = resolved;
|
|
245
|
+
|
|
246
|
+
// A path of another shape is refused whatever the script; a host call that supplies nothing gives no result, so no path reaches into one.
|
|
247
|
+
const typeAt = path => {
|
|
248
|
+
ResultPath.validateShape(path);
|
|
249
|
+
|
|
250
|
+
if (resolved.type === null) {
|
|
251
|
+
throw NexoErrors.validation('NEXO3600', `The script ends in the host call '${hostCall.name}', whose host supplies no values, so it has no result for a path to reach`, locate(definition, '/expression'));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return String(ResultPath.typeAt(resolved.type, path, registry, () => locate(definition, '/expression')));
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return Object.freeze({
|
|
258
|
+
definition,
|
|
259
|
+
hostCall: hostCall === null ? null : Object.freeze({ name: hostCall.name, pointer: hostCall.pointer, declaration: hostCall.declaration, candidates: hostCall.candidates }),
|
|
260
|
+
type: resolved.type === null ? null : String(resolved.type),
|
|
261
|
+
usedDeclarations,
|
|
262
|
+
deferredAccesses,
|
|
263
|
+
getCandidateDeclarations: () => Object.freeze({ declarations: Object.freeze(resolved.candidateDeclarations.map(useOf)), isOverestimate: resolved.selectsAsEvaluated }),
|
|
264
|
+
typeAt,
|
|
265
|
+
getLocation: pointer => locate(definition, pointer),
|
|
266
|
+
get isCurrent() {
|
|
267
|
+
return isCurrent();
|
|
268
|
+
},
|
|
269
|
+
createEvaluator,
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A registry a host resolves scripts against: Nexo's own module and the modules the host registers, each module's vocabulary in effect for every script resolved after it.
|
|
275
|
+
* It reads no text of its own: a script's imports decide which of its names are namespaces, so `nexo.parse`, `format` and `quote` read and write a text alike for every registry, and registering a module changes how no text reads. No registry is shared, so two hosts never see each other's modules.
|
|
276
|
+
*/
|
|
277
|
+
const createRegistry = modules => {
|
|
278
|
+
if (!Array.isArray(modules)) {
|
|
279
|
+
throw NexoErrors.module('NEXO4546', 'A registry is created from a list of modules, each as createModule makes it', {});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const registry = new NexoRegistry([Vocabulary.module, ...modules]);
|
|
283
|
+
|
|
284
|
+
return Object.freeze({
|
|
285
|
+
registerModule: module => {
|
|
286
|
+
registry.registerModule(module);
|
|
287
|
+
},
|
|
288
|
+
get moduleNames() {
|
|
289
|
+
return registry.moduleNames;
|
|
290
|
+
},
|
|
291
|
+
get revision() {
|
|
292
|
+
return registry.revision;
|
|
293
|
+
},
|
|
294
|
+
getObservationType: name => registry.getObservationType(name),
|
|
295
|
+
getDeclaringModuleName: declaration => registry.getDeclaringModuleName(declaration),
|
|
296
|
+
getImplementation: declaration => registry.getImplementation(declaration),
|
|
297
|
+
createBindings: (values = {}, options = {}) => createBindings(registry, values, options),
|
|
298
|
+
resolve: (script, bindings = {}, options = {}) => {
|
|
299
|
+
// The script is read before its bindings, so one that does not follow the syntax or the structure asks no module's `typeOf`.
|
|
300
|
+
const definition = definitionOf(script, suppliedImportsOf(options, 'resolve'));
|
|
301
|
+
|
|
302
|
+
return resolutionOf(registry, definition, getParameterBindings(registry, bindings) ?? new ParameterBindings(registry, bindings ?? {}));
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/** The declaration helpers, each creating a declaration as a module declares it, a member with no receiver of its own, since where it stands gives it one. */
|
|
308
|
+
const declarations = Object.freeze({
|
|
309
|
+
createPropertyDeclaration: Declarations.createPropertyDeclaration,
|
|
310
|
+
createFunctionDeclaration: Declarations.createFunctionDeclaration,
|
|
311
|
+
createIndexerDeclaration: Declarations.createIndexerDeclaration,
|
|
312
|
+
createValueParameterDeclaration: Declarations.createValueParameterDeclaration,
|
|
313
|
+
createExpressionParameterDeclaration: Declarations.createExpressionParameterDeclaration,
|
|
314
|
+
createPredicateParameterDeclaration: Declarations.createPredicateParameterDeclaration,
|
|
315
|
+
createFunctionParameterDeclaration: Declarations.createFunctionParameterDeclaration,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
/** Result paths: a path's shape validated, and the value it reaches in a result, a path of another shape refused there too. */
|
|
319
|
+
const paths = Object.freeze({
|
|
320
|
+
validate: path => ResultPath.validateShape(path),
|
|
321
|
+
valueAt: (value, path) => (ResultPath.validateShape(path), ResultPath.valueAt(value, path)),
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
/** Nexo: reads, checks and writes source and definitions, declares and registers vocabulary, binds values, resolves and evaluates, and says which build it is and which specification it follows. */
|
|
325
|
+
export const nexo = Object.freeze({
|
|
326
|
+
parse: (source, options) => parse(source, options),
|
|
327
|
+
validate: definition => new DefinitionValidator().validate(definition),
|
|
328
|
+
format: (script, options) => write(script, options, false),
|
|
329
|
+
quote: (script, options) => write(script, options, true),
|
|
330
|
+
getLocation: locate,
|
|
331
|
+
createModule: (name, contents) => new NexoModule(name, contents),
|
|
332
|
+
declarations,
|
|
333
|
+
createRegistry: (modules = []) => createRegistry(modules),
|
|
334
|
+
paths,
|
|
335
|
+
info: Object.freeze({ versions: Object.freeze({ implementation: Versions.implementation, language: Versions.language, definition: Versions.definition }) }),
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
export { NexoError };
|