@dallaylaen/ski-interpreter 2.2.1 → 2.3.0
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/CHANGELOG.md +37 -0
- package/README.md +65 -11
- package/bin/ski.js +195 -82
- package/lib/ski-interpreter.cjs.js +374 -261
- package/lib/ski-interpreter.cjs.js.map +3 -3
- package/lib/ski-interpreter.esm.js +374 -261
- package/lib/ski-interpreter.esm.js.map +2 -2
- package/lib/ski-interpreter.min.js +3 -3
- package/lib/ski-interpreter.min.js.map +3 -3
- package/lib/ski-quest.min.js +3 -3
- package/lib/ski-quest.min.js.map +3 -3
- package/package.json +4 -1
- package/types/src/expr.d.ts +131 -118
- package/types/src/parser.d.ts +6 -2
- package/types/src/quest.d.ts +44 -0
|
@@ -101,12 +101,19 @@ var require_expr = __commonJS({
|
|
|
101
101
|
max: 1e3,
|
|
102
102
|
maxArgs: 32
|
|
103
103
|
};
|
|
104
|
+
var ORDER = {
|
|
105
|
+
"leftmost-outermost": "LO",
|
|
106
|
+
"leftmost-innermost": "LI",
|
|
107
|
+
LO: "LO",
|
|
108
|
+
LI: "LI"
|
|
109
|
+
};
|
|
104
110
|
var control = {
|
|
105
111
|
descend: prepareWrapper("descend"),
|
|
106
112
|
prune: prepareWrapper("prune"),
|
|
107
113
|
redo: prepareWrapper("redo"),
|
|
108
114
|
stop: prepareWrapper("stop")
|
|
109
115
|
};
|
|
116
|
+
var native = {};
|
|
110
117
|
var Expr = class _Expr {
|
|
111
118
|
/**
|
|
112
119
|
* @descr A combinatory logic expression.
|
|
@@ -121,10 +128,43 @@ var require_expr = __commonJS({
|
|
|
121
128
|
* src?: string,
|
|
122
129
|
* parser: object,
|
|
123
130
|
* }} [context]
|
|
131
|
+
* @property {number} [arity] - number of arguments the term is waiting for (if known)
|
|
132
|
+
* @property {string} [note] - a brief description what the term does
|
|
133
|
+
* @property {string} [fancyName] - how to display in html mode, e.g. φ instead of 'f'
|
|
134
|
+
* Typically only applicable to descendants of Named.
|
|
135
|
+
* @property {TermInfo} [props] - properties inferred from the term's behavior
|
|
124
136
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
137
|
+
/**
|
|
138
|
+
*
|
|
139
|
+
* @desc Define properties of the term based on user supplied options and/or inference results.
|
|
140
|
+
* Typically useful for declaring Native and Alias terms.
|
|
141
|
+
* @private
|
|
142
|
+
* @param {Object} options
|
|
143
|
+
* @param {string} [options.note] - a brief description what the term does
|
|
144
|
+
* @param {number} [options.arity] - number of arguments the term is waiting for (if known)
|
|
145
|
+
* @param {string} [options.fancy] - how to display in html mode, e.g. φ instead of 'f'
|
|
146
|
+
* @param {boolean} [options.canonize] - whether to try to infer the properties
|
|
147
|
+
* @param {number} [options.max] - maximum number of steps for inference, if canonize is true
|
|
148
|
+
* @param {number} [options.maxArgs] - maximum number of arguments for inference, if canonize is true
|
|
149
|
+
* @return {this}
|
|
150
|
+
*/
|
|
151
|
+
_setup(options = {}) {
|
|
152
|
+
if (options.fancy !== void 0)
|
|
153
|
+
this.fancyName = options.fancyName;
|
|
154
|
+
if (options.note !== void 0)
|
|
155
|
+
this.note = options.note;
|
|
156
|
+
if (options.arity !== void 0)
|
|
157
|
+
this.arity = options.arity;
|
|
158
|
+
if (options.canonize) {
|
|
159
|
+
const guess = this.infer(options);
|
|
160
|
+
if (guess.normal) {
|
|
161
|
+
this.arity = this.arity ?? guess.arity;
|
|
162
|
+
this.note = this.note ?? guess.expr.format({ html: true, lambda: ["", " ↦ ", ""] });
|
|
163
|
+
delete guess.steps;
|
|
164
|
+
this.props = guess;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return this;
|
|
128
168
|
}
|
|
129
169
|
/**
|
|
130
170
|
* @desc apply self to zero or more terms and return the resulting term,
|
|
@@ -158,18 +198,67 @@ var require_expr = __commonJS({
|
|
|
158
198
|
/**
|
|
159
199
|
* @desc Traverse the expression tree, applying change() to each node.
|
|
160
200
|
* If change() returns an Expr, the node is replaced with that value.
|
|
161
|
-
*
|
|
162
|
-
* or
|
|
201
|
+
* A null/undefined value is interpreted as
|
|
202
|
+
* "descend further if applicable, or leave the node unchanged".
|
|
203
|
+
*
|
|
204
|
+
* Returned values may be decorated:
|
|
205
|
+
*
|
|
206
|
+
* SKI.control.prune will suppress further descending even if nothing was returned
|
|
207
|
+
* SKI.control.stop will terminate further changes.
|
|
208
|
+
* SKI.control.redo will apply the callback to the returned subtree, recursively.
|
|
163
209
|
*
|
|
164
|
-
*
|
|
210
|
+
* Note that if redo was applied at least once to a subtree, a null return from the same subtree
|
|
211
|
+
* will be replaced by the last non-null value returned.
|
|
212
|
+
*
|
|
213
|
+
* The traversal order is leftmost-outermost, unless options.order = 'leftmost-innermost' is specified.
|
|
214
|
+
* Short aliases 'LO' and 'LI' (case-sensitive) are also accepted.
|
|
165
215
|
*
|
|
166
216
|
* Returns null if no changes were made, or the new expression otherwise.
|
|
167
217
|
*
|
|
168
|
-
* @param {
|
|
218
|
+
* @param {{
|
|
219
|
+
* order?: 'LO' | 'LI' | 'leftmost-outermost' | 'leftmost-innermost',
|
|
220
|
+
* }} [options]
|
|
221
|
+
* @param {(e:Expr) => TraverseValue<Expr>} change
|
|
169
222
|
* @returns {Expr|null}
|
|
170
223
|
*/
|
|
171
|
-
traverse(change) {
|
|
172
|
-
|
|
224
|
+
traverse(options, change) {
|
|
225
|
+
if (typeof options === "function") {
|
|
226
|
+
change = options;
|
|
227
|
+
options = {};
|
|
228
|
+
}
|
|
229
|
+
const order = ORDER[options.order ?? "LO"];
|
|
230
|
+
if (order === void 0)
|
|
231
|
+
throw new Error("Unknown traversal order: " + options.order);
|
|
232
|
+
const [expr, _] = unwrap(this._traverse_redo({ order }, change));
|
|
233
|
+
return expr;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* @private
|
|
237
|
+
* @param {Object} options
|
|
238
|
+
* @param {(e:Expr) => TraverseValue<Expr>} change
|
|
239
|
+
* @returns {TraverseValue<Expr>}
|
|
240
|
+
*/
|
|
241
|
+
_traverse_redo(options, change) {
|
|
242
|
+
let action;
|
|
243
|
+
let expr = this;
|
|
244
|
+
let prev;
|
|
245
|
+
do {
|
|
246
|
+
prev = expr;
|
|
247
|
+
const next = options.order === "LI" ? expr._traverse_descend(options, change) ?? change(expr) : change(expr) ?? expr._traverse_descend(options, change);
|
|
248
|
+
[expr, action] = unwrap(next);
|
|
249
|
+
} while (expr && action === control.redo);
|
|
250
|
+
if (!expr && prev !== this)
|
|
251
|
+
expr = prev;
|
|
252
|
+
return action ? action(expr) : expr;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* @private
|
|
256
|
+
* @param {Object} options
|
|
257
|
+
* @param {(e:Expr) => TraverseValue<Expr>} change
|
|
258
|
+
* @returns {TraverseValue<Expr>}
|
|
259
|
+
*/
|
|
260
|
+
_traverse_descend(options, change) {
|
|
261
|
+
return null;
|
|
173
262
|
}
|
|
174
263
|
/**
|
|
175
264
|
* @desc Returns true if predicate() is true for any subterm of the expression, false otherwise.
|
|
@@ -233,60 +322,61 @@ var require_expr = __commonJS({
|
|
|
233
322
|
* Use toLambda() if you want to get a lambda term in any case.
|
|
234
323
|
*
|
|
235
324
|
* @param {{max?: number, maxArgs?: number}} options
|
|
236
|
-
* @return {
|
|
237
|
-
* normal: boolean,
|
|
238
|
-
* steps: number,
|
|
239
|
-
* expr?: Expr,
|
|
240
|
-
* arity?: number,
|
|
241
|
-
* proper?: boolean,
|
|
242
|
-
* discard?: boolean,
|
|
243
|
-
* duplicate?: boolean,
|
|
244
|
-
* skip?: Set<number>,
|
|
245
|
-
* dup?: Set<number>,
|
|
246
|
-
* }}
|
|
325
|
+
* @return {TermInfo}
|
|
247
326
|
*/
|
|
248
327
|
infer(options = {}) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
328
|
+
return this._infer({
|
|
329
|
+
max: options.max ?? DEFAULTS.max,
|
|
330
|
+
maxArgs: options.maxArgs ?? DEFAULTS.maxArgs
|
|
331
|
+
}, 0);
|
|
253
332
|
}
|
|
254
333
|
/**
|
|
255
|
-
*
|
|
256
|
-
* @param {{max: number, maxArgs: number
|
|
257
|
-
* @param {
|
|
258
|
-
* @
|
|
259
|
-
* @returns {{
|
|
260
|
-
* normal: boolean,
|
|
261
|
-
* steps: number,
|
|
262
|
-
* expr?: Expr,
|
|
263
|
-
* arity?: number,
|
|
264
|
-
* skip?: Set<number>,
|
|
265
|
-
* dup?: Set<number>,
|
|
266
|
-
* duplicate, discard, proper: boolean
|
|
267
|
-
* }
|
|
334
|
+
* @desc Internal method for infer(), which performs the actual inference.
|
|
335
|
+
* @param {{max: number, maxArgs: number}} options
|
|
336
|
+
* @param {number} nargs - var index to avoid name clashes
|
|
337
|
+
* @returns {TermInfo}
|
|
268
338
|
* @private
|
|
269
339
|
*/
|
|
270
|
-
_infer(options,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
340
|
+
_infer(options, nargs) {
|
|
341
|
+
const probe = [];
|
|
342
|
+
let steps = 0;
|
|
343
|
+
let expr = this;
|
|
344
|
+
main: for (let i = 0; i < options.maxArgs; i++) {
|
|
345
|
+
const next = expr.run({ max: options.max - steps });
|
|
346
|
+
steps += next.steps;
|
|
347
|
+
if (!next.final)
|
|
348
|
+
break;
|
|
349
|
+
if (firstVar(next.expr)) {
|
|
350
|
+
expr = next.expr;
|
|
351
|
+
if (!expr.any((e) => !(e instanceof FreeVar || e instanceof App)))
|
|
352
|
+
return maybeLambda(probe, expr, { steps });
|
|
353
|
+
const list = expr.unroll();
|
|
354
|
+
let discard = false;
|
|
355
|
+
let duplicate = false;
|
|
356
|
+
const acc = [];
|
|
357
|
+
for (let j = 1; j < list.length; j++) {
|
|
358
|
+
const sub = list[j]._infer(
|
|
359
|
+
{ maxArgs: options.maxArgs - nargs, max: options.max - steps },
|
|
360
|
+
// limit recursion
|
|
361
|
+
nargs + i
|
|
362
|
+
// avoid variable name clashes
|
|
363
|
+
);
|
|
364
|
+
steps += sub.steps;
|
|
365
|
+
if (!sub.expr)
|
|
366
|
+
break main;
|
|
367
|
+
if (sub.discard)
|
|
368
|
+
discard = true;
|
|
369
|
+
if (sub.duplicate)
|
|
370
|
+
duplicate = true;
|
|
371
|
+
acc.push(sub.expr);
|
|
372
|
+
}
|
|
373
|
+
return maybeLambda(probe, list[0].apply(...acc), { discard, duplicate, steps });
|
|
374
|
+
}
|
|
375
|
+
const push = nthvar(nargs + i);
|
|
376
|
+
probe.push(push);
|
|
377
|
+
expr = next.expr.apply(push);
|
|
279
378
|
}
|
|
280
|
-
|
|
281
|
-
steps += next.steps;
|
|
282
|
-
if (!next.final)
|
|
283
|
-
return { normal: false, steps };
|
|
284
|
-
if (next.steps !== 0)
|
|
285
|
-
return next.expr._infer(options, preArgs, steps);
|
|
286
|
-
if (this.unroll()[0] instanceof FreeVar)
|
|
287
|
-
return { normal: false, steps };
|
|
288
|
-
const push = nthvar(preArgs.length + options.index);
|
|
289
|
-
return this.apply(push)._infer(options, [...preArgs, push], steps);
|
|
379
|
+
return { normal: false, proper: false, steps };
|
|
290
380
|
}
|
|
291
381
|
/**
|
|
292
382
|
* @desc Expand an expression into a list of terms
|
|
@@ -324,7 +414,7 @@ var require_expr = __commonJS({
|
|
|
324
414
|
* @return {IterableIterator<{expr: Expr, steps?: number, comment?: string}>}
|
|
325
415
|
*/
|
|
326
416
|
*toLambda(options = {}) {
|
|
327
|
-
|
|
417
|
+
let expr = this.traverse((e) => {
|
|
328
418
|
if (e instanceof FreeVar || e instanceof App || e instanceof Lambda || e instanceof Alias)
|
|
329
419
|
return null;
|
|
330
420
|
const guess = e.infer({ max: options.max, maxArgs: options.maxArgs });
|
|
@@ -332,7 +422,25 @@ var require_expr = __commonJS({
|
|
|
332
422
|
throw new Error("Failed to infer an equivalent lambda term for " + e);
|
|
333
423
|
return guess.expr;
|
|
334
424
|
}) ?? this;
|
|
335
|
-
|
|
425
|
+
const seen = /* @__PURE__ */ new Set();
|
|
426
|
+
let steps = 0;
|
|
427
|
+
while (expr) {
|
|
428
|
+
const next = expr.traverse({ order: "LI" }, (e) => {
|
|
429
|
+
if (seen.has(e))
|
|
430
|
+
return null;
|
|
431
|
+
if (e instanceof App && e.fun instanceof Lambda) {
|
|
432
|
+
const guess = e.infer({ max: options.max, maxArgs: options.maxArgs });
|
|
433
|
+
steps += guess.steps;
|
|
434
|
+
if (!guess.normal) {
|
|
435
|
+
seen.add(e);
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
return control.stop(guess.expr);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
yield { expr, steps };
|
|
442
|
+
expr = next;
|
|
443
|
+
}
|
|
336
444
|
}
|
|
337
445
|
/**
|
|
338
446
|
* @desc Rewrite the expression into S, K, and I combinators step by step.
|
|
@@ -345,28 +453,31 @@ var require_expr = __commonJS({
|
|
|
345
453
|
* @return {IterableIterator<{final: boolean, expr: Expr, steps: number}>}
|
|
346
454
|
*/
|
|
347
455
|
*toSKI(options = {}) {
|
|
456
|
+
let expr = this.traverse((e) => {
|
|
457
|
+
if (e instanceof FreeVar || e instanceof App || e instanceof Lambda || e instanceof Alias)
|
|
458
|
+
return null;
|
|
459
|
+
return e.infer().expr;
|
|
460
|
+
}) ?? this;
|
|
348
461
|
let steps = 0;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
462
|
+
while (expr) {
|
|
463
|
+
const next = expr.traverse({ order: "LI" }, (e) => {
|
|
464
|
+
if (!(e instanceof Lambda) || e.impl instanceof Lambda)
|
|
465
|
+
return null;
|
|
466
|
+
if (e.impl === e.arg)
|
|
467
|
+
return control.stop(native.I);
|
|
468
|
+
if (!e.impl.any((t) => t === e.arg))
|
|
469
|
+
return control.stop(native.K.apply(e.impl));
|
|
470
|
+
if (!(e.impl instanceof App))
|
|
471
|
+
throw new Error("toSKI: assert failed: lambda body is of unexpected type " + e.impl.constructor.name);
|
|
472
|
+
if (e.impl.arg === e.arg && !e.impl.fun.any((t) => t === e.arg))
|
|
473
|
+
return control.stop(e.impl.fun);
|
|
474
|
+
return control.stop(native.S.apply(new Lambda(e.arg, e.impl.fun), new Lambda(e.arg, e.impl.arg)));
|
|
475
|
+
});
|
|
476
|
+
yield { expr, steps, final: !next };
|
|
477
|
+
steps++;
|
|
357
478
|
expr = next;
|
|
358
|
-
steps += opt.steps;
|
|
359
479
|
}
|
|
360
480
|
}
|
|
361
|
-
/**
|
|
362
|
-
* @desc Internal method for toSKI, which performs one step of the conversion.
|
|
363
|
-
* @param {{max: number, steps: number}} options
|
|
364
|
-
* @returns {Expr}
|
|
365
|
-
* @private
|
|
366
|
-
*/
|
|
367
|
-
_rski(options) {
|
|
368
|
-
return this;
|
|
369
|
-
}
|
|
370
481
|
/**
|
|
371
482
|
* Replace all instances of plug in the expression with value and return the resulting expression,
|
|
372
483
|
* or null if no changes could be made.
|
|
@@ -506,19 +617,24 @@ var require_expr = __commonJS({
|
|
|
506
617
|
}
|
|
507
618
|
/**
|
|
508
619
|
* @desc Assert expression equality. Can be used in tests.
|
|
509
|
-
*
|
|
620
|
+
*
|
|
621
|
+
* `this` is the expected value and the argument is the actual one.
|
|
622
|
+
* Mnemonic: the expected value is always a combinator, the actual one may be anything.
|
|
623
|
+
*
|
|
624
|
+
* @param {Expr} actual
|
|
510
625
|
* @param {string} comment
|
|
511
626
|
*/
|
|
512
|
-
expect(
|
|
627
|
+
expect(actual, comment = "") {
|
|
513
628
|
comment = comment ? comment + ": " : "";
|
|
514
|
-
if (!(
|
|
515
|
-
throw new Error(comment + "
|
|
516
|
-
|
|
629
|
+
if (!(actual instanceof _Expr)) {
|
|
630
|
+
throw new Error(comment + "Expected a combinator but found " + (actual?.constructor?.name ?? typeof actual));
|
|
631
|
+
}
|
|
632
|
+
const diff = this.diff(actual);
|
|
517
633
|
if (!diff)
|
|
518
634
|
return;
|
|
519
635
|
const poorMans = new Error(comment + diff);
|
|
520
|
-
poorMans.expected =
|
|
521
|
-
poorMans.actual =
|
|
636
|
+
poorMans.expected = this.diag();
|
|
637
|
+
poorMans.actual = actual.diag();
|
|
522
638
|
throw poorMans;
|
|
523
639
|
}
|
|
524
640
|
/**
|
|
@@ -646,7 +762,7 @@ var require_expr = __commonJS({
|
|
|
646
762
|
if (e instanceof Lambda)
|
|
647
763
|
return [`${indent}Lambda (${e.arg}[${e.arg.id}]):`, ...rec(e.impl, indent + " ")];
|
|
648
764
|
if (e instanceof Alias)
|
|
649
|
-
return [
|
|
765
|
+
return [`${indent}Alias (${e.name}): \\`, ...rec(e.impl, indent)];
|
|
650
766
|
if (e instanceof FreeVar)
|
|
651
767
|
return [`${indent}FreeVar: ${e.name}[${e.id}]`];
|
|
652
768
|
return [`${indent}${e.constructor.name}: ${e}`];
|
|
@@ -673,57 +789,20 @@ var require_expr = __commonJS({
|
|
|
673
789
|
super();
|
|
674
790
|
this.arg = arg;
|
|
675
791
|
this.fun = fun;
|
|
676
|
-
this.final = false;
|
|
677
|
-
this.arity = this.fun.arity > 0 ? this.fun.arity - 1 : 0;
|
|
678
792
|
}
|
|
793
|
+
/** @property {boolean} [final] */
|
|
679
794
|
weight() {
|
|
680
795
|
return this.fun.weight() + this.arg.weight();
|
|
681
796
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
return { normal: false, steps };
|
|
692
|
-
let discard = false;
|
|
693
|
-
let duplicate = false;
|
|
694
|
-
const out = [];
|
|
695
|
-
for (const term of list) {
|
|
696
|
-
const guess = term._infer({
|
|
697
|
-
...options,
|
|
698
|
-
maxArgs: options.maxArgs - preArgs.length,
|
|
699
|
-
max: options.max - steps,
|
|
700
|
-
index: preArgs.length + options.index
|
|
701
|
-
});
|
|
702
|
-
steps += guess.steps;
|
|
703
|
-
if (!guess.normal)
|
|
704
|
-
return { normal: false, steps };
|
|
705
|
-
out.push(guess.expr);
|
|
706
|
-
discard = discard || guess.discard;
|
|
707
|
-
duplicate = duplicate || guess.duplicate;
|
|
708
|
-
}
|
|
709
|
-
return {
|
|
710
|
-
normal: true,
|
|
711
|
-
steps,
|
|
712
|
-
...maybeLambda(preArgs, first.apply(...out), {
|
|
713
|
-
discard,
|
|
714
|
-
duplicate
|
|
715
|
-
})
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
traverse(change) {
|
|
719
|
-
const replaced = change(this);
|
|
720
|
-
if (replaced instanceof Expr)
|
|
721
|
-
return replaced;
|
|
722
|
-
const fun = this.fun.traverse(change);
|
|
723
|
-
const arg = this.arg.traverse(change);
|
|
724
|
-
if (!fun && !arg)
|
|
725
|
-
return null;
|
|
726
|
-
return (fun ?? this.fun).apply(arg ?? this.arg);
|
|
797
|
+
_traverse_descend(options, change) {
|
|
798
|
+
const [fun, fAction] = unwrap(this.fun._traverse_redo(options, change));
|
|
799
|
+
if (fAction === control.stop)
|
|
800
|
+
return control.stop(fun ? fun.apply(this.arg) : null);
|
|
801
|
+
const [arg, aAction] = unwrap(this.arg._traverse_redo(options, change));
|
|
802
|
+
const final = fun || arg ? (fun ?? this.fun).apply(arg ?? this.arg) : null;
|
|
803
|
+
if (aAction === control.stop)
|
|
804
|
+
return control.stop(final);
|
|
805
|
+
return final;
|
|
727
806
|
}
|
|
728
807
|
any(predicate) {
|
|
729
808
|
return predicate(this) || this.fun.any(predicate) || this.arg.any(predicate);
|
|
@@ -782,11 +861,6 @@ var require_expr = __commonJS({
|
|
|
782
861
|
unroll() {
|
|
783
862
|
return [...this.fun.unroll(), this.arg];
|
|
784
863
|
}
|
|
785
|
-
_rski(options) {
|
|
786
|
-
if (options.steps >= options.max)
|
|
787
|
-
return this;
|
|
788
|
-
return this.fun._rski(options).apply(this.arg._rski(options));
|
|
789
|
-
}
|
|
790
864
|
diff(other, swap = false) {
|
|
791
865
|
if (!(other instanceof _App))
|
|
792
866
|
return super.diff(other, swap);
|
|
@@ -848,6 +922,8 @@ var require_expr = __commonJS({
|
|
|
848
922
|
* If a scope object is given, however, two variables with the same name and scope
|
|
849
923
|
* are considered identical.
|
|
850
924
|
*
|
|
925
|
+
* By convention, FreeVar.global is a constant denoting a global unbound variable.
|
|
926
|
+
*
|
|
851
927
|
* @param {string} name - name of the variable
|
|
852
928
|
* @param {any} scope - an object representing where the variable belongs to.
|
|
853
929
|
*/
|
|
@@ -878,6 +954,7 @@ var require_expr = __commonJS({
|
|
|
878
954
|
return options.var[0] + name + options.var[1];
|
|
879
955
|
}
|
|
880
956
|
};
|
|
957
|
+
FreeVar.global = ["global"];
|
|
881
958
|
var Native = class extends Named {
|
|
882
959
|
/**
|
|
883
960
|
* @desc A named term with a known rewriting rule.
|
|
@@ -893,29 +970,14 @@ var require_expr = __commonJS({
|
|
|
893
970
|
*
|
|
894
971
|
* @param {String} name
|
|
895
972
|
* @param {Partial} impl
|
|
896
|
-
* @param {{note?: string, arity?: number, canonize?: boolean
|
|
973
|
+
* @param {{note?: string, arity?: number, canonize?: boolean }} [opt]
|
|
897
974
|
*/
|
|
898
975
|
constructor(name, impl, opt = {}) {
|
|
899
976
|
super(name);
|
|
900
977
|
this.invoke = impl;
|
|
901
|
-
|
|
902
|
-
this.arity = opt.arity || guess.arity || 1;
|
|
903
|
-
this.note = opt.note ?? guess.expr?.format({ terse: true, html: true, lambda: ["", " ↦ ", ""] });
|
|
904
|
-
}
|
|
905
|
-
_rski(options) {
|
|
906
|
-
if (this === native.I || this === native.K || this === native.S || options.steps >= options.max)
|
|
907
|
-
return this;
|
|
908
|
-
const canon = this.infer().expr;
|
|
909
|
-
if (!canon)
|
|
910
|
-
return this;
|
|
911
|
-
options.steps++;
|
|
912
|
-
return canon._rski(options);
|
|
978
|
+
this._setup({ canonize: true, ...opt });
|
|
913
979
|
}
|
|
914
980
|
};
|
|
915
|
-
var native = {};
|
|
916
|
-
function addNative(name, impl, opt) {
|
|
917
|
-
native[name] = new Native(name, impl, opt);
|
|
918
|
-
}
|
|
919
981
|
var Lambda = class _Lambda extends Expr {
|
|
920
982
|
/**
|
|
921
983
|
* @desc Lambda abstraction of arg over impl.
|
|
@@ -956,23 +1018,13 @@ var require_expr = __commonJS({
|
|
|
956
1018
|
weight() {
|
|
957
1019
|
return this.impl.weight() + 1;
|
|
958
1020
|
}
|
|
959
|
-
_infer(options, preArgs = [], steps = 0) {
|
|
960
|
-
if (preArgs.length > options.maxArgs)
|
|
961
|
-
return { normal: false, steps };
|
|
962
|
-
const push = nthvar(preArgs.length + options.index);
|
|
963
|
-
return this.invoke(push)._infer(options, [...preArgs, push], steps + 1);
|
|
964
|
-
}
|
|
965
1021
|
invoke(arg) {
|
|
966
1022
|
return this.impl.subst(this.arg, arg) ?? this.impl;
|
|
967
1023
|
}
|
|
968
|
-
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
const impl = this.impl.traverse(change);
|
|
973
|
-
if (!impl)
|
|
974
|
-
return null;
|
|
975
|
-
return new _Lambda(this.arg, impl);
|
|
1024
|
+
_traverse_descend(options, change) {
|
|
1025
|
+
const [impl, iAction] = unwrap(this.impl._traverse_redo(options, change));
|
|
1026
|
+
const final = impl ? new _Lambda(this.arg, impl) : null;
|
|
1027
|
+
return iAction === control.stop ? control.stop(final) : final;
|
|
976
1028
|
}
|
|
977
1029
|
any(predicate) {
|
|
978
1030
|
return predicate(this) || this.impl.any(predicate);
|
|
@@ -994,26 +1046,6 @@ var require_expr = __commonJS({
|
|
|
994
1046
|
const change = this.impl.subst(search, replace);
|
|
995
1047
|
return change ? new _Lambda(this.arg, change) : null;
|
|
996
1048
|
}
|
|
997
|
-
_rski(options) {
|
|
998
|
-
const impl = this.impl._rski(options);
|
|
999
|
-
if (options.steps >= options.max)
|
|
1000
|
-
return new _Lambda(this.arg, impl);
|
|
1001
|
-
options.steps++;
|
|
1002
|
-
if (impl === this.arg)
|
|
1003
|
-
return native.I;
|
|
1004
|
-
if (!impl.any((e) => e === this.arg))
|
|
1005
|
-
return native.K.apply(impl);
|
|
1006
|
-
if (impl instanceof App) {
|
|
1007
|
-
const { fun, arg } = impl;
|
|
1008
|
-
if (arg === this.arg && !fun.any((e) => e === this.arg))
|
|
1009
|
-
return fun._rski(options);
|
|
1010
|
-
return native.S.apply(
|
|
1011
|
-
new _Lambda(this.arg, fun)._rski(options),
|
|
1012
|
-
new _Lambda(this.arg, arg)._rski(options)
|
|
1013
|
-
);
|
|
1014
|
-
}
|
|
1015
|
-
throw new Error("Don't know how to convert to SKI" + this);
|
|
1016
|
-
}
|
|
1017
1049
|
diff(other, swap = false) {
|
|
1018
1050
|
if (!(other instanceof _Lambda))
|
|
1019
1051
|
return super.diff(other, swap);
|
|
@@ -1030,25 +1062,24 @@ var require_expr = __commonJS({
|
|
|
1030
1062
|
return true;
|
|
1031
1063
|
}
|
|
1032
1064
|
};
|
|
1033
|
-
var Church = class _Church extends
|
|
1065
|
+
var Church = class _Church extends Expr {
|
|
1034
1066
|
/**
|
|
1035
1067
|
* @desc Church numeral representing non-negative integer n:
|
|
1036
1068
|
* n f x = f(f(...(f x)...)) with f applied n times.
|
|
1037
1069
|
* @param {number} n
|
|
1038
1070
|
*/
|
|
1039
1071
|
constructor(n) {
|
|
1040
|
-
|
|
1041
|
-
if (!(
|
|
1072
|
+
n = Number.parseInt(n);
|
|
1073
|
+
if (!(n >= 0))
|
|
1042
1074
|
throw new Error("Church number must be a non-negative integer");
|
|
1043
|
-
|
|
1044
|
-
|
|
1075
|
+
super();
|
|
1076
|
+
this.invoke = (x) => (y) => {
|
|
1045
1077
|
let expr = y;
|
|
1046
|
-
for (let i =
|
|
1078
|
+
for (let i = n; i-- > 0; )
|
|
1047
1079
|
expr = x.apply(expr);
|
|
1048
1080
|
return expr;
|
|
1049
1081
|
};
|
|
1050
|
-
|
|
1051
|
-
this.n = p;
|
|
1082
|
+
this.n = n;
|
|
1052
1083
|
this.arity = 2;
|
|
1053
1084
|
}
|
|
1054
1085
|
diff(other, swap = false) {
|
|
@@ -1061,6 +1092,9 @@ var require_expr = __commonJS({
|
|
|
1061
1092
|
_unspaced(arg) {
|
|
1062
1093
|
return false;
|
|
1063
1094
|
}
|
|
1095
|
+
_format(options, nargs) {
|
|
1096
|
+
return nargs >= 2 ? options.redex[0] + this.n + options.redex[1] : this.n + "";
|
|
1097
|
+
}
|
|
1064
1098
|
};
|
|
1065
1099
|
function waitn(expr, n) {
|
|
1066
1100
|
return (arg) => n <= 1 ? expr.apply(arg) : waitn(expr.apply(arg), n - 1);
|
|
@@ -1087,14 +1121,9 @@ var require_expr = __commonJS({
|
|
|
1087
1121
|
if (!(impl instanceof Expr))
|
|
1088
1122
|
throw new Error("Attempt to create an alias for a non-expression: " + impl);
|
|
1089
1123
|
this.impl = impl;
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
this.arity = guess.proper && guess.arity || 0;
|
|
1094
|
-
this.proper = guess.proper ?? false;
|
|
1095
|
-
this.terminal = options.terminal ?? this.proper;
|
|
1096
|
-
this.canonical = guess.expr;
|
|
1097
|
-
this.invoke = waitn(impl, this.arity);
|
|
1124
|
+
this._setup(options);
|
|
1125
|
+
this.terminal = options.terminal ?? this.props?.proper;
|
|
1126
|
+
this.invoke = waitn(impl, this.arity ?? 0);
|
|
1098
1127
|
}
|
|
1099
1128
|
/**
|
|
1100
1129
|
* @property {boolean} [outdated] - whether the alias is outdated
|
|
@@ -1108,8 +1137,8 @@ var require_expr = __commonJS({
|
|
|
1108
1137
|
weight() {
|
|
1109
1138
|
return this.terminal ? 1 : this.impl.weight();
|
|
1110
1139
|
}
|
|
1111
|
-
|
|
1112
|
-
return
|
|
1140
|
+
_traverse_descend(options, change) {
|
|
1141
|
+
return this.impl._traverse_redo(options, change);
|
|
1113
1142
|
}
|
|
1114
1143
|
any(predicate) {
|
|
1115
1144
|
return predicate(this) || this.impl.any(predicate);
|
|
@@ -1130,9 +1159,6 @@ var require_expr = __commonJS({
|
|
|
1130
1159
|
return replace;
|
|
1131
1160
|
return this.impl.subst(search, replace);
|
|
1132
1161
|
}
|
|
1133
|
-
_infer(options, preArgs = [], steps = 0) {
|
|
1134
|
-
return this.impl._infer(options, preArgs, steps);
|
|
1135
|
-
}
|
|
1136
1162
|
// DO NOT REMOVE TYPE or tsc chokes with
|
|
1137
1163
|
// TS2527: The inferred type of 'Alias' references an inaccessible 'this' type.
|
|
1138
1164
|
/**
|
|
@@ -1148,9 +1174,6 @@ var require_expr = __commonJS({
|
|
|
1148
1174
|
return null;
|
|
1149
1175
|
return other.diff(this.impl, !swap);
|
|
1150
1176
|
}
|
|
1151
|
-
_rski(options) {
|
|
1152
|
-
return this.impl._rski(options);
|
|
1153
|
-
}
|
|
1154
1177
|
_braced(first) {
|
|
1155
1178
|
return this.outdated ? this.impl._braced(first) : false;
|
|
1156
1179
|
}
|
|
@@ -1159,6 +1182,9 @@ var require_expr = __commonJS({
|
|
|
1159
1182
|
return outdated ? this.impl._format(options, nargs) : super._format(options, nargs);
|
|
1160
1183
|
}
|
|
1161
1184
|
};
|
|
1185
|
+
function addNative(name, impl, opt) {
|
|
1186
|
+
native[name] = new Native(name, impl, opt);
|
|
1187
|
+
}
|
|
1162
1188
|
addNative("I", (x) => x);
|
|
1163
1189
|
addNative("K", (x) => (_) => x);
|
|
1164
1190
|
addNative("S", (x) => (y) => (z) => x.apply(z, y.apply(z)));
|
|
@@ -1172,6 +1198,11 @@ var require_expr = __commonJS({
|
|
|
1172
1198
|
note: "Increase a Church numeral argument by 1, otherwise n => f => x => f(n f x)"
|
|
1173
1199
|
}
|
|
1174
1200
|
);
|
|
1201
|
+
function firstVar(expr) {
|
|
1202
|
+
while (expr instanceof App)
|
|
1203
|
+
expr = expr.fun;
|
|
1204
|
+
return expr instanceof FreeVar;
|
|
1205
|
+
}
|
|
1175
1206
|
function maybeLambda(args, expr, caps = {}) {
|
|
1176
1207
|
const count = new Array(args.length).fill(0);
|
|
1177
1208
|
let proper = true;
|
|
@@ -1195,8 +1226,10 @@ var require_expr = __commonJS({
|
|
|
1195
1226
|
dup.add(i);
|
|
1196
1227
|
}
|
|
1197
1228
|
return {
|
|
1229
|
+
normal: true,
|
|
1230
|
+
steps: caps.steps,
|
|
1198
1231
|
expr: args.length ? new Lambda(args, expr) : expr,
|
|
1199
|
-
|
|
1232
|
+
arity: args.length,
|
|
1200
1233
|
...skip.size ? { skip } : {},
|
|
1201
1234
|
...dup.size ? { dup } : {},
|
|
1202
1235
|
duplicate: !!dup.size || caps.duplicate || false,
|
|
@@ -1207,43 +1240,6 @@ var require_expr = __commonJS({
|
|
|
1207
1240
|
function nthvar(n) {
|
|
1208
1241
|
return new FreeVar("abcdefgh"[n] ?? "x" + n);
|
|
1209
1242
|
}
|
|
1210
|
-
function* simplifyLambda(expr, options = {}, state = { steps: 0 }) {
|
|
1211
|
-
yield { expr, steps: state.steps, comment: "(self)" };
|
|
1212
|
-
if (expr.freeOnly())
|
|
1213
|
-
return;
|
|
1214
|
-
let maxWeight = expr.weight();
|
|
1215
|
-
if (expr instanceof Lambda) {
|
|
1216
|
-
for (const term of simplifyLambda(expr.impl, options, state)) {
|
|
1217
|
-
const candidate = new Lambda(expr.arg, term.expr);
|
|
1218
|
-
if (candidate.weight() < maxWeight) {
|
|
1219
|
-
maxWeight = candidate.weight();
|
|
1220
|
-
yield { expr: candidate, steps: state.steps, comment: "(lambda)" + term.comment };
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
if (expr instanceof App) {
|
|
1225
|
-
let { fun, arg } = expr;
|
|
1226
|
-
for (const term of simplifyLambda(fun, options, state)) {
|
|
1227
|
-
const candidate = term.expr.apply(arg);
|
|
1228
|
-
if (candidate.weight() < maxWeight) {
|
|
1229
|
-
maxWeight = candidate.weight();
|
|
1230
|
-
fun = term.expr;
|
|
1231
|
-
yield { expr: candidate, steps: state.steps, comment: "(fun)" + term.comment };
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
for (const term of simplifyLambda(arg, options, state)) {
|
|
1235
|
-
const candidate = fun.apply(term.expr);
|
|
1236
|
-
if (candidate.weight() < maxWeight) {
|
|
1237
|
-
maxWeight = candidate.weight();
|
|
1238
|
-
yield { expr: candidate, steps: state.steps, comment: "(arg)" + term.comment };
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
const canon = expr.infer({ max: options.max, maxArgs: options.maxArgs });
|
|
1243
|
-
state.steps += canon.steps;
|
|
1244
|
-
if (canon.expr && canon.expr.weight() < maxWeight)
|
|
1245
|
-
yield { expr: canon.expr, steps: state.steps, comment: "(canonical)" };
|
|
1246
|
-
}
|
|
1247
1243
|
function toposort(list, env) {
|
|
1248
1244
|
if (list instanceof Expr)
|
|
1249
1245
|
list = [list];
|
|
@@ -1347,7 +1343,7 @@ var require_parser = __commonJS({
|
|
|
1347
1343
|
"->",
|
|
1348
1344
|
"\\+"
|
|
1349
1345
|
);
|
|
1350
|
-
var SKI2 = class
|
|
1346
|
+
var SKI2 = class {
|
|
1351
1347
|
/**
|
|
1352
1348
|
*
|
|
1353
1349
|
* @param {{
|
|
@@ -1393,18 +1389,18 @@ var require_parser = __commonJS({
|
|
|
1393
1389
|
*
|
|
1394
1390
|
* @param {Alias|String} term
|
|
1395
1391
|
* @param {String|Expr|function(Expr):Partial} [impl]
|
|
1396
|
-
* @param {
|
|
1392
|
+
* @param {object|string} [options]
|
|
1393
|
+
* @param {string} [options.note] - optional annotation for the term, default is auto-generated if this.annotate is true
|
|
1394
|
+
* @param {boolean} [options.canonize] - whether to canonize the term's implementation, default is this.annotate
|
|
1395
|
+
* @param {boolean} [options.fancy] - alternative HTML-friendly name for the term
|
|
1396
|
+
* @param {number} [options.arity] - custom arity for the term, default is inferred from the implementation
|
|
1397
1397
|
* @return {SKI} chainable
|
|
1398
1398
|
*/
|
|
1399
|
-
add(term, impl,
|
|
1399
|
+
add(term, impl, options) {
|
|
1400
1400
|
term = this._named(term, impl);
|
|
1401
|
-
if (
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
note = guess.expr.format({ terse: true, html: true, lambda: ["", " ↦ ", ""] });
|
|
1405
|
-
}
|
|
1406
|
-
if (note !== void 0)
|
|
1407
|
-
term.note = note;
|
|
1401
|
+
if (typeof options === "string")
|
|
1402
|
+
options = { note: options, canonize: false };
|
|
1403
|
+
term._setup({ canonize: this.annotate, ...options });
|
|
1408
1404
|
if (this.known[term.name])
|
|
1409
1405
|
this.known[term.name].outdated = true;
|
|
1410
1406
|
this.known[term.name] = term;
|
|
@@ -1596,7 +1592,7 @@ var require_parser = __commonJS({
|
|
|
1596
1592
|
expr.outdated = true;
|
|
1597
1593
|
const def = item.match(/^([A-Z]|[a-z][a-z_0-9]*)\s*=(.*)$/s);
|
|
1598
1594
|
if (def && def[2] === "")
|
|
1599
|
-
expr = new FreeVar(def[1], options.scope ??
|
|
1595
|
+
expr = new FreeVar(def[1], options.scope ?? FreeVar.global);
|
|
1600
1596
|
else
|
|
1601
1597
|
expr = this.parseLine(item, jar, options);
|
|
1602
1598
|
if (def) {
|
|
@@ -1641,7 +1637,7 @@ var require_parser = __commonJS({
|
|
|
1641
1637
|
const tokens = combChars.split(source);
|
|
1642
1638
|
const empty = new Empty();
|
|
1643
1639
|
const stack = [empty];
|
|
1644
|
-
const context = options.scope ||
|
|
1640
|
+
const context = options.scope || FreeVar.global;
|
|
1645
1641
|
for (const c of tokens) {
|
|
1646
1642
|
if (c === "(")
|
|
1647
1643
|
stack.push(empty);
|
|
@@ -1859,6 +1855,62 @@ var require_quest = __commonJS({
|
|
|
1859
1855
|
return { pass: false, details: [], exception: e, steps: 0, input };
|
|
1860
1856
|
}
|
|
1861
1857
|
}
|
|
1858
|
+
verify(options) {
|
|
1859
|
+
const findings = this.verifyMeta(options);
|
|
1860
|
+
if (options.solutions) {
|
|
1861
|
+
const solCheck = this.verifySolutions(options.solutions);
|
|
1862
|
+
if (solCheck)
|
|
1863
|
+
findings.solutions = solCheck;
|
|
1864
|
+
}
|
|
1865
|
+
if (options.seen) {
|
|
1866
|
+
if (!this.id)
|
|
1867
|
+
findings.seen = "No id in quest " + (this.name ?? "(unnamed)");
|
|
1868
|
+
if (options.seen.has(this.id))
|
|
1869
|
+
findings.seen = "Duplicate quest id " + this.id;
|
|
1870
|
+
options.seen.add(this.id);
|
|
1871
|
+
}
|
|
1872
|
+
return Object.keys(findings).length ? findings : null;
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* @desc Verify that solutions that are expected to pass/fail do so.
|
|
1876
|
+
* @param {SelfCheck|{[key: string]: SelfCheck}} dataset
|
|
1877
|
+
* @return {{shouldPass: {input: string[], result: QuestResult}[], shouldFail: {input: string[], result: QuestResult}[]} | null}
|
|
1878
|
+
*/
|
|
1879
|
+
verifySolutions(dataset) {
|
|
1880
|
+
if (typeof dataset === "object" && !Array.isArray(dataset?.accepted) && !Array.isArray(dataset?.rejected)) {
|
|
1881
|
+
if (!this.id || !dataset[this.id])
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
const { accepted = [], rejected = [] } = dataset[this.id] ?? dataset;
|
|
1885
|
+
const ret = { shouldPass: [], shouldFail: [] };
|
|
1886
|
+
for (const input of accepted) {
|
|
1887
|
+
const result = this.check(...input);
|
|
1888
|
+
if (!result.pass)
|
|
1889
|
+
ret.shouldPass.push({ input, result });
|
|
1890
|
+
}
|
|
1891
|
+
for (const input of rejected) {
|
|
1892
|
+
const result = this.check(...input);
|
|
1893
|
+
if (result.pass)
|
|
1894
|
+
ret.shouldFail.push({ input, result });
|
|
1895
|
+
}
|
|
1896
|
+
return ret.shouldFail.length + ret.shouldPass.length ? ret : null;
|
|
1897
|
+
}
|
|
1898
|
+
verifyMeta(options = {}) {
|
|
1899
|
+
const findings = {};
|
|
1900
|
+
for (const field of ["name", "intro"]) {
|
|
1901
|
+
const found = checkHtml(this[field]);
|
|
1902
|
+
if (found)
|
|
1903
|
+
findings[field] = found;
|
|
1904
|
+
}
|
|
1905
|
+
if (options.date) {
|
|
1906
|
+
const date = new Date(this.meta.created_at);
|
|
1907
|
+
if (isNaN(date))
|
|
1908
|
+
findings.date = "invalid date format: " + this.meta.created_at;
|
|
1909
|
+
else if (date < /* @__PURE__ */ new Date("2024-07-15") || date > /* @__PURE__ */ new Date())
|
|
1910
|
+
findings.date = "date out of range: " + this.meta.created_at;
|
|
1911
|
+
}
|
|
1912
|
+
return findings;
|
|
1913
|
+
}
|
|
1862
1914
|
/**
|
|
1863
1915
|
*
|
|
1864
1916
|
* @return {TestCase[]}
|
|
@@ -2009,11 +2061,67 @@ var require_quest = __commonJS({
|
|
|
2009
2061
|
return expr;
|
|
2010
2062
|
}
|
|
2011
2063
|
};
|
|
2064
|
+
var Group = class {
|
|
2065
|
+
constructor(options) {
|
|
2066
|
+
this.name = options.name;
|
|
2067
|
+
this.intro = list2str(options.intro);
|
|
2068
|
+
this.id = options.id;
|
|
2069
|
+
if (options.content)
|
|
2070
|
+
this.content = options.content.map((c) => c instanceof Quest2 ? c : new Quest2(c));
|
|
2071
|
+
}
|
|
2072
|
+
verify(options) {
|
|
2073
|
+
const findings = {};
|
|
2074
|
+
const id = checkId(this.id, options.seen);
|
|
2075
|
+
if (id)
|
|
2076
|
+
findings[this.id] = id;
|
|
2077
|
+
for (const field of ["name", "intro"]) {
|
|
2078
|
+
const found = checkHtml(this[field]);
|
|
2079
|
+
if (found)
|
|
2080
|
+
findings[field] = found;
|
|
2081
|
+
}
|
|
2082
|
+
findings.content = this.content.map((q) => q.verify(options));
|
|
2083
|
+
return findings;
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2012
2086
|
function list2str(str) {
|
|
2013
2087
|
if (str === void 0 || typeof str === "string")
|
|
2014
2088
|
return str;
|
|
2015
2089
|
return Array.isArray(str) ? str.join(" ") : "" + str;
|
|
2016
2090
|
}
|
|
2091
|
+
function checkId(id, seen) {
|
|
2092
|
+
if (id === void 0)
|
|
2093
|
+
return "missing";
|
|
2094
|
+
if (typeof id !== "string" && typeof id !== "number")
|
|
2095
|
+
return "is a " + typeof id;
|
|
2096
|
+
if (seen) {
|
|
2097
|
+
if (seen.has(id))
|
|
2098
|
+
return "duplicate id " + id;
|
|
2099
|
+
seen.add(id);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
function checkHtml(str) {
|
|
2103
|
+
if (str === void 0)
|
|
2104
|
+
return "missing";
|
|
2105
|
+
if (typeof str !== "string")
|
|
2106
|
+
return "not a string but " + typeof str;
|
|
2107
|
+
const tagStack = [];
|
|
2108
|
+
const tagRegex = /<\/?([a-z]+)(?:\s[^>]*)?>/gi;
|
|
2109
|
+
let match;
|
|
2110
|
+
while ((match = tagRegex.exec(str)) !== null) {
|
|
2111
|
+
const [fullTag, tagName] = match;
|
|
2112
|
+
if (fullTag.startsWith("</")) {
|
|
2113
|
+
if (tagStack.length === 0 || tagStack.pop() !== tagName)
|
|
2114
|
+
return `Unmatched closing tag: </${tagName}>`;
|
|
2115
|
+
} else {
|
|
2116
|
+
tagStack.push(tagName);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
if (tagStack.length > 0)
|
|
2120
|
+
return `Unclosed tags: ${tagStack.join(", ")}`;
|
|
2121
|
+
return null;
|
|
2122
|
+
}
|
|
2123
|
+
Quest2.Group = Group;
|
|
2124
|
+
Quest2.Case = Case;
|
|
2017
2125
|
module2.exports = { Quest: Quest2 };
|
|
2018
2126
|
}
|
|
2019
2127
|
});
|
|
@@ -2023,6 +2131,7 @@ var require_extras = __commonJS({
|
|
|
2023
2131
|
"src/extras.js"(exports2, module2) {
|
|
2024
2132
|
"use strict";
|
|
2025
2133
|
var { Expr, Alias, FreeVar } = require_expr();
|
|
2134
|
+
var { Quest: Quest2 } = require_quest();
|
|
2026
2135
|
function search(seed, options, predicate) {
|
|
2027
2136
|
const {
|
|
2028
2137
|
depth = 16,
|
|
@@ -2088,6 +2197,10 @@ var require_extras = __commonJS({
|
|
|
2088
2197
|
function deepFormat(obj, options = {}) {
|
|
2089
2198
|
if (obj instanceof Expr)
|
|
2090
2199
|
return obj.format(options);
|
|
2200
|
+
if (obj instanceof Quest2)
|
|
2201
|
+
return "Quest(" + obj.name + ")";
|
|
2202
|
+
if (obj instanceof Quest2.Case)
|
|
2203
|
+
return "Quest.Case";
|
|
2091
2204
|
if (Array.isArray(obj))
|
|
2092
2205
|
return obj.map(deepFormat);
|
|
2093
2206
|
if (typeof obj !== "object" || obj === null || obj.constructor !== Object)
|