@optique/core 0.9.0-dev.211 → 0.9.0-dev.215

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/dist/modifiers.js CHANGED
@@ -9,12 +9,31 @@ import { formatMessage, message, text } from "./message.js";
9
9
  * - Returning success with undefined state when inner parser fails without consuming
10
10
  * @internal
11
11
  */
12
- function parseOptionalStyle(context, parser) {
12
+ function parseOptionalStyleSync(context, parser) {
13
13
  const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
14
14
  const result = parser.parse({
15
15
  ...context,
16
16
  state: innerState
17
17
  });
18
+ return processOptionalStyleResult(result, innerState, context);
19
+ }
20
+ /**
21
+ * Internal async helper for optional-style parsing logic.
22
+ * @internal
23
+ */
24
+ async function parseOptionalStyleAsync(context, parser) {
25
+ const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
26
+ const result = await parser.parse({
27
+ ...context,
28
+ state: innerState
29
+ });
30
+ return processOptionalStyleResult(result, innerState, context);
31
+ }
32
+ /**
33
+ * Internal helper to process optional-style parse results.
34
+ * @internal
35
+ */
36
+ function processOptionalStyleResult(result, innerState, context) {
18
37
  if (result.success) {
19
38
  if (result.next.state !== innerState || result.consumed.length === 0) return {
20
39
  success: true,
@@ -45,6 +64,7 @@ function parseOptionalStyle(context, parser) {
45
64
  * without consuming input if the wrapped parser fails to match.
46
65
  * If the wrapped parser succeeds, this returns its value.
47
66
  * If the wrapped parser fails, this returns `undefined` without consuming input.
67
+ * @template M The execution mode of the parser.
48
68
  * @template TValue The type of the value returned by the wrapped parser.
49
69
  * @template TState The type of the state used by the wrapped parser.
50
70
  * @param parser The {@link Parser} to make optional.
@@ -52,7 +72,25 @@ function parseOptionalStyle(context, parser) {
52
72
  * or `undefined` if the wrapped parser fails to match.
53
73
  */
54
74
  function optional(parser) {
75
+ const syncParser = parser;
76
+ const isAsync = parser.$mode === "async";
77
+ function* suggestSync(context, prefix) {
78
+ const innerState = typeof context.state === "undefined" ? syncParser.initialState : context.state[0];
79
+ yield* syncParser.suggest({
80
+ ...context,
81
+ state: innerState
82
+ }, prefix);
83
+ }
84
+ async function* suggestAsync(context, prefix) {
85
+ const innerState = typeof context.state === "undefined" ? syncParser.initialState : context.state[0];
86
+ const suggestions = parser.suggest({
87
+ ...context,
88
+ state: innerState
89
+ }, prefix);
90
+ for await (const s of suggestions) yield s;
91
+ }
55
92
  return {
93
+ $mode: parser.$mode,
56
94
  $valueType: [],
57
95
  $stateType: [],
58
96
  priority: parser.priority,
@@ -62,28 +100,27 @@ function optional(parser) {
62
100
  }],
63
101
  initialState: void 0,
64
102
  parse(context) {
65
- return parseOptionalStyle(context, parser);
103
+ if (isAsync) return parseOptionalStyleAsync(context, parser);
104
+ return parseOptionalStyleSync(context, syncParser);
66
105
  },
67
106
  complete(state) {
68
107
  if (typeof state === "undefined") return {
69
108
  success: true,
70
109
  value: void 0
71
110
  };
72
- return parser.complete(state[0]);
111
+ if (!isAsync) return syncParser.complete(state[0]);
112
+ return (async () => await parser.complete(state[0]))();
73
113
  },
74
114
  suggest(context, prefix) {
75
- const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
76
- return parser.suggest({
77
- ...context,
78
- state: innerState
79
- }, prefix);
115
+ if (isAsync) return suggestAsync(context, prefix);
116
+ return suggestSync(context, prefix);
80
117
  },
81
118
  getDocFragments(state, defaultValue) {
82
119
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
83
120
  kind: "available",
84
121
  state: state.state[0]
85
122
  };
86
- return parser.getDocFragments(innerState, defaultValue);
123
+ return syncParser.getDocFragments(innerState, defaultValue);
87
124
  }
88
125
  };
89
126
  }
@@ -122,7 +159,25 @@ var WithDefaultError = class extends Error {
122
159
  }
123
160
  };
124
161
  function withDefault(parser, defaultValue, options) {
162
+ const syncParser = parser;
163
+ const isAsync = parser.$mode === "async";
164
+ function* suggestSync(context, prefix) {
165
+ const innerState = typeof context.state === "undefined" ? syncParser.initialState : context.state[0];
166
+ yield* syncParser.suggest({
167
+ ...context,
168
+ state: innerState
169
+ }, prefix);
170
+ }
171
+ async function* suggestAsync(context, prefix) {
172
+ const innerState = typeof context.state === "undefined" ? syncParser.initialState : context.state[0];
173
+ const suggestions = parser.suggest({
174
+ ...context,
175
+ state: innerState
176
+ }, prefix);
177
+ for await (const s of suggestions) yield s;
178
+ }
125
179
  return {
180
+ $mode: parser.$mode,
126
181
  $valueType: [],
127
182
  $stateType: [],
128
183
  priority: parser.priority,
@@ -132,7 +187,8 @@ function withDefault(parser, defaultValue, options) {
132
187
  }],
133
188
  initialState: void 0,
134
189
  parse(context) {
135
- return parseOptionalStyle(context, parser);
190
+ if (isAsync) return parseOptionalStyleAsync(context, parser);
191
+ return parseOptionalStyleSync(context, syncParser);
136
192
  },
137
193
  complete(state) {
138
194
  if (typeof state === "undefined") try {
@@ -147,14 +203,12 @@ function withDefault(parser, defaultValue, options) {
147
203
  error: error instanceof WithDefaultError ? error.errorMessage : message`${text(String(error))}`
148
204
  };
149
205
  }
150
- return parser.complete(state[0]);
206
+ if (!isAsync) return syncParser.complete(state[0]);
207
+ return (async () => await parser.complete(state[0]))();
151
208
  },
152
209
  suggest(context, prefix) {
153
- const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
154
- return parser.suggest({
155
- ...context,
156
- state: innerState
157
- }, prefix);
210
+ if (isAsync) return suggestAsync(context, prefix);
211
+ return suggestSync(context, prefix);
158
212
  },
159
213
  getDocFragments(state, upperDefaultValue) {
160
214
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
@@ -162,7 +216,7 @@ function withDefault(parser, defaultValue, options) {
162
216
  state: state.state[0]
163
217
  };
164
218
  const actualDefaultValue = upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue;
165
- const fragments = parser.getDocFragments(innerState, actualDefaultValue);
219
+ const fragments = syncParser.getDocFragments(innerState, actualDefaultValue);
166
220
  if (options?.message) {
167
221
  const modifiedFragments = fragments.fragments.map((fragment) => {
168
222
  if (fragment.type === "entry") return {
@@ -191,6 +245,7 @@ function withDefault(parser, defaultValue, options) {
191
245
  * - Computing derived values from parsed input
192
246
  * - Creating reusable transformations that can be applied to any parser
193
247
  *
248
+ * @template M The execution mode of the parser.
194
249
  * @template T The type of the value produced by the original parser.
195
250
  * @template U The type of the value produced by the mapping function.
196
251
  * @template TState The type of the state used by the original parser.
@@ -214,33 +269,68 @@ function withDefault(parser, defaultValue, options) {
214
269
  * ```
215
270
  */
216
271
  function map(parser, transform) {
217
- return {
272
+ const syncParser = parser;
273
+ const isAsync = parser.$mode === "async";
274
+ function* suggestSync(context, prefix) {
275
+ yield* syncParser.suggest(context, prefix);
276
+ }
277
+ async function* suggestAsync(context, prefix) {
278
+ const suggestions = parser.suggest(context, prefix);
279
+ for await (const s of suggestions) yield s;
280
+ }
281
+ const result = {
282
+ $mode: "sync",
218
283
  $valueType: [],
219
284
  $stateType: parser.$stateType,
220
285
  priority: parser.priority,
221
286
  usage: parser.usage,
222
287
  initialState: parser.initialState,
223
- parse: parser.parse.bind(parser),
288
+ parse(context) {
289
+ if (isAsync) return parser.parse(context);
290
+ return syncParser.parse(context);
291
+ },
224
292
  complete(state) {
225
- const result = parser.complete(state);
226
- if (result.success) return {
227
- success: true,
228
- value: transform(result.value)
229
- };
230
- return result;
293
+ if (!isAsync) {
294
+ const innerResult = syncParser.complete(state);
295
+ if (innerResult.success) return {
296
+ success: true,
297
+ value: transform(innerResult.value)
298
+ };
299
+ return {
300
+ success: false,
301
+ error: innerResult.error
302
+ };
303
+ }
304
+ return (async () => {
305
+ const innerResult = await parser.complete(state);
306
+ if (innerResult.success) return {
307
+ success: true,
308
+ value: transform(innerResult.value)
309
+ };
310
+ return {
311
+ success: false,
312
+ error: innerResult.error
313
+ };
314
+ })();
231
315
  },
232
316
  suggest(context, prefix) {
233
- return parser.suggest(context, prefix);
317
+ if (isAsync) return suggestAsync(context, prefix);
318
+ return suggestSync(context, prefix);
234
319
  },
235
320
  getDocFragments(state, _defaultValue) {
236
- return parser.getDocFragments(state, void 0);
321
+ return syncParser.getDocFragments(state, void 0);
237
322
  }
238
323
  };
324
+ return {
325
+ ...result,
326
+ $mode: parser.$mode
327
+ };
239
328
  }
240
329
  /**
241
330
  * Creates a parser that allows multiple occurrences of a given parser.
242
331
  * This parser can be used to parse multiple values of the same type,
243
332
  * such as multiple command-line arguments or options.
333
+ * @template M The execution mode of the parser.
244
334
  * @template TValue The type of the value that the parser produces.
245
335
  * @template TState The type of the state used by the parser.
246
336
  * @param parser The {@link Parser} to apply multiple times.
@@ -252,8 +342,59 @@ function map(parser, transform) {
252
342
  * of type {@link TState}.
253
343
  */
254
344
  function multiple(parser, options = {}) {
345
+ const syncParser = parser;
346
+ const isAsync = parser.$mode === "async";
255
347
  const { min = 0, max = Infinity } = options;
256
- return {
348
+ const parseSync = (context) => {
349
+ let added = context.state.length < 1;
350
+ let result = syncParser.parse({
351
+ ...context,
352
+ state: context.state.at(-1) ?? syncParser.initialState
353
+ });
354
+ if (!result.success) if (!added) {
355
+ result = syncParser.parse({
356
+ ...context,
357
+ state: syncParser.initialState
358
+ });
359
+ if (!result.success) return result;
360
+ added = true;
361
+ } else return result;
362
+ return {
363
+ success: true,
364
+ next: {
365
+ ...result.next,
366
+ state: [...added ? context.state : context.state.slice(0, -1), result.next.state]
367
+ },
368
+ consumed: result.consumed
369
+ };
370
+ };
371
+ const parseAsync = async (context) => {
372
+ let added = context.state.length < 1;
373
+ let resultOrPromise = parser.parse({
374
+ ...context,
375
+ state: context.state.at(-1) ?? parser.initialState
376
+ });
377
+ let result = await resultOrPromise;
378
+ if (!result.success) if (!added) {
379
+ resultOrPromise = parser.parse({
380
+ ...context,
381
+ state: parser.initialState
382
+ });
383
+ result = await resultOrPromise;
384
+ if (!result.success) return result;
385
+ added = true;
386
+ } else return result;
387
+ return {
388
+ success: true,
389
+ next: {
390
+ ...result.next,
391
+ state: [...added ? context.state : context.state.slice(0, -1), result.next.state]
392
+ },
393
+ consumed: result.consumed
394
+ };
395
+ };
396
+ const resultParser = {
397
+ $mode: parser.$mode,
257
398
  $valueType: [],
258
399
  $stateType: [],
259
400
  priority: parser.priority,
@@ -264,71 +405,79 @@ function multiple(parser, options = {}) {
264
405
  }],
265
406
  initialState: [],
266
407
  parse(context) {
267
- let added = context.state.length < 1;
268
- let result = parser.parse({
269
- ...context,
270
- state: context.state.at(-1) ?? parser.initialState
271
- });
272
- if (!result.success) if (!added) {
273
- result = parser.parse({
274
- ...context,
275
- state: parser.initialState
276
- });
277
- if (!result.success) return result;
278
- added = true;
279
- } else return result;
280
- return {
281
- success: true,
282
- next: {
283
- ...result.next,
284
- state: [...added ? context.state : context.state.slice(0, -1), result.next.state]
285
- },
286
- consumed: result.consumed
287
- };
408
+ if (isAsync) return parseAsync(context);
409
+ return parseSync(context);
288
410
  },
289
411
  complete(state) {
290
- const result = [];
291
- for (const s of state) {
292
- const valueResult = parser.complete(s);
293
- if (valueResult.success) result.push(valueResult.value);
294
- else return {
295
- success: false,
296
- error: valueResult.error
297
- };
298
- }
299
- if (result.length < min) {
300
- const customMessage = options.errors?.tooFew;
301
- return {
302
- success: false,
303
- error: customMessage ? typeof customMessage === "function" ? customMessage(min, result.length) : customMessage : message`Expected at least ${text(min.toLocaleString("en"))} values, but got only ${text(result.length.toLocaleString("en"))}.`
304
- };
305
- } else if (result.length > max) {
306
- const customMessage = options.errors?.tooMany;
307
- return {
308
- success: false,
309
- error: customMessage ? typeof customMessage === "function" ? customMessage(max, result.length) : customMessage : message`Expected at most ${text(max.toLocaleString("en"))} values, but got ${text(result.length.toLocaleString("en"))}.`
310
- };
412
+ if (!isAsync) {
413
+ const result = [];
414
+ for (const s of state) {
415
+ const valueResult = syncParser.complete(s);
416
+ if (valueResult.success) result.push(valueResult.value);
417
+ else return {
418
+ success: false,
419
+ error: valueResult.error
420
+ };
421
+ }
422
+ return validateMultipleResult(result);
311
423
  }
312
- return {
313
- success: true,
314
- value: result
315
- };
424
+ return (async () => {
425
+ const result = [];
426
+ for (const s of state) {
427
+ const valueResult = await parser.complete(s);
428
+ if (valueResult.success) result.push(valueResult.value);
429
+ else return {
430
+ success: false,
431
+ error: valueResult.error
432
+ };
433
+ }
434
+ return validateMultipleResult(result);
435
+ })();
316
436
  },
317
437
  suggest(context, prefix) {
318
438
  const innerState = context.state.length > 0 ? context.state.at(-1) : parser.initialState;
319
- return parser.suggest({
320
- ...context,
321
- state: innerState
322
- }, prefix);
439
+ if (isAsync) return async function* () {
440
+ const suggestions = parser.suggest({
441
+ ...context,
442
+ state: innerState
443
+ }, prefix);
444
+ for await (const s of suggestions) yield s;
445
+ }();
446
+ return function* () {
447
+ yield* syncParser.suggest({
448
+ ...context,
449
+ state: innerState
450
+ }, prefix);
451
+ }();
323
452
  },
324
453
  getDocFragments(state, defaultValue) {
325
454
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state.length > 0 ? {
326
455
  kind: "available",
327
456
  state: state.state.at(-1)
328
457
  } : { kind: "unavailable" };
329
- return parser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
458
+ return syncParser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
330
459
  }
331
460
  };
461
+ function validateMultipleResult(result) {
462
+ if (result.length < min) {
463
+ const customMessage = options.errors?.tooFew;
464
+ return {
465
+ success: false,
466
+ error: customMessage ? typeof customMessage === "function" ? customMessage(min, result.length) : customMessage : message`Expected at least ${text(min.toLocaleString("en"))} values, but got only ${text(result.length.toLocaleString("en"))}.`
467
+ };
468
+ } else if (result.length > max) {
469
+ const customMessage = options.errors?.tooMany;
470
+ return {
471
+ success: false,
472
+ error: customMessage ? typeof customMessage === "function" ? customMessage(max, result.length) : customMessage : message`Expected at most ${text(max.toLocaleString("en"))} values, but got ${text(result.length.toLocaleString("en"))}.`
473
+ };
474
+ }
475
+ return {
476
+ success: true,
477
+ value: result
478
+ };
479
+ }
480
+ return resultParser;
332
481
  }
333
482
 
334
483
  //#endregion