@dallaylaen/ski-interpreter 2.2.0 → 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.
@@ -1,12 +1,77 @@
1
- export type ActionWrapper<T> = T | {
2
- value: T | null;
3
- action: string;
4
- } | null;
1
+ export type TraverseValue<T_1> = T_1 | TraverseControl<T_1> | null;
5
2
  export type Partial = Expr | ((arg0: Expr) => Partial);
3
+ /**
4
+ * : boolean, // whether the irreducible form is only contains its arguments. implies normal.
5
+ * arity?: number, // the number of arguments that is sufficient to reach the normal form
6
+ * // absent unless normal.
7
+ * discard?: boolean, // whether the term (or subterms, unless proper) can discard arguments.
8
+ * duplicate?: boolean, // whether the term (or subterms, unless proper) can duplicate arguments.
9
+ * skip?: Set<number>, // indices of arguments that are discarded. nonempty inplies discard.
10
+ * dup?: Set<number>, // indices of arguments that are duplicated. nonempty implies duplicate.
11
+ * expr?: Expr, // canonical form containing lambdas, applications, and variables, if any
12
+ * steps?: number, // number of steps taken to obtain the aforementioned information, if applicable
13
+ * }} TermInfo
14
+ */
15
+ export type proper = {
16
+ normal: boolean;
17
+ };
6
18
  /**
7
19
  * @typedef {Expr | function(Expr): Partial} Partial
8
20
  */
