@optique/core 0.5.0-dev.79 → 0.5.0-dev.82
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/constructs.cjs +754 -0
- package/dist/constructs.d.cts +1100 -0
- package/dist/constructs.d.ts +1100 -0
- package/dist/constructs.js +748 -0
- package/dist/facade.cjs +56 -46
- package/dist/facade.js +32 -22
- package/dist/index.cjs +20 -17
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -1
- package/dist/modifiers.cjs +300 -0
- package/dist/modifiers.d.cts +192 -0
- package/dist/modifiers.d.ts +192 -0
- package/dist/modifiers.js +296 -0
- package/dist/parser.cjs +20 -1581
- package/dist/parser.d.cts +6 -1279
- package/dist/parser.d.ts +6 -1279
- package/dist/parser.js +4 -1565
- package/dist/primitives.cjs +595 -0
- package/dist/primitives.d.cts +274 -0
- package/dist/primitives.d.ts +274 -0
- package/dist/primitives.js +591 -0
- package/dist/valueparser.cjs +28 -28
- package/dist/valueparser.d.cts +144 -0
- package/dist/valueparser.d.ts +144 -0
- package/dist/valueparser.js +28 -28
- package/package.json +25 -1
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
const require_message = require('./message.cjs');
|
|
2
|
+
|
|
3
|
+
//#region src/constructs.ts
|
|
4
|
+
/**
|
|
5
|
+
* @since 0.5.0
|
|
6
|
+
*/
|
|
7
|
+
function or(...args) {
|
|
8
|
+
let parsers;
|
|
9
|
+
let options;
|
|
10
|
+
if (args.length > 0 && args[args.length - 1] && typeof args[args.length - 1] === "object" && !("$valueType" in args[args.length - 1])) {
|
|
11
|
+
options = args[args.length - 1];
|
|
12
|
+
parsers = args.slice(0, -1);
|
|
13
|
+
} else {
|
|
14
|
+
parsers = args;
|
|
15
|
+
options = void 0;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
$valueType: [],
|
|
19
|
+
$stateType: [],
|
|
20
|
+
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
21
|
+
usage: [{
|
|
22
|
+
type: "exclusive",
|
|
23
|
+
terms: parsers.map((p) => p.usage)
|
|
24
|
+
}],
|
|
25
|
+
initialState: void 0,
|
|
26
|
+
complete(state) {
|
|
27
|
+
if (state == null) return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: options?.errors?.noMatch ?? require_message.message`No matching option or command found.`
|
|
30
|
+
};
|
|
31
|
+
const [i, result] = state;
|
|
32
|
+
if (result.success) return parsers[i].complete(result.next.state);
|
|
33
|
+
return {
|
|
34
|
+
success: false,
|
|
35
|
+
error: result.error
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
parse(context) {
|
|
39
|
+
let error = {
|
|
40
|
+
consumed: 0,
|
|
41
|
+
error: context.buffer.length < 1 ? options?.errors?.noMatch ?? require_message.message`No matching option or command found.` : (() => {
|
|
42
|
+
const token = context.buffer[0];
|
|
43
|
+
const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
|
|
44
|
+
return options?.errors?.unexpectedInput != null ? typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput : defaultMsg;
|
|
45
|
+
})()
|
|
46
|
+
};
|
|
47
|
+
const orderedParsers = parsers.map((p, i) => [p, i]);
|
|
48
|
+
orderedParsers.sort(([_, a], [__, b]) => context.state?.[0] === a ? -1 : context.state?.[0] === b ? 1 : a - b);
|
|
49
|
+
for (const [parser, i] of orderedParsers) {
|
|
50
|
+
const result = parser.parse({
|
|
51
|
+
...context,
|
|
52
|
+
state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
|
|
53
|
+
});
|
|
54
|
+
if (result.success && result.consumed.length > 0) {
|
|
55
|
+
if (context.state?.[0] !== i && context.state?.[1].success) return {
|
|
56
|
+
success: false,
|
|
57
|
+
consumed: context.buffer.length - result.next.buffer.length,
|
|
58
|
+
error: require_message.message`${require_message.values(context.state[1].consumed)} and ${require_message.values(result.consumed)} cannot be used together.`
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
next: {
|
|
63
|
+
...context,
|
|
64
|
+
buffer: result.next.buffer,
|
|
65
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
66
|
+
state: [i, result]
|
|
67
|
+
},
|
|
68
|
+
consumed: result.consumed
|
|
69
|
+
};
|
|
70
|
+
} else if (!result.success && error.consumed < result.consumed) error = result;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
...error,
|
|
74
|
+
success: false
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
getDocFragments(state, _defaultValue) {
|
|
78
|
+
let description;
|
|
79
|
+
let fragments;
|
|
80
|
+
if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }, void 0).fragments);
|
|
81
|
+
else {
|
|
82
|
+
const [index, parserResult] = state.state;
|
|
83
|
+
const innerState = parserResult.success ? {
|
|
84
|
+
kind: "available",
|
|
85
|
+
state: parserResult.next.state
|
|
86
|
+
} : { kind: "unavailable" };
|
|
87
|
+
const docFragments = parsers[index].getDocFragments(innerState, void 0);
|
|
88
|
+
description = docFragments.description;
|
|
89
|
+
fragments = docFragments.fragments;
|
|
90
|
+
}
|
|
91
|
+
const entries = fragments.filter((f) => f.type === "entry");
|
|
92
|
+
const sections = [];
|
|
93
|
+
for (const fragment of fragments) {
|
|
94
|
+
if (fragment.type !== "section") continue;
|
|
95
|
+
if (fragment.title == null) entries.push(...fragment.entries);
|
|
96
|
+
else sections.push(fragment);
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
description,
|
|
100
|
+
fragments: [...sections.map((s) => ({
|
|
101
|
+
...s,
|
|
102
|
+
type: "section"
|
|
103
|
+
})), {
|
|
104
|
+
type: "section",
|
|
105
|
+
entries
|
|
106
|
+
}]
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @since 0.5.0
|
|
113
|
+
*/
|
|
114
|
+
function longestMatch(...args) {
|
|
115
|
+
let parsers;
|
|
116
|
+
let options;
|
|
117
|
+
if (args.length > 0 && args[args.length - 1] && typeof args[args.length - 1] === "object" && !("$valueType" in args[args.length - 1])) {
|
|
118
|
+
options = args[args.length - 1];
|
|
119
|
+
parsers = args.slice(0, -1);
|
|
120
|
+
} else {
|
|
121
|
+
parsers = args;
|
|
122
|
+
options = void 0;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
$valueType: [],
|
|
126
|
+
$stateType: [],
|
|
127
|
+
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
128
|
+
usage: [{
|
|
129
|
+
type: "exclusive",
|
|
130
|
+
terms: parsers.map((p) => p.usage)
|
|
131
|
+
}],
|
|
132
|
+
initialState: void 0,
|
|
133
|
+
complete(state) {
|
|
134
|
+
if (state == null) return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: options?.errors?.noMatch ?? require_message.message`No matching option or command found.`
|
|
137
|
+
};
|
|
138
|
+
const [i, result] = state;
|
|
139
|
+
if (result.success) return parsers[i].complete(result.next.state);
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
error: result.error
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
parse(context) {
|
|
146
|
+
let bestMatch = null;
|
|
147
|
+
let error = {
|
|
148
|
+
consumed: 0,
|
|
149
|
+
error: context.buffer.length < 1 ? options?.errors?.noMatch ?? require_message.message`No matching option or command found.` : (() => {
|
|
150
|
+
const token = context.buffer[0];
|
|
151
|
+
const defaultMsg = require_message.message`Unexpected option or subcommand: ${require_message.optionName(token)}.`;
|
|
152
|
+
return options?.errors?.unexpectedInput != null ? typeof options.errors.unexpectedInput === "function" ? options.errors.unexpectedInput(token) : options.errors.unexpectedInput : defaultMsg;
|
|
153
|
+
})()
|
|
154
|
+
};
|
|
155
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
156
|
+
const parser = parsers[i];
|
|
157
|
+
const result = parser.parse({
|
|
158
|
+
...context,
|
|
159
|
+
state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
|
|
160
|
+
});
|
|
161
|
+
if (result.success) {
|
|
162
|
+
const consumed = context.buffer.length - result.next.buffer.length;
|
|
163
|
+
if (bestMatch === null || consumed > bestMatch.consumed) bestMatch = {
|
|
164
|
+
index: i,
|
|
165
|
+
result,
|
|
166
|
+
consumed
|
|
167
|
+
};
|
|
168
|
+
} else if (error.consumed < result.consumed) error = result;
|
|
169
|
+
}
|
|
170
|
+
if (bestMatch && bestMatch.result.success) return {
|
|
171
|
+
success: true,
|
|
172
|
+
next: {
|
|
173
|
+
...context,
|
|
174
|
+
buffer: bestMatch.result.next.buffer,
|
|
175
|
+
optionsTerminated: bestMatch.result.next.optionsTerminated,
|
|
176
|
+
state: [bestMatch.index, bestMatch.result]
|
|
177
|
+
},
|
|
178
|
+
consumed: bestMatch.result.consumed
|
|
179
|
+
};
|
|
180
|
+
return {
|
|
181
|
+
...error,
|
|
182
|
+
success: false
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
getDocFragments(state, _defaultValue) {
|
|
186
|
+
let description;
|
|
187
|
+
let fragments;
|
|
188
|
+
if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
|
|
189
|
+
else {
|
|
190
|
+
const [i, result] = state.state;
|
|
191
|
+
if (result.success) {
|
|
192
|
+
const docResult = parsers[i].getDocFragments({
|
|
193
|
+
kind: "available",
|
|
194
|
+
state: result.next.state
|
|
195
|
+
});
|
|
196
|
+
description = docResult.description;
|
|
197
|
+
fragments = docResult.fragments;
|
|
198
|
+
} else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
description,
|
|
202
|
+
fragments
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function object(labelOrParsers, maybeParsersOrOptions, maybeOptions) {
|
|
208
|
+
const label = typeof labelOrParsers === "string" ? labelOrParsers : void 0;
|
|
209
|
+
let parsers;
|
|
210
|
+
let options = {};
|
|
211
|
+
if (typeof labelOrParsers === "string") {
|
|
212
|
+
parsers = maybeParsersOrOptions;
|
|
213
|
+
options = maybeOptions ?? {};
|
|
214
|
+
} else {
|
|
215
|
+
parsers = labelOrParsers;
|
|
216
|
+
options = maybeParsersOrOptions ?? {};
|
|
217
|
+
}
|
|
218
|
+
const parserPairs = Object.entries(parsers);
|
|
219
|
+
parserPairs.sort(([_, parserA], [__, parserB]) => parserB.priority - parserA.priority);
|
|
220
|
+
return {
|
|
221
|
+
$valueType: [],
|
|
222
|
+
$stateType: [],
|
|
223
|
+
priority: Math.max(...Object.values(parsers).map((p) => p.priority)),
|
|
224
|
+
usage: parserPairs.flatMap(([_, p]) => p.usage),
|
|
225
|
+
initialState: Object.fromEntries(Object.entries(parsers).map(([key, parser]) => [key, parser.initialState])),
|
|
226
|
+
parse(context) {
|
|
227
|
+
let error = {
|
|
228
|
+
consumed: 0,
|
|
229
|
+
error: context.buffer.length > 0 ? (() => {
|
|
230
|
+
const token = context.buffer[0];
|
|
231
|
+
const customMessage = options.errors?.unexpectedInput;
|
|
232
|
+
return customMessage ? typeof customMessage === "function" ? customMessage(token) : customMessage : require_message.message`Unexpected option or argument: ${token}.`;
|
|
233
|
+
})() : options.errors?.endOfInput ?? require_message.message`Expected an option or argument, but got end of input.`
|
|
234
|
+
};
|
|
235
|
+
let currentContext = context;
|
|
236
|
+
let anySuccess = false;
|
|
237
|
+
const allConsumed = [];
|
|
238
|
+
let madeProgress = true;
|
|
239
|
+
while (madeProgress && currentContext.buffer.length > 0) {
|
|
240
|
+
madeProgress = false;
|
|
241
|
+
for (const [field, parser] of parserPairs) {
|
|
242
|
+
const result = parser.parse({
|
|
243
|
+
...currentContext,
|
|
244
|
+
state: currentContext.state && typeof currentContext.state === "object" && field in currentContext.state ? currentContext.state[field] : parser.initialState
|
|
245
|
+
});
|
|
246
|
+
if (result.success && result.consumed.length > 0) {
|
|
247
|
+
currentContext = {
|
|
248
|
+
...currentContext,
|
|
249
|
+
buffer: result.next.buffer,
|
|
250
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
251
|
+
state: {
|
|
252
|
+
...currentContext.state,
|
|
253
|
+
[field]: result.next.state
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
allConsumed.push(...result.consumed);
|
|
257
|
+
anySuccess = true;
|
|
258
|
+
madeProgress = true;
|
|
259
|
+
break;
|
|
260
|
+
} else if (!result.success && error.consumed < result.consumed) error = result;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (anySuccess) return {
|
|
264
|
+
success: true,
|
|
265
|
+
next: currentContext,
|
|
266
|
+
consumed: allConsumed
|
|
267
|
+
};
|
|
268
|
+
if (context.buffer.length === 0) {
|
|
269
|
+
let allCanComplete = true;
|
|
270
|
+
for (const [field, parser] of parserPairs) {
|
|
271
|
+
const fieldState = context.state && typeof context.state === "object" && field in context.state ? context.state[field] : parser.initialState;
|
|
272
|
+
const completeResult = parser.complete(fieldState);
|
|
273
|
+
if (!completeResult.success) {
|
|
274
|
+
allCanComplete = false;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (allCanComplete) return {
|
|
279
|
+
success: true,
|
|
280
|
+
next: context,
|
|
281
|
+
consumed: []
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
...error,
|
|
286
|
+
success: false
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
complete(state) {
|
|
290
|
+
const result = {};
|
|
291
|
+
for (const field in state) {
|
|
292
|
+
if (!(field in parsers)) continue;
|
|
293
|
+
const valueResult = parsers[field].complete(state[field]);
|
|
294
|
+
if (valueResult.success) result[field] = valueResult.value;
|
|
295
|
+
else return {
|
|
296
|
+
success: false,
|
|
297
|
+
error: valueResult.error
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
success: true,
|
|
302
|
+
value: result
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
getDocFragments(state, defaultValue) {
|
|
306
|
+
const fragments = parserPairs.flatMap(([field, p]) => {
|
|
307
|
+
const fieldState = state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
308
|
+
kind: "available",
|
|
309
|
+
state: state.state[field]
|
|
310
|
+
};
|
|
311
|
+
return p.getDocFragments(fieldState, defaultValue?.[field]).fragments;
|
|
312
|
+
});
|
|
313
|
+
const entries = fragments.filter((d) => d.type === "entry");
|
|
314
|
+
const sections = [];
|
|
315
|
+
for (const fragment of fragments) {
|
|
316
|
+
if (fragment.type !== "section") continue;
|
|
317
|
+
if (fragment.title == null) entries.push(...fragment.entries);
|
|
318
|
+
else sections.push(fragment);
|
|
319
|
+
}
|
|
320
|
+
const section = {
|
|
321
|
+
title: label,
|
|
322
|
+
entries
|
|
323
|
+
};
|
|
324
|
+
sections.push(section);
|
|
325
|
+
return { fragments: sections.map((s) => ({
|
|
326
|
+
...s,
|
|
327
|
+
type: "section"
|
|
328
|
+
})) };
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function tuple(labelOrParsers, maybeParsers) {
|
|
333
|
+
const label = typeof labelOrParsers === "string" ? labelOrParsers : void 0;
|
|
334
|
+
const parsers = typeof labelOrParsers === "string" ? maybeParsers : labelOrParsers;
|
|
335
|
+
return {
|
|
336
|
+
$valueType: [],
|
|
337
|
+
$stateType: [],
|
|
338
|
+
usage: parsers.toSorted((a, b) => b.priority - a.priority).flatMap((p) => p.usage),
|
|
339
|
+
priority: parsers.length > 0 ? Math.max(...parsers.map((p) => p.priority)) : 0,
|
|
340
|
+
initialState: parsers.map((parser) => parser.initialState),
|
|
341
|
+
parse(context) {
|
|
342
|
+
let currentContext = context;
|
|
343
|
+
const allConsumed = [];
|
|
344
|
+
const matchedParsers = /* @__PURE__ */ new Set();
|
|
345
|
+
while (matchedParsers.size < parsers.length) {
|
|
346
|
+
let foundMatch = false;
|
|
347
|
+
let error = {
|
|
348
|
+
consumed: 0,
|
|
349
|
+
error: require_message.message`No remaining parsers could match the input.`
|
|
350
|
+
};
|
|
351
|
+
const remainingParsers = parsers.map((parser, index) => [parser, index]).filter(([_, index]) => !matchedParsers.has(index)).sort(([parserA], [parserB]) => parserB.priority - parserA.priority);
|
|
352
|
+
for (const [parser, index] of remainingParsers) {
|
|
353
|
+
const result = parser.parse({
|
|
354
|
+
...currentContext,
|
|
355
|
+
state: currentContext.state[index]
|
|
356
|
+
});
|
|
357
|
+
if (result.success && result.consumed.length > 0) {
|
|
358
|
+
currentContext = {
|
|
359
|
+
...currentContext,
|
|
360
|
+
buffer: result.next.buffer,
|
|
361
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
362
|
+
state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
|
|
363
|
+
};
|
|
364
|
+
allConsumed.push(...result.consumed);
|
|
365
|
+
matchedParsers.add(index);
|
|
366
|
+
foundMatch = true;
|
|
367
|
+
break;
|
|
368
|
+
} else if (!result.success && error.consumed < result.consumed) error = result;
|
|
369
|
+
}
|
|
370
|
+
if (!foundMatch) for (const [parser, index] of remainingParsers) {
|
|
371
|
+
const result = parser.parse({
|
|
372
|
+
...currentContext,
|
|
373
|
+
state: currentContext.state[index]
|
|
374
|
+
});
|
|
375
|
+
if (result.success && result.consumed.length < 1) {
|
|
376
|
+
currentContext = {
|
|
377
|
+
...currentContext,
|
|
378
|
+
state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
|
|
379
|
+
};
|
|
380
|
+
matchedParsers.add(index);
|
|
381
|
+
foundMatch = true;
|
|
382
|
+
break;
|
|
383
|
+
} else if (!result.success && result.consumed < 1) {
|
|
384
|
+
matchedParsers.add(index);
|
|
385
|
+
foundMatch = true;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (!foundMatch) return {
|
|
390
|
+
...error,
|
|
391
|
+
success: false
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
success: true,
|
|
396
|
+
next: currentContext,
|
|
397
|
+
consumed: allConsumed
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
complete(state) {
|
|
401
|
+
const result = [];
|
|
402
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
403
|
+
const valueResult = parsers[i].complete(state[i]);
|
|
404
|
+
if (valueResult.success) result[i] = valueResult.value;
|
|
405
|
+
else return {
|
|
406
|
+
success: false,
|
|
407
|
+
error: valueResult.error
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
success: true,
|
|
412
|
+
value: result
|
|
413
|
+
};
|
|
414
|
+
},
|
|
415
|
+
getDocFragments(state, defaultValue) {
|
|
416
|
+
const fragments = parsers.flatMap((p, i) => {
|
|
417
|
+
const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
418
|
+
kind: "available",
|
|
419
|
+
state: state.state[i]
|
|
420
|
+
};
|
|
421
|
+
return p.getDocFragments(indexState, defaultValue?.[i]).fragments;
|
|
422
|
+
});
|
|
423
|
+
const entries = fragments.filter((d) => d.type === "entry");
|
|
424
|
+
const sections = [];
|
|
425
|
+
for (const fragment of fragments) {
|
|
426
|
+
if (fragment.type !== "section") continue;
|
|
427
|
+
if (fragment.title == null) entries.push(...fragment.entries);
|
|
428
|
+
else sections.push(fragment);
|
|
429
|
+
}
|
|
430
|
+
const section = {
|
|
431
|
+
title: label,
|
|
432
|
+
entries
|
|
433
|
+
};
|
|
434
|
+
sections.push(section);
|
|
435
|
+
return { fragments: sections.map((s) => ({
|
|
436
|
+
...s,
|
|
437
|
+
type: "section"
|
|
438
|
+
})) };
|
|
439
|
+
},
|
|
440
|
+
[Symbol.for("Deno.customInspect")]() {
|
|
441
|
+
const parsersStr = parsers.length === 1 ? `[1 parser]` : `[${parsers.length} parsers]`;
|
|
442
|
+
return label ? `tuple(${JSON.stringify(label)}, ${parsersStr})` : `tuple(${parsersStr})`;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function merge(...args) {
|
|
447
|
+
const label = typeof args[0] === "string" ? args[0] : void 0;
|
|
448
|
+
let parsers = typeof args[0] === "string" ? args.slice(1) : args;
|
|
449
|
+
parsers = parsers.toSorted((a, b) => b.priority - a.priority);
|
|
450
|
+
const initialState = {};
|
|
451
|
+
for (const parser of parsers) if (parser.initialState && typeof parser.initialState === "object") for (const field in parser.initialState) initialState[field] = parser.initialState[field];
|
|
452
|
+
return {
|
|
453
|
+
$valueType: [],
|
|
454
|
+
$stateType: [],
|
|
455
|
+
priority: Math.max(...parsers.map((p) => p.priority)),
|
|
456
|
+
usage: parsers.flatMap((p) => p.usage),
|
|
457
|
+
initialState,
|
|
458
|
+
parse(context) {
|
|
459
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
460
|
+
const parser = parsers[i];
|
|
461
|
+
let parserState;
|
|
462
|
+
if (parser.initialState === void 0) parserState = void 0;
|
|
463
|
+
else if (parser.initialState && typeof parser.initialState === "object") if (context.state && typeof context.state === "object") {
|
|
464
|
+
const extractedState = {};
|
|
465
|
+
for (const field in parser.initialState) extractedState[field] = field in context.state ? context.state[field] : parser.initialState[field];
|
|
466
|
+
parserState = extractedState;
|
|
467
|
+
} else parserState = parser.initialState;
|
|
468
|
+
else parserState = parser.initialState;
|
|
469
|
+
const result = parser.parse({
|
|
470
|
+
...context,
|
|
471
|
+
state: parserState
|
|
472
|
+
});
|
|
473
|
+
if (result.success) {
|
|
474
|
+
let newState;
|
|
475
|
+
if (parser.initialState === void 0) newState = {
|
|
476
|
+
...context.state,
|
|
477
|
+
[`__parser_${i}`]: result.next.state
|
|
478
|
+
};
|
|
479
|
+
else newState = {
|
|
480
|
+
...context.state,
|
|
481
|
+
...result.next.state
|
|
482
|
+
};
|
|
483
|
+
return {
|
|
484
|
+
success: true,
|
|
485
|
+
next: {
|
|
486
|
+
...context,
|
|
487
|
+
buffer: result.next.buffer,
|
|
488
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
489
|
+
state: newState
|
|
490
|
+
},
|
|
491
|
+
consumed: result.consumed
|
|
492
|
+
};
|
|
493
|
+
} else if (result.consumed < 1) continue;
|
|
494
|
+
else return result;
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
success: false,
|
|
498
|
+
consumed: 0,
|
|
499
|
+
error: require_message.message`No matching option or argument found.`
|
|
500
|
+
};
|
|
501
|
+
},
|
|
502
|
+
complete(state) {
|
|
503
|
+
const object$1 = {};
|
|
504
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
505
|
+
const parser = parsers[i];
|
|
506
|
+
let parserState;
|
|
507
|
+
if (parser.initialState === void 0) {
|
|
508
|
+
const key = `__parser_${i}`;
|
|
509
|
+
if (state && typeof state === "object" && key in state) parserState = state[key];
|
|
510
|
+
else parserState = void 0;
|
|
511
|
+
} else if (parser.initialState && typeof parser.initialState === "object") if (state && typeof state === "object") {
|
|
512
|
+
const extractedState = {};
|
|
513
|
+
for (const field in parser.initialState) extractedState[field] = field in state ? state[field] : parser.initialState[field];
|
|
514
|
+
parserState = extractedState;
|
|
515
|
+
} else parserState = parser.initialState;
|
|
516
|
+
else parserState = parser.initialState;
|
|
517
|
+
const result = parser.complete(parserState);
|
|
518
|
+
if (!result.success) return result;
|
|
519
|
+
for (const field in result.value) object$1[field] = result.value[field];
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
success: true,
|
|
523
|
+
value: object$1
|
|
524
|
+
};
|
|
525
|
+
},
|
|
526
|
+
getDocFragments(state, _defaultValue) {
|
|
527
|
+
const fragments = parsers.flatMap((p) => {
|
|
528
|
+
const parserState = p.initialState === void 0 ? { kind: "unavailable" } : state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
529
|
+
kind: "available",
|
|
530
|
+
state: state.state
|
|
531
|
+
};
|
|
532
|
+
return p.getDocFragments(parserState, void 0).fragments;
|
|
533
|
+
});
|
|
534
|
+
const entries = fragments.filter((f) => f.type === "entry");
|
|
535
|
+
const sections = [];
|
|
536
|
+
for (const fragment of fragments) {
|
|
537
|
+
if (fragment.type !== "section") continue;
|
|
538
|
+
if (fragment.title == null) entries.push(...fragment.entries);
|
|
539
|
+
else sections.push(fragment);
|
|
540
|
+
}
|
|
541
|
+
if (label) {
|
|
542
|
+
const labeledSection = {
|
|
543
|
+
title: label,
|
|
544
|
+
entries
|
|
545
|
+
};
|
|
546
|
+
sections.push(labeledSection);
|
|
547
|
+
return { fragments: sections.map((s) => ({
|
|
548
|
+
...s,
|
|
549
|
+
type: "section"
|
|
550
|
+
})) };
|
|
551
|
+
}
|
|
552
|
+
return { fragments: [...sections.map((s) => ({
|
|
553
|
+
...s,
|
|
554
|
+
type: "section"
|
|
555
|
+
})), {
|
|
556
|
+
type: "section",
|
|
557
|
+
entries
|
|
558
|
+
}] };
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function concat(...parsers) {
|
|
563
|
+
const initialState = parsers.map((parser) => parser.initialState);
|
|
564
|
+
return {
|
|
565
|
+
$valueType: [],
|
|
566
|
+
$stateType: [],
|
|
567
|
+
priority: parsers.length > 0 ? Math.max(...parsers.map((p) => p.priority)) : 0,
|
|
568
|
+
usage: parsers.flatMap((p) => p.usage),
|
|
569
|
+
initialState,
|
|
570
|
+
parse(context) {
|
|
571
|
+
let currentContext = context;
|
|
572
|
+
const allConsumed = [];
|
|
573
|
+
const matchedParsers = /* @__PURE__ */ new Set();
|
|
574
|
+
while (matchedParsers.size < parsers.length) {
|
|
575
|
+
let foundMatch = false;
|
|
576
|
+
let error = {
|
|
577
|
+
consumed: 0,
|
|
578
|
+
error: require_message.message`No remaining parsers could match the input.`
|
|
579
|
+
};
|
|
580
|
+
const remainingParsers = parsers.map((parser, index) => [parser, index]).filter(([_, index]) => !matchedParsers.has(index)).sort(([parserA], [parserB]) => parserB.priority - parserA.priority);
|
|
581
|
+
for (const [parser, index] of remainingParsers) {
|
|
582
|
+
const result = parser.parse({
|
|
583
|
+
...currentContext,
|
|
584
|
+
state: currentContext.state[index]
|
|
585
|
+
});
|
|
586
|
+
if (result.success && result.consumed.length > 0) {
|
|
587
|
+
currentContext = {
|
|
588
|
+
...currentContext,
|
|
589
|
+
buffer: result.next.buffer,
|
|
590
|
+
optionsTerminated: result.next.optionsTerminated,
|
|
591
|
+
state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
|
|
592
|
+
};
|
|
593
|
+
allConsumed.push(...result.consumed);
|
|
594
|
+
matchedParsers.add(index);
|
|
595
|
+
foundMatch = true;
|
|
596
|
+
break;
|
|
597
|
+
} else if (!result.success && error.consumed < result.consumed) error = result;
|
|
598
|
+
}
|
|
599
|
+
if (!foundMatch) for (const [parser, index] of remainingParsers) {
|
|
600
|
+
const result = parser.parse({
|
|
601
|
+
...currentContext,
|
|
602
|
+
state: currentContext.state[index]
|
|
603
|
+
});
|
|
604
|
+
if (result.success && result.consumed.length < 1) {
|
|
605
|
+
currentContext = {
|
|
606
|
+
...currentContext,
|
|
607
|
+
state: currentContext.state.map((s, idx) => idx === index ? result.next.state : s)
|
|
608
|
+
};
|
|
609
|
+
matchedParsers.add(index);
|
|
610
|
+
foundMatch = true;
|
|
611
|
+
break;
|
|
612
|
+
} else if (!result.success && result.consumed < 1) {
|
|
613
|
+
matchedParsers.add(index);
|
|
614
|
+
foundMatch = true;
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (!foundMatch) return {
|
|
619
|
+
...error,
|
|
620
|
+
success: false
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
success: true,
|
|
625
|
+
next: currentContext,
|
|
626
|
+
consumed: allConsumed
|
|
627
|
+
};
|
|
628
|
+
},
|
|
629
|
+
complete(state) {
|
|
630
|
+
const results = [];
|
|
631
|
+
for (let i = 0; i < parsers.length; i++) {
|
|
632
|
+
const parser = parsers[i];
|
|
633
|
+
const parserState = state[i];
|
|
634
|
+
const result = parser.complete(parserState);
|
|
635
|
+
if (!result.success) return result;
|
|
636
|
+
if (Array.isArray(result.value)) results.push(...result.value);
|
|
637
|
+
else results.push(result.value);
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
success: true,
|
|
641
|
+
value: results
|
|
642
|
+
};
|
|
643
|
+
},
|
|
644
|
+
getDocFragments(state, _defaultValue) {
|
|
645
|
+
const fragments = parsers.flatMap((p, index) => {
|
|
646
|
+
const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
|
|
647
|
+
kind: "available",
|
|
648
|
+
state: state.state[index]
|
|
649
|
+
};
|
|
650
|
+
return p.getDocFragments(indexState, void 0).fragments;
|
|
651
|
+
});
|
|
652
|
+
const entries = fragments.filter((f) => f.type === "entry");
|
|
653
|
+
const sections = [];
|
|
654
|
+
for (const fragment of fragments) {
|
|
655
|
+
if (fragment.type !== "section") continue;
|
|
656
|
+
if (fragment.title == null) entries.push(...fragment.entries);
|
|
657
|
+
else sections.push(fragment);
|
|
658
|
+
}
|
|
659
|
+
const result = [...sections.map((s) => ({
|
|
660
|
+
...s,
|
|
661
|
+
type: "section"
|
|
662
|
+
}))];
|
|
663
|
+
if (entries.length > 0) result.push({
|
|
664
|
+
type: "section",
|
|
665
|
+
entries
|
|
666
|
+
});
|
|
667
|
+
return { fragments: result };
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Wraps a parser with a group label for documentation purposes.
|
|
673
|
+
*
|
|
674
|
+
* The `group()` function is a documentation-only wrapper that applies a label
|
|
675
|
+
* to any parser for help text organization. This allows you to use clean code
|
|
676
|
+
* structure with combinators like {@link merge} while maintaining well-organized
|
|
677
|
+
* help text through group labeling.
|
|
678
|
+
*
|
|
679
|
+
* The wrapped parser has identical parsing behavior but generates documentation
|
|
680
|
+
* fragments wrapped in a labeled section. This is particularly useful when
|
|
681
|
+
* combining parsers using {@link merge}—you can wrap the merged result with
|
|
682
|
+
* `group()` to add a section header in help output.
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```typescript
|
|
686
|
+
* const apiOptions = merge(
|
|
687
|
+
* object({ endpoint: option("--endpoint", string()) }),
|
|
688
|
+
* object({ timeout: option("--timeout", integer()) })
|
|
689
|
+
* );
|
|
690
|
+
*
|
|
691
|
+
* const groupedApiOptions = group("API Options", apiOptions);
|
|
692
|
+
* // Now produces a labeled "API Options" section in help text
|
|
693
|
+
* ```
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```typescript
|
|
697
|
+
* // Can be used with any parser, not just merge()
|
|
698
|
+
* const verboseGroup = group("Verbosity", object({
|
|
699
|
+
* verbose: option("-v", "--verbose"),
|
|
700
|
+
* quiet: option("-q", "--quiet")
|
|
701
|
+
* }));
|
|
702
|
+
* ```
|
|
703
|
+
*
|
|
704
|
+
* @template TValue The value type of the wrapped parser.
|
|
705
|
+
* @template TState The state type of the wrapped parser.
|
|
706
|
+
* @param label A descriptive label for this parser group, used for
|
|
707
|
+
* documentation and help text organization.
|
|
708
|
+
* @param parser The parser to wrap with a group label.
|
|
709
|
+
* @returns A new parser that behaves identically to the input parser
|
|
710
|
+
* but generates documentation within a labeled section.
|
|
711
|
+
* @since 0.4.0
|
|
712
|
+
*/
|
|
713
|
+
function group(label, parser) {
|
|
714
|
+
return {
|
|
715
|
+
$valueType: parser.$valueType,
|
|
716
|
+
$stateType: parser.$stateType,
|
|
717
|
+
priority: parser.priority,
|
|
718
|
+
usage: parser.usage,
|
|
719
|
+
initialState: parser.initialState,
|
|
720
|
+
parse: (context) => parser.parse(context),
|
|
721
|
+
complete: (state) => parser.complete(state),
|
|
722
|
+
getDocFragments: (state, defaultValue) => {
|
|
723
|
+
const { description, fragments } = parser.getDocFragments(state, defaultValue);
|
|
724
|
+
const allEntries = [];
|
|
725
|
+
const titledSections = [];
|
|
726
|
+
for (const fragment of fragments) if (fragment.type === "entry") allEntries.push(fragment);
|
|
727
|
+
else if (fragment.type === "section") if (fragment.title) titledSections.push(fragment);
|
|
728
|
+
else allEntries.push(...fragment.entries);
|
|
729
|
+
const labeledSection = {
|
|
730
|
+
title: label,
|
|
731
|
+
entries: allEntries
|
|
732
|
+
};
|
|
733
|
+
return {
|
|
734
|
+
description,
|
|
735
|
+
fragments: [...titledSections.map((s) => ({
|
|
736
|
+
...s,
|
|
737
|
+
type: "section"
|
|
738
|
+
})), {
|
|
739
|
+
type: "section",
|
|
740
|
+
...labeledSection
|
|
741
|
+
}]
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
//#endregion
|
|
748
|
+
exports.concat = concat;
|
|
749
|
+
exports.group = group;
|
|
750
|
+
exports.longestMatch = longestMatch;
|
|
751
|
+
exports.merge = merge;
|
|
752
|
+
exports.object = object;
|
|
753
|
+
exports.or = or;
|
|
754
|
+
exports.tuple = tuple;
|