@optique/core 1.4.0-dev.2683 → 1.4.0-dev.2709

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.
@@ -0,0 +1,368 @@
1
+
2
+ //#region src/internal/empty-input.ts
3
+ /**
4
+ * The state of an empty step that leaves its input state unchanged.
5
+ * @internal
6
+ */
7
+ const UNCHANGED = Object.freeze({ unchanged: true });
8
+ /**
9
+ * Returns the state that an empty step produces from the given initial
10
+ * state.
11
+ * @param next The step's resulting state.
12
+ * @param initialState The state the step started from.
13
+ * @returns The resulting state.
14
+ * @internal
15
+ */
16
+ function resolveEmptyStepState(next, initialState) {
17
+ return "unchanged" in next ? initialState : next.state(initialState);
18
+ }
19
+ const emptyInputFactsKey = Symbol("emptyInputFacts");
20
+ const UNKNOWN = Object.freeze({});
21
+ /**
22
+ * Attaches empty-input facts to a parser. The facts are stored as a
23
+ * non-enumerable property, so object spread does not copy them, and they
24
+ * are bound to the parser's current `parse`, `complete`, and
25
+ * `initialState`, so replacing any of those invalidates them.
26
+ * @param parser The parser to annotate.
27
+ * @param facts The facts to attach.
28
+ * @internal
29
+ */
30
+ function defineEmptyInputFacts(parser, facts) {
31
+ if (Object.keys(facts).length < 1) return;
32
+ Object.defineProperty(parser, emptyInputFactsKey, {
33
+ value: {
34
+ facts,
35
+ parse: parser.parse,
36
+ complete: parser.complete,
37
+ ...Object.getOwnPropertyDescriptor(parser, "initialState")?.get == null ? { initialState: { value: parser.initialState } } : {}
38
+ },
39
+ configurable: true,
40
+ enumerable: false,
41
+ writable: false
42
+ });
43
+ }
44
+ /**
45
+ * Returns the empty-input facts of a parser, or an empty object when the
46
+ * facts are unknown.
47
+ * @param parser The parser to inspect.
48
+ * @returns The facts known for the parser.
49
+ * @internal
50
+ */
51
+ function getEmptyInputFacts(parser) {
52
+ const stored = parser[emptyInputFactsKey];
53
+ if (stored == null || stored.parse !== parser.parse || stored.complete !== parser.complete || stored.initialState != null && stored.initialState.value !== parser.initialState) return UNKNOWN;
54
+ return stored.facts;
55
+ }
56
+ /**
57
+ * Returns whether `parse(parser, [])` succeeds, when that is known from the
58
+ * facts.
59
+ * @param facts The parser's facts.
60
+ * @returns `true` or `false` when known, `undefined` otherwise.
61
+ * @internal
62
+ */
63
+ function acceptsEmptyInput(facts) {
64
+ if (facts.step === "failure") return false;
65
+ if (facts.step == null) return void 0;
66
+ return facts.afterStep;
67
+ }
68
+ /**
69
+ * Returns the facts of a parser that fails its empty step and whose
70
+ * completion from the initial state has the given outcome.
71
+ * @param fromInitial Whether completion from the initial state succeeds.
72
+ * @returns The facts.
73
+ * @internal
74
+ */
75
+ function failingStepFacts(fromInitial) {
76
+ return {
77
+ step: "failure",
78
+ fromInitial
79
+ };
80
+ }
81
+ /**
82
+ * Returns whether `complete()` succeeds after the parser was offered an
83
+ * empty step whose state change was kept (on success) or discarded (on
84
+ * failure).
85
+ * @param facts The parser's facts.
86
+ * @returns The outcome, or `undefined` when unknown.
87
+ * @internal
88
+ */
89
+ function completionAfterOptionalStep(facts) {
90
+ if (facts.step === "failure") return facts.fromInitial;
91
+ if (facts.step == null) return void 0;
92
+ return facts.afterStep;
93
+ }
94
+ /**
95
+ * The state of a wrapper that stores its child's resulting state as the
96
+ * only element of a fresh array.
97
+ */
98
+ function wrapInFreshArray(child, childInitialState) {
99
+ return { state: () => [resolveEmptyStepState(child, childInitialState)] };
100
+ }
101
+ /**
102
+ * Computes the facts of `optional(p)` and `withDefault(p, value)` when
103
+ * completing the wrapper from its initial state is known to succeed.
104
+ * @param child The facts of the wrapped parser.
105
+ * @param childInitialState The wrapped parser's initial state.
106
+ * @param fromInitial Whether completing the wrapper from its initial state
107
+ * succeeds, or `undefined` when unknown.
108
+ * @returns The facts of the wrapper.
109
+ * @internal
110
+ */
111
+ function optionalLikeFacts(child, childInitialState, fromInitial) {
112
+ if (child.step === "failure") return {
113
+ step: "success",
114
+ next: UNCHANGED,
115
+ afterStep: fromInitial,
116
+ fromInitial
117
+ };
118
+ if (child.step == null) return { fromInitial };
119
+ return {
120
+ step: child.step,
121
+ ...child.next == null ? {} : { next: wrapInFreshArray(child.next, childInitialState) },
122
+ afterStep: child.afterStep,
123
+ fromInitial
124
+ };
125
+ }
126
+ /**
127
+ * Computes the facts of `multiple(p, { min })` without a `max` bound.
128
+ * @param child The facts of the repeated parser.
129
+ * @param retainsItem Whether a successful empty step of the child is kept
130
+ * as a repetition item, or `undefined` when unknown.
131
+ * @param childInitialState The repeated parser's initial state.
132
+ * @param min The minimum number of items.
133
+ * @returns The facts of the repetition.
134
+ * @internal
135
+ */
136
+ function multipleFacts(child, retainsItem, childInitialState, min) {
137
+ const fromInitial = min < 1;
138
+ if (child.step === "failure") return min < 1 ? {
139
+ step: "success",
140
+ next: UNCHANGED,
141
+ afterStep: true,
142
+ fromInitial
143
+ } : {
144
+ step: "failure",
145
+ fromInitial
146
+ };
147
+ if (child.step == null || retainsItem == null) return { fromInitial };
148
+ if (!retainsItem) return {
149
+ step: "success",
150
+ next: UNCHANGED,
151
+ afterStep: min < 1,
152
+ fromInitial
153
+ };
154
+ return {
155
+ step: child.step,
156
+ ...child.next == null ? {} : { next: wrapInFreshArray(child.next, childInitialState) },
157
+ afterStep: child.afterStep == null ? void 0 : min <= 1 && child.afterStep,
158
+ fromInitial
159
+ };
160
+ }
161
+ function selectZeroInputCandidate(branches) {
162
+ let index = -1;
163
+ let count = 0;
164
+ for (let i = 0; i < branches.length; i++) {
165
+ const branch = branches[i];
166
+ if (branch.matchesTokens) continue;
167
+ if (branch.facts.step == null) return void 0;
168
+ if (branch.facts.step !== "success") continue;
169
+ if (index < 0) index = i;
170
+ count++;
171
+ }
172
+ return count === 1 ? { index } : null;
173
+ }
174
+ /**
175
+ * Computes the completion of `or()` and `longestMatch()` from their initial
176
+ * state, which selects the unique zero-input candidate among the branches
177
+ * that cannot match tokens.
178
+ */
179
+ function exclusiveFromInitial(branches) {
180
+ const candidate = selectZeroInputCandidate(branches);
181
+ if (candidate === void 0) return void 0;
182
+ if (candidate === null) return false;
183
+ return branches[candidate.index].facts.afterStep;
184
+ }
185
+ /**
186
+ * An exemplar of an exclusive combinator's state after it selected a branch.
187
+ * It is a fresh, non-nullish value, which is all that repetition item checks
188
+ * look at.
189
+ */
190
+ function exclusiveNextState(index) {
191
+ return { state: () => [index, {
192
+ success: true,
193
+ consumed: []
194
+ }] };
195
+ }
196
+ /**
197
+ * Computes the facts of `or()`.
198
+ * @param branches The branches of the exclusive combinator.
199
+ * @returns The facts of the combinator.
200
+ * @internal
201
+ */
202
+ function orFacts(branches) {
203
+ const candidate = selectZeroInputCandidate(branches);
204
+ if (candidate === void 0) return {};
205
+ const fromInitial = exclusiveFromInitial(branches);
206
+ if (candidate === null) return {
207
+ step: "failure",
208
+ fromInitial
209
+ };
210
+ return {
211
+ step: "success",
212
+ next: exclusiveNextState(candidate.index),
213
+ afterStep: branches[candidate.index].facts.afterStep,
214
+ fromInitial
215
+ };
216
+ }
217
+ /**
218
+ * Computes the facts of `longestMatch()`, which selects the first branch
219
+ * whose empty step succeeds, preferring a definitive success over a
220
+ * provisional one.
221
+ * @param branches The branches of the exclusive combinator.
222
+ * @returns The facts of the combinator.
223
+ * @internal
224
+ */
225
+ function longestMatchFacts(branches) {
226
+ const fromInitial = exclusiveFromInitial(branches);
227
+ let best = null;
228
+ for (let i = 0; i < branches.length; i++) {
229
+ const facts = branches[i].facts;
230
+ if (facts.step == null) return { fromInitial };
231
+ if (facts.step === "failure") continue;
232
+ if (best == null || best.facts.step === "provisional" && facts.step === "success") best = {
233
+ index: i,
234
+ facts
235
+ };
236
+ if (best.facts.step === "success") break;
237
+ }
238
+ if (best == null) return {
239
+ step: "failure",
240
+ fromInitial
241
+ };
242
+ return {
243
+ step: best.facts.step,
244
+ next: exclusiveNextState(best.index),
245
+ afterStep: best.facts.afterStep,
246
+ fromInitial
247
+ };
248
+ }
249
+ function all(values) {
250
+ let unknown = false;
251
+ for (const value of values) {
252
+ if (value === false) return false;
253
+ if (value == null) unknown = true;
254
+ }
255
+ return unknown ? void 0 : true;
256
+ }
257
+ /**
258
+ * Computes the facts of `tuple()`. On an empty buffer `tuple()` offers
259
+ * each child one zero-token step, stores the state of every child whose
260
+ * step succeeded in a fresh state array, and skips the rest.
261
+ * @param children The children in declaration order.
262
+ * @returns The facts of the tuple.
263
+ * @internal
264
+ */
265
+ function tupleFacts(children) {
266
+ const fromInitial = all(children.map((c) => c.facts.fromInitial));
267
+ if (children.some((c) => c.facts.step == null)) return {
268
+ step: "success",
269
+ fromInitial
270
+ };
271
+ const afterStep = all(children.map((c) => completionAfterOptionalStep(c.facts)));
272
+ let next;
273
+ if (children.every((c) => c.facts.step === "failure")) next = UNCHANGED;
274
+ else if (children.every((c) => c.facts.step === "failure" || c.facts.next != null)) {
275
+ const steps = children.map((c) => c.facts.step === "failure" ? void 0 : c.facts.next);
276
+ next = { state: (initialState) => {
277
+ const slots = Array.isArray(initialState) ? initialState : [];
278
+ return steps.map((step, i) => step == null ? slots[i] : resolveEmptyStepState(step, slots[i]));
279
+ } };
280
+ }
281
+ return {
282
+ step: "success",
283
+ ...next == null ? {} : { next },
284
+ afterStep,
285
+ fromInitial
286
+ };
287
+ }
288
+ /**
289
+ * The state of `object()` after an empty step: a committed field state makes
290
+ * it store a fresh record with the other fields unchanged.
291
+ */
292
+ function objectNextState(changed) {
293
+ if (changed.length < 1) return UNCHANGED;
294
+ return { state: (initialState) => {
295
+ const record = initialState != null && typeof initialState === "object" ? { ...initialState } : {};
296
+ for (const [key, next] of changed) record[key] = resolveEmptyStepState(next, record[key]);
297
+ return record;
298
+ } };
299
+ }
300
+ /**
301
+ * Computes the facts of a synchronous `object()`. On an empty buffer
302
+ * `object()` offers each field that cannot match tokens one zero-token
303
+ * step, keeps the resulting state when it changed, and then succeeds only
304
+ * if every field can complete.
305
+ * @param children The fields.
306
+ * @param keys The field keys, parallel to `children`.
307
+ * @returns The facts of the object.
308
+ * @internal
309
+ */
310
+ function objectFacts(children, keys) {
311
+ const fromInitial = all(children.map((c) => c.facts.fromInitial));
312
+ const outcomes = [];
313
+ const changed = [];
314
+ let nextKnown = true;
315
+ for (const [i, child] of children.entries()) {
316
+ const { step, next } = child.facts;
317
+ if (child.matchesTokens || step === "failure") outcomes.push(child.facts.fromInitial);
318
+ else if (step == null) {
319
+ outcomes.push(void 0);
320
+ nextKnown = false;
321
+ } else if (next == null) {
322
+ nextKnown = false;
323
+ outcomes.push(child.facts.afterStep === child.facts.fromInitial ? child.facts.afterStep : void 0);
324
+ } else if ("unchanged" in next) outcomes.push(child.facts.fromInitial);
325
+ else {
326
+ changed.push([keys[i], next]);
327
+ outcomes.push(child.facts.afterStep);
328
+ }
329
+ }
330
+ const accepted = all(outcomes);
331
+ if (accepted == null) return { fromInitial };
332
+ if (!accepted) return {
333
+ step: "failure",
334
+ fromInitial
335
+ };
336
+ return {
337
+ step: "success",
338
+ ...nextKnown ? { next: objectNextState(changed) } : {},
339
+ afterStep: true,
340
+ fromInitial
341
+ };
342
+ }
343
+ /**
344
+ * Attaches empty-input facts to a parser and returns the parser.
345
+ * @param parser The parser to annotate.
346
+ * @param facts The facts to attach.
347
+ * @returns The same parser.
348
+ * @internal
349
+ */
350
+ function withEmptyInputFacts(parser, facts) {
351
+ defineEmptyInputFacts(parser, facts);
352
+ return parser;
353
+ }
354
+
355
+ //#endregion
356
+ exports.UNCHANGED = UNCHANGED;
357
+ exports.acceptsEmptyInput = acceptsEmptyInput;
358
+ exports.defineEmptyInputFacts = defineEmptyInputFacts;
359
+ exports.failingStepFacts = failingStepFacts;
360
+ exports.getEmptyInputFacts = getEmptyInputFacts;
361
+ exports.longestMatchFacts = longestMatchFacts;
362
+ exports.multipleFacts = multipleFacts;
363
+ exports.objectFacts = objectFacts;
364
+ exports.optionalLikeFacts = optionalLikeFacts;
365
+ exports.orFacts = orFacts;
366
+ exports.resolveEmptyStepState = resolveEmptyStepState;
367
+ exports.tupleFacts = tupleFacts;
368
+ exports.withEmptyInputFacts = withEmptyInputFacts;