21
+ /**
22
+ * @typedef {{
23
+ * normal: boolean, // whether the term becomes irreducible after receiving a number of arguments.
24
+ * // if false, other properties may be missing.
25
+ * proper: boolean, // whether the irreducible form is only contains its arguments. implies normal.
26
+ * arity?: number, // the number of arguments that is sufficient to reach the normal form
27
+ * // absent unless normal.
28
+ * discard?: boolean, // whether the term (or subterms, unless proper) can discard arguments.
29
+ * duplicate?: boolean, // whether the term (or subterms, unless proper) can duplicate arguments.
30
+ * skip?: Set<number>, // indices of arguments that are discarded. nonempty inplies discard.
31
+ * dup?: Set<number>, // indices of arguments that are duplicated. nonempty implies duplicate.
32
+ * expr?: Expr, // canonical form containing lambdas, applications, and variables, if any
33
+ * steps?: number, // number of steps taken to obtain the aforementioned information, if applicable
34
+ * }} TermInfo
35
+ */
9
36
  export class Expr {
37
+ /**
38
+ * @descr A combinatory logic expression.
39
+ *
40
+ * Applications, variables, and other terms like combinators per se
41
+ * are subclasses of this class.
42
+ *
43
+ * @abstract
44
+ * @property {{
45
+ * scope?: any,
46
+ * env?: { [key: string]: Expr },
47
+ * src?: string,
48
+ * parser: object,
49
+ * }} [context]
50
+ * @property {number} [arity] - number of arguments the term is waiting for (if known)
51
+ * @property {string} [note] - a brief description what the term does
52
+ * @property {string} [fancyName] - how to display in html mode, e.g. &phi; instead of 'f'
53
+ * Typically only applicable to descendants of Named.
54
+ * @property {TermInfo} [props] - properties inferred from the term's behavior
55
+ */
56
+ /**
57
+ *
58
+ * @desc Define properties of the term based on user supplied options and/or inference results.
59
+ * Typically useful for declaring Native and Alias terms.
60
+ * @private
61
+ * @param {Object} options
62
+ * @param {string} [options.note] - a brief description what the term does
63
+ * @param {number} [options.arity] - number of arguments the term is waiting for (if known)
64
+ * @param {string} [options.fancy] - how to display in html mode, e.g. &phi; instead of 'f'
65
+ * @param {boolean} [options.canonize] - whether to try to infer the properties
66
+ * @param {number} [options.max] - maximum number of steps for inference, if canonize is true
67
+ * @param {number} [options.maxArgs] - maximum number of arguments for inference, if canonize is true
68
+ * @return {this}
69
+ */
70
+ private _setup;
71
+ fancyName: any;
72
+ note: any;
73
+ arity: any;
74
+ props: TermInfo;
10
75
  /**
11
76
  * @desc apply self to zero or more terms and return the resulting term,
12
77
  * without performing any calculations whatsoever
@@ -19,19 +84,54 @@ export class Expr {
19
84
  * @return {Expr}
20
85
  */
21
86
  expand(): Expr;
87
+ /**
88
+ * @desc Returns true if the expression contains only free variables and applications, false otherwise.
89
+ * @returns {boolean}
90
+ */
22
91
  freeOnly(): boolean;
23
92
  /**
24
93
  * @desc Traverse the expression tree, applying change() to each node.
25
94
  * If change() returns an Expr, the node is replaced with that value.
26
- * Otherwise, the node is left descended further (if applicable)
27
- * or left unchanged.
95
+ * A null/undefined value is interpreted as
96
+ * "descend further if applicable, or leave the node unchanged".
97
+ *
98
+ * Returned values may be decorated:
99
+ *
100
+ * SKI.control.prune will suppress further descending even if nothing was returned
101
+ * SKI.control.stop will terminate further changes.
102
+ * SKI.control.redo will apply the callback to the returned subtree, recursively.
103
+ *
104
+ * Note that if redo was applied at least once to a subtree, a null return from the same subtree
105
+ * will be replaced by the last non-null value returned.
106
+ *
107
+ * The traversal order is leftmost-outermost, unless options.order = 'leftmost-innermost' is specified.
108
+ * Short aliases 'LO' and 'LI' (case-sensitive) are also accepted.
28
109
  *
29
110
  * Returns null if no changes were made, or the new expression otherwise.
30
111
  *
31
- * @param {(e:Expr) => (Expr|null)} change
112
+ * @param {{
113
+ * order?: 'LO' | 'LI' | 'leftmost-outermost' | 'leftmost-innermost',
114
+ * }} [options]
115
+ * @param {(e:Expr) => TraverseValue<Expr>} change
32
116
  * @returns {Expr|null}
33
117
  */
34
- traverse(change: (e: Expr) => (Expr | null)): Expr | null;
118
+ traverse(options?: {
119
+ order?: "LO" | "LI" | "leftmost-outermost" | "leftmost-innermost";
120
+ }, change: (e: Expr) => TraverseValue<Expr>): Expr | null;
121
+ /**
122
+ * @private
123
+ * @param {Object} options
124
+ * @param {(e:Expr) => TraverseValue<Expr>} change
125
+ * @returns {TraverseValue<Expr>}
126
+ */
127
+ private _traverse_redo;
128
+ /**
129
+ * @private
130
+ * @param {Object} options
131
+ * @param {(e:Expr) => TraverseValue<Expr>} change
132
+ * @returns {TraverseValue<Expr>}
133
+ */
134
+ private _traverse_descend;
35
135
  /**
36
136
  * @desc Returns true if predicate() is true for any subterm of the expression, false otherwise.
37
137
  *
@@ -55,13 +155,20 @@ export class Expr {
55
155
  * @experimental
56
156
  * @template T
57
157
  * @param {T} initial
58
- * @param {(acc: T, expr: Expr) => ActionWrapper<T>} combine
158
+ * @param {(acc: T, expr: Expr) => TraverseValue<T>} combine
59
159
  * @returns {T}
60
160
  */
61
- fold<T>(initial: T, combine: (acc: T, expr: Expr) => ActionWrapper<T>): T;
62
- _fold(initial: any, combine: any): any;
161
+ fold<T_1>(initial: T_1, combine: (acc: T_1, expr: Expr) => TraverseValue<T_1>): T_1;
63
162
  /**
64
- * @desc rough estimate of the complexity of the term
163
+ * @template T
164
+ * @param {T} initial
165
+ * @param {(acc: T, expr: Expr) => TraverseValue<T>} combine
166
+ * @returns {TraverseValue<T>}
167
+ * @private
168
+ */
169
+ private _fold;
170
+ /**
171
+ * @desc rough estimate of the term's complexity
65
172
  * @return {number}
66
173
  */
67
174
  weight(): number;
@@ -78,38 +185,17 @@ export class Expr {
78
185
  * Use toLambda() if you want to get a lambda term in any case.
79
186
  *
80
187
  * @param {{max?: number, maxArgs?: number}} options
81
- * @return {{
82
- * normal: boolean,
83
- * steps: number,
84
- * expr?: Expr,
85
- * arity?: number,
86
- * proper?: boolean,
87
- * discard?: boolean,
88
- * duplicate?: boolean,
89
- * skip?: Set<number>,
90
- * dup?: Set<number>,
91
- * }}
188
+ * @return {TermInfo}
92
189
  */
93
190
  infer(options?: {
94
191
  max?: number;
95
192
  maxArgs?: number;
96
- }): {
97
- normal: boolean;
98
- steps: number;
99
- expr?: Expr;
100
- arity?: number;
101
- proper?: boolean;
102
- discard?: boolean;
103
- duplicate?: boolean;
104
- skip?: Set<number>;
105
- dup?: Set<number>;
106
- };
193
+ }): TermInfo;
107
194
  /**
108
- *
109
- * @param {{max: number, maxArgs: number, index: number}} options
110
- * @param {FreeVar[]} preArgs
111
- * @param {number} steps
112
- * @returns {{normal: boolean, steps: number}|{normal: boolean, steps: number}|{normal: boolean, steps: number, expr: Lambda|*, arity?: *, skip?: Set<any>, dup?: Set<any>, duplicate, discard, proper: boolean}|*|{normal: boolean, steps: number}}
195
+ * @desc Internal method for infer(), which performs the actual inference.
196
+ * @param {{max: number, maxArgs: number}} options
197
+ * @param {number} nargs - var index to avoid name clashes
198
+ * @returns {TermInfo}
113
199
  * @private
114
200
  */
115
201
  private _infer;
@@ -144,7 +230,7 @@ export class Expr {
144
230
  * latin?: number,
145
231
  * }} options
146
232
  * @param {number} [maxWeight] - maximum allowed weight of terms in the sequence
147
- * @return {IterableIterator<{expr: Expr, steps: number?, comment: string?}>}
233
+ * @return {IterableIterator<{expr: Expr, steps?: number, comment?: string}>}
148
234
  */
149
235
  toLambda(options?: {
150
236
  max?: number;
@@ -155,8 +241,8 @@ export class Expr {
155
241
  latin?: number;
156
242
  }): IterableIterator<{
157
243
  expr: Expr;
158
- steps: number | null;
159
- comment: string | null;
244
+ steps?: number;
245
+ comment?: string;
160
246
  }>;
161
247
  /**
162
248
  * @desc Rewrite the expression into S, K, and I combinators step by step.
@@ -175,13 +261,6 @@ export class Expr {
175
261
  expr: Expr;
176
262
  steps: number;
177
263
  }>;
178
- /**
179
- * @desc Internal method for toSKI, which performs one step of the conversion.
180
- * @param {{max: number, steps: number}} options
181
- * @returns {Expr}
182
- * @private
183
- */
184
- private _rski;
185
264
  /**
186
265
  * Replace all instances of plug in the expression with value and return the resulting expression,
187
266
  * or null if no changes could be made.
@@ -227,14 +306,14 @@ export class Expr {
227
306
  * @desc Run uninterrupted sequence of step() applications
228
307
  * until the expression is irreducible, or max number of steps is reached.
229
308
  * Default number of steps = 1000.
230
- * @param {{max: number?, steps: number?, throw: boolean?}|Expr} [opt]
309
+ * @param {{max?: number, steps?: number, throw?: boolean}|Expr} [opt]
231
310
  * @param {Expr} args
232
311
  * @return {{expr: Expr, steps: number, final: boolean}}
233
312
  */
234
313
  run(opt?: {
235
- max: number | null;
236
- steps: number | null;
237
- throw: boolean | null;
314
+ max?: number;
315
+ steps?: number;
316
+ throw?: boolean;
238
317
  } | Expr, ...args: Expr): {
239
318
  expr: Expr;
240
319
  steps: number;
@@ -243,11 +322,11 @@ export class Expr {
243
322
  /**
244
323
  * Execute step() while possible, yielding a brief description of events after each step.
245
324
  * Mnemonics: like run() but slower.
246
- * @param {{max: number?}} options
325
+ * @param {{max?: number}} options
247
326
  * @return {IterableIterator<{final: boolean, expr: Expr, steps: number}>}
248
327
  */
249
328
  walk(options?: {
250
- max: number | null;
329
+ max?: number;
251
330
  }): IterableIterator<{
252
331
  final: boolean;
253
332
  expr: Expr;
@@ -289,10 +368,14 @@ export class Expr {
289
368
  diff(other: Expr, swap?: boolean): string | null;
290
369
  /**
291
370
  * @desc Assert expression equality. Can be used in tests.
292
- * @param {Expr} expected
371
+ *
372
+ * `this` is the expected value and the argument is the actual one.
373
+ * Mnemonic: the expected value is always a combinator, the actual one may be anything.
374
+ *
375
+ * @param {Expr} actual
293
376
  * @param {string} comment
294
377
  */
295
- expect(expected: Expr, comment?: string): void;
378
+ expect(actual: Expr, comment?: string): void;
296
379
  /**
297
380
  * @desc Returns string representation of the expression.
298
381
  * Same as format() without options.
@@ -365,6 +448,28 @@ export class Expr {
365
448
  * @private
366
449
  */
367
450
  private _format;
451
+ /**
452
+ * @desc Returns a string representation of the expression tree, with indentation to show structure.
453
+ *
454
+ * Applications are flattened to avoid excessive nesting.
455
+ * Variables include ids to distinguish different instances of the same variable name.
456
+ *
457
+ * May be useful for debugging.
458
+ *
459
+ * @returns {string}
460
+ *
461
+ * @example
462
+ * > console.log(ski.parse('C 5 x (x->x x)').diag())
463
+ * App:
464
+ * Native: C
465
+ * Church: 5
466
+ * FreeVar: x[53]
467
+ * Lambda (x[54]):
468
+ * App:
469
+ * FreeVar: x[54]
470
+ * FreeVar: x[54]
471
+ */
472
+ diag(): string;
368
473
  /**
369
474
  * @desc Convert the expression to a JSON-serializable format.
370
475
  * @returns {string}
@@ -388,11 +493,9 @@ export class App extends Expr {
388
493
  constructor(fun: Expr, arg: Expr);
389
494
  arg: Expr;
390
495
  fun: Expr;
391
- final: boolean;
392
- arity: number;
393
- _infer(options: any, preArgs?: any[], steps?: number): any;
394
- traverse(change: any): Expr;
496
+ _traverse_descend(options: any, change: any): any;
395
497
  any(predicate: any): any;
498
+ _fold(initial: any, combine: any): any;
396
499
  subst(search: any, replace: any): Expr;
397
500
  /**
398
501
  * @return {{expr: Expr, steps: number}}
@@ -402,11 +505,7 @@ export class App extends Expr {
402
505
  steps: number;
403
506
  };
404
507
  invoke(arg: any): Partial;
405
- /**
406
- * @desc Convert the expression to SKI combinatory logic
407
- * @return {Expr}
408
- */
409
- _rski(options: any): Expr;
508
+ final: boolean;
410
509
  diff(other: any, swap?: boolean): string;
411
510
  _braced(first: any): boolean;
412
511
  _format(options: any, nargs: any): string;
@@ -436,6 +535,8 @@ export class FreeVar extends Named {
436
535
  * If a scope object is given, however, two variables with the same name and scope
437
536
  * are considered identical.
438
537
  *
538
+ * By convention, FreeVar.global is a constant denoting a global unbound variable.
539
+ *
439
540
  * @param {string} name - name of the variable
440
541
  * @param {any} scope - an object representing where the variable belongs to.
441
542
  */
@@ -445,6 +546,9 @@ export class FreeVar extends Named {
445
546
  diff(other: any, swap?: boolean): string;
446
547
  subst(search: any, replace: any): any;
447
548
  }
549
+ export namespace FreeVar {
550
+ let global: string[];
551
+ }
448
552
  export class Lambda extends Expr {
449
553
  /**
450
554
  * @desc Lambda abstraction of arg over impl.
@@ -465,12 +569,11 @@ export class Lambda extends Expr {
465
569
  arg: FreeVar;
466
570
  impl: Expr;
467
571
  arity: number;
468
- _infer(options: any, preArgs?: any[], steps?: number): any;
469
572
  invoke(arg: any): Expr;
470
- traverse(change: any): Expr | Lambda;
573
+ _traverse_descend(options: any, change: any): any;
471
574
  any(predicate: any): any;
575
+ _fold(initial: any, combine: any): any;
472
576
  subst(search: any, replace: any): Lambda;
473
- _rski(options: any): any;
474
577
  diff(other: any, swap?: boolean): string;
475
578
  _format(options: any, nargs: any): string;
476
579
  _braced(first: any): boolean;
@@ -490,18 +593,14 @@ export class Native extends Named {
490
593
  *
491
594
  * @param {String} name
492
595
  * @param {Partial} impl
493
- * @param {{note?: string, arity?: number, canonize?: boolean, apply?: function(Expr):(Expr|null) }} [opt]
596
+ * @param {{note?: string, arity?: number, canonize?: boolean }} [opt]
494
597
  */
495
598
  constructor(name: string, impl: Partial, opt?: {
496
599
  note?: string;
497
600
  arity?: number;
498
601
  canonize?: boolean;
499
- apply?: (arg0: Expr) => (Expr | null);
500
602
  });
501
603
  invoke: Partial;
502
- arity: any;
503
- note: any;
504
- _rski(options: any): Expr | this;
505
604
  }
506
605
  export class Alias extends Named {
507
606
  /**
@@ -518,60 +617,60 @@ export class Alias extends Named {
518
617
  *
519
618
  * @param {String} name
520
619
  * @param {Expr} impl
521
- * @param {{canonize: boolean?, max: number?, maxArgs: number?, note: string?, terminal: boolean?}} [options]
620
+ * @param {{canonize?: boolean, max?: number, maxArgs?: number, note?: string, terminal?: boolean}} [options]
522
621
  */
523
622
  constructor(name: string, impl: Expr, options?: {
524
- canonize: boolean | null;
525
- max: number | null;
526
- maxArgs: number | null;
527
- note: string | null;
528
- terminal: boolean | null;
623
+ canonize?: boolean;
624
+ max?: number;
625
+ maxArgs?: number;
626
+ note?: string;
627
+ terminal?: boolean;
529
628
  });
530
629
  impl: Expr;
531
- note: string;
532
- arity: any;
533
- proper: any;
534
630
  terminal: any;
535
- canonical: any;
536
631
  invoke: (arg: any) => any;
537
- traverse(change: any): any;
632
+ _traverse_descend(options: any, change: any): any;
538
633
  any(predicate: any): any;
634
+ _fold(initial: any, combine: any): any;
539
635
  subst(search: any, replace: any): any;
540
- _infer(options: any, preArgs?: any[], steps?: number): any;
541
- /**
542
- *
543
- * @return {{expr: Expr, steps: number}}
544
- */
545
- step(): {
546
- expr: Expr;
547
- steps: number;
548
- };
549
636
  diff(other: any, swap?: boolean): any;
550
- _rski(options: any): Expr;
551
637
  _braced(first: any): boolean;
552
638
  }
553
- export class Church extends Native {
639
+ export class Church extends Expr {
554
640
  /**
555
641
  * @desc Church numeral representing non-negative integer n:
556
642
  * n f x = f(f(...(f x)...)) with f applied n times.
557
643
  * @param {number} n
558
644
  */
559
645
  constructor(n: number);
560
- n: any;
646
+ invoke: (x: any) => (y: any) => any;
647
+ /** @type {number} */
648
+ n: number;
561
649
  arity: number;
562
650
  diff(other: any, swap?: boolean): string;
651
+ _unspaced(arg: any): boolean;
652
+ _format(options: any, nargs: any): any;
563
653
  }
564
654
  /**
655
+ * @desc List of predefined native combinators.
656
+ * This is required for toSKI() to work, otherwise could as well have been in parser.js.
565
657
  * @type {{[key: string]: Native}}
566
658
  */
567
659
  declare const native: {
568
660
  [key: string]: Native;
569
661
  };
570
- declare namespace control {
571
- let descend: (arg0: any) => any;
572
- let prune: (arg0: any) => any;
573
- let stop: (arg0: any) => any;
574
- }
662
+ /**
663
+ * @template T
664
+ * @typedef {T | TraverseControl<T> | null} TraverseValue
665
+ */
666
+ /**
667
+ * @desc Control primitives for fold() and traverse() methods.
668
+ * @template T
669
+ * @type {{[name: string]: function(T): TraverseControl<T>}}
670
+ */
671
+ declare const control: {
672
+ [name: string]: (arg0: T) => TraverseControl<T>;
673
+ };
575
674
  /**
576
675
  * @desc Sort a list in such a way that dependent terms come after the (named) terms they depend on.
577
676
  * If env is given, only terms listed there are taken into account.
@@ -54,5 +54,21 @@ export function search(seed: Expr[], options: {
54
54
  * @returns {any}
55
55
  */
56
56
  export function deepFormat(obj: any, options?: object): any;
57
- export function declare(expr: any, env: any): string;
57
+ /**
58
+ * @desc Given an expression and a hash of named terms,
59
+ * return a semicolon-separated string that declares said expression
60
+ * unambiguously.
61
+ *
62
+ * @example
63
+ * var expr = ski.parse("T=CI; V=BCT; V x y");
64
+ * SKI.extras.declare(expr, expr.context.env);
65
+ * // 'B; C; I; T=CI; V=BC(T); x=; y=; Vx y'
66
+ *
67
+ * @param {Expr} expr
68
+ * @param {{[s: string]: Named}} [env]
69
+ * @returns {string}
70
+ */
71
+ export function declare(expr: Expr, env?: {
72
+ [s: string]: Named;
73
+ }): string;
58
74
  import { Expr } from "./expr";
@@ -27,26 +27,39 @@ export function restrict(set: Set<string>, spec?: string): Set<string>;
27
27
  /**
28
28
  * @private
29
29
  * @template T
30
- * @param {T|ActionWrapper<T>} value
31
- * @returns {[T?, string|undefined]}
30
+ * @param {T|TraverseControl<T>|null} value
31
+ * @returns {[T?, function|undefined]}
32
32
  */
33
- export function unwrap<T>(value: T | ActionWrapper<T>): [T?, string | undefined];
33
+ export function unwrap<T>(value: T | TraverseControl<T> | null): [T?, Function | undefined];
34
34
  /**
35
+ * @desc Prepare a self-referencing wrapper function for use as a fold/traverse control decorator.
36
+ *
37
+ * If `fun` is created by `prepareWrapper`, then
38
+ * unwrap(fun(x)) will always return exactly [x, fun], and the second value can be checked with ===.
39
+ *
40
+ * An optional label can be provided for debugging purposes.
35
41
  *
36
42
  * @private
37
43
  * @template T
38
- * @param {string} action
39
- * @returns {function(T): ActionWrapper<T>}
44
+ * @param {string} [label]
45
+ * @returns {function(T): TraverseControl<T>}
40
46
  */
41
- export function prepareWrapper<T>(action: string): (arg0: T) => ActionWrapper<T>;
42
- declare class ActionWrapper {
47
+ export function prepareWrapper<T>(label?: string): (arg0: T) => TraverseControl<T>;
48
+ declare class TraverseControl {
43
49
  /**
50
+ * @desc A wrapper for values returned by fold/traverse callbacks
51
+ * which instructs the traversal to alter its behavior while
52
+ * retaining the value in question.
53
+ *
54
+ * This class is instantiated internally be `SKI.control.*` functions,
55
+ * and is not intended to be used directly by client code.
56
+ *
44
57
  * @template T
45
58
  * @param {T} value
46
- * @param {string} action
59
+ * @param {function(T): TraverseControl<T>} decoration
47
60
  */
48
- constructor(value: T, action: string);
61
+ constructor(value: T, decoration: (arg0: T) => TraverseControl<T>);
49
62
  value: T;
50
- action: string;
63
+ decoration: (arg0: T) => TraverseControl<T>;
51
64
  }
52
65
  export {};