@dallaylaen/ski-interpreter 2.3.2 → 2.4.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 +26 -0
- package/README.md +9 -2
- package/bin/ski.js +10 -7
- package/lib/ski-interpreter.cjs.js +2082 -2187
- package/lib/ski-interpreter.cjs.js.map +4 -4
- package/lib/ski-interpreter.esm.js +2065 -2198
- package/lib/ski-interpreter.esm.js.map +4 -4
- package/lib/ski-interpreter.min.js +3 -3
- package/lib/ski-interpreter.min.js.map +4 -4
- package/lib/ski-quest.min.js +3 -3
- package/lib/ski-quest.min.js.map +4 -4
- package/{types/src → lib/types}/expr.d.ts +238 -248
- package/{types/src → lib/types}/extras.d.ts +24 -23
- package/lib/types/index.d.ts +68 -0
- package/{types/src → lib/types}/internal.d.ts +27 -23
- package/{types/src → lib/types}/parser.d.ts +40 -83
- package/lib/types/quest.d.ts +232 -0
- package/lib/types/toposort.d.ts +30 -0
- package/package.json +13 -12
- package/types/index.d.ts +0 -3
- package/types/src/quest.d.ts +0 -257
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Expr, Named, FormatOptions, TermInfo } from './expr';
|
|
2
|
+
import { toposort } from './toposort';
|
|
1
3
|
/**
|
|
2
4
|
* @desc Extra utilities that do not belong in the core.
|
|
3
5
|
*/
|
|
@@ -26,20 +28,31 @@
|
|
|
26
28
|
* @param {(e: Expr, props: {}) => number?} predicate
|
|
27
29
|
* @return {{expr?: Expr, total: number, probed: number, gen: number, cache?: Expr[][]}}
|
|
28
30
|
*/
|
|
29
|
-
|
|
31
|
+
type SearchOptions = {
|
|
30
32
|
depth?: number;
|
|
31
33
|
tries?: number;
|
|
32
34
|
infer?: boolean;
|
|
33
35
|
maxArgs?: number;
|
|
34
36
|
max?: number;
|
|
35
37
|
noskip?: boolean;
|
|
36
|
-
|
|
38
|
+
retain?: boolean;
|
|
39
|
+
progress?: (info: {
|
|
40
|
+
gen: number;
|
|
41
|
+
total: number;
|
|
42
|
+
probed: number;
|
|
43
|
+
step: boolean;
|
|
44
|
+
}) => void;
|
|
45
|
+
progressInterval?: number;
|
|
46
|
+
};
|
|
47
|
+
type SearchCallback = (e: Expr, props: TermInfo) => (number | undefined);
|
|
48
|
+
type SearchResult = {
|
|
37
49
|
expr?: Expr;
|
|
38
50
|
total: number;
|
|
39
51
|
probed: number;
|
|
40
52
|
gen: number;
|
|
41
53
|
cache?: Expr[][];
|
|
42
54
|
};
|
|
55
|
+
declare function search(seed: Expr[], options: SearchOptions, predicate: SearchCallback): SearchResult;
|
|
43
56
|
/**
|
|
44
57
|
* @desc Recursively replace all instances of Expr in a data structure with
|
|
45
58
|
* respective string representation using the format() options.
|
|
@@ -48,12 +61,8 @@ export function search(seed: Expr[], options: {
|
|
|
48
61
|
* May be useful for debugging or diagnostic output.
|
|
49
62
|
*
|
|
50
63
|
* @experimental
|
|
51
|
-
*
|
|
52
|
-
* @param {any} obj
|
|
53
|
-
* @param {object} [options] - see Expr.format()
|
|
54
|
-
* @returns {any}
|
|
55
64
|
*/
|
|
56
|
-
|
|
65
|
+
declare function deepFormat(obj: any, options?: FormatOptions): any;
|
|
57
66
|
/**
|
|
58
67
|
* @desc Given an expression and a hash of named terms,
|
|
59
68
|
* return a semicolon-separated string that declares said expression
|
|
@@ -68,21 +77,13 @@ export function deepFormat(obj: any, options?: object): any;
|
|
|
68
77
|
* @param {{[s: string]: Named}} [env]
|
|
69
78
|
* @returns {string}
|
|
70
79
|
*/
|
|
71
|
-
|
|
80
|
+
declare function declare(expr: Expr, env?: {
|
|
72
81
|
[s: string]: Named;
|
|
73
82
|
}): string;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
* @template T
|
|
83
|
-
* @param {Expr} expr
|
|
84
|
-
* @param {(head: Expr, tail: T[]) => T} fun
|
|
85
|
-
* @return {T}
|
|
86
|
-
*/
|
|
87
|
-
export function foldr<T>(expr: Expr, fun: (head: Expr, tail: T[]) => T): T;
|
|
88
|
-
import { Expr } from "./expr";
|
|
83
|
+
export declare const extras: {
|
|
84
|
+
search: typeof search;
|
|
85
|
+
deepFormat: typeof deepFormat;
|
|
86
|
+
declare: typeof declare;
|
|
87
|
+
toposort: typeof toposort;
|
|
88
|
+
};
|
|
89
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { FreeVar, Church } from './expr';
|
|
2
|
+
import { Parser } from './parser';
|
|
3
|
+
import { Quest } from './quest';
|
|
4
|
+
import { toposort } from './toposort';
|
|
5
|
+
export declare class SKI extends Parser {
|
|
6
|
+
static native: import("./internal").Dict<import("./expr").Native>;
|
|
7
|
+
static control: {
|
|
8
|
+
descend: <T>(value: T) => import("./internal").TraverseControl<T>;
|
|
9
|
+
prune: <T>(value: T) => import("./internal").TraverseControl<T>;
|
|
10
|
+
redo: <T>(value: T) => import("./internal").TraverseControl<T>;
|
|
11
|
+
stop: <T>(value: T) => import("./internal").TraverseControl<T>;
|
|
12
|
+
};
|
|
13
|
+
static classes: {
|
|
14
|
+
Expr: typeof import("./expr").Expr;
|
|
15
|
+
App: typeof import("./expr").App;
|
|
16
|
+
Named: typeof import("./expr").Named;
|
|
17
|
+
FreeVar: typeof FreeVar;
|
|
18
|
+
Native: typeof import("./expr").Native;
|
|
19
|
+
Lambda: typeof import("./expr").Lambda;
|
|
20
|
+
Church: typeof Church;
|
|
21
|
+
Alias: typeof import("./expr").Alias;
|
|
22
|
+
};
|
|
23
|
+
static B: import("./expr").Native;
|
|
24
|
+
static C: import("./expr").Native;
|
|
25
|
+
static I: import("./expr").Native;
|
|
26
|
+
static K: import("./expr").Native;
|
|
27
|
+
static S: import("./expr").Native;
|
|
28
|
+
static W: import("./expr").Native;
|
|
29
|
+
static vars(scope?: object): {
|
|
30
|
+
[key: string]: FreeVar;
|
|
31
|
+
};
|
|
32
|
+
static church(n: number): Church;
|
|
33
|
+
static extras: {
|
|
34
|
+
search: (seed: import("./expr").Expr[], options: {
|
|
35
|
+
depth?: number;
|
|
36
|
+
tries?: number;
|
|
37
|
+
infer?: boolean;
|
|
38
|
+
maxArgs?: number;
|
|
39
|
+
max?: number;
|
|
40
|
+
noskip?: boolean;
|
|
41
|
+
retain?: boolean;
|
|
42
|
+
progress?: (info: {
|
|
43
|
+
gen: number;
|
|
44
|
+
total: number;
|
|
45
|
+
probed: number;
|
|
46
|
+
step: boolean;
|
|
47
|
+
}) => void;
|
|
48
|
+
progressInterval?: number;
|
|
49
|
+
}, predicate: (e: import("./expr").Expr, props: import("./expr").TermInfo) => (number | undefined)) => {
|
|
50
|
+
expr?: import("./expr").Expr;
|
|
51
|
+
total: number;
|
|
52
|
+
probed: number;
|
|
53
|
+
gen: number;
|
|
54
|
+
cache?: import("./expr").Expr[][];
|
|
55
|
+
};
|
|
56
|
+
deepFormat: (obj: any, options?: import("./expr").FormatOptions) => any;
|
|
57
|
+
declare: (expr: import("./expr").Expr, env?: {
|
|
58
|
+
[s: string]: import("./expr").Named;
|
|
59
|
+
}) => string;
|
|
60
|
+
toposort: typeof toposort;
|
|
61
|
+
};
|
|
62
|
+
static Quest: typeof Quest;
|
|
63
|
+
}
|
|
64
|
+
declare global {
|
|
65
|
+
interface Window {
|
|
66
|
+
SKI: typeof SKI;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type Dict<T> = {
|
|
2
|
+
[key: string]: T;
|
|
3
|
+
};
|
|
4
|
+
export declare class Tokenizer {
|
|
2
5
|
/**
|
|
3
6
|
* @desc Create a tokenizer that splits strings into tokens according to the given terms.
|
|
4
7
|
* The terms are interpreted as regular expressions, and are sorted by length
|
|
5
8
|
* to ensure that longer matches are preferred over shorter ones.
|
|
6
9
|
* @param {...string|RegExp} terms
|
|
7
10
|
*/
|
|
8
|
-
constructor(...terms: (string | RegExp)[]);
|
|
9
11
|
rex: RegExp;
|
|
12
|
+
constructor(...terms: (string | RegExp)[]);
|
|
10
13
|
/**
|
|
11
14
|
* @desc Split the given string into tokens according to the terms specified in the constructor.
|
|
12
15
|
* @param {string} str
|
|
@@ -23,14 +26,33 @@ export class Tokenizer {
|
|
|
23
26
|
* @param {string} [spec]
|
|
24
27
|
* @returns {Set<string>}
|
|
25
28
|
*/
|
|
26
|
-
export function restrict(set: Set<string>, spec?: string): Set<string>;
|
|
29
|
+
export declare function restrict(set: Set<string>, spec?: string): Set<string>;
|
|
30
|
+
export type TraverseDecorator<T> = (value: T) => TraverseControl<T>;
|
|
31
|
+
export type TraverseValue<T> = T | TraverseControl<T> | null | undefined;
|
|
32
|
+
export declare class TraverseControl<T> {
|
|
33
|
+
/**
|
|
34
|
+
* @desc A wrapper for values returned by fold/traverse callbacks
|
|
35
|
+
* which instructs the traversal to alter its behavior while
|
|
36
|
+
* retaining the value in question.
|
|
37
|
+
*
|
|
38
|
+
* This class is instantiated internally be `SKI.control.*` functions,
|
|
39
|
+
* and is not intended to be used directly by client code.
|
|
40
|
+
*
|
|
41
|
+
* @template T
|
|
42
|
+
* @param {T} value
|
|
43
|
+
* @param {function(T): TraverseControl<T>} decoration
|
|
44
|
+
*/
|
|
45
|
+
value: T;
|
|
46
|
+
decoration: TraverseDecorator<T>;
|
|
47
|
+
constructor(value: T, decoration: TraverseDecorator<T>);
|
|
48
|
+
}
|
|
27
49
|
/**
|
|
28
50
|
* @private
|
|
29
51
|
* @template T
|
|
30
52
|
* @param {T|TraverseControl<T>|null} value
|
|
31
53
|
* @returns {[T?, function|undefined]}
|
|
32
54
|
*/
|
|
33
|
-
export function unwrap<T>(value:
|
|
55
|
+
export declare function unwrap<T>(value: TraverseValue<T>): [T?, TraverseDecorator<T>?];
|
|
34
56
|
/**
|
|
35
57
|
* @desc Prepare a self-referencing wrapper function for use as a fold/traverse control decorator.
|
|
36
58
|
*
|
|
@@ -44,22 +66,4 @@ export function unwrap<T>(value: T | TraverseControl<T> | null): [T?, Function |
|
|
|
44
66
|
* @param {string} [label]
|
|
45
67
|
* @returns {function(T): TraverseControl<T>}
|
|
46
68
|
*/
|
|
47
|
-
export function prepareWrapper
|
|
48
|
-
declare class TraverseControl {
|
|
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
|
-
*
|
|
57
|
-
* @template T
|
|
58
|
-
* @param {T} value
|
|
59
|
-
* @param {function(T): TraverseControl<T>} decoration
|
|
60
|
-
*/
|
|
61
|
-
constructor(value: T, decoration: (arg0: T) => TraverseControl<T>);
|
|
62
|
-
value: T;
|
|
63
|
-
decoration: (arg0: T) => TraverseControl<T>;
|
|
64
|
-
}
|
|
65
|
-
export {};
|
|
69
|
+
export declare function prepareWrapper(label: string): <T>(value: T) => TraverseControl<T>;
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
import { Expr, Alias, Native, Invocation } from './expr';
|
|
2
|
+
export type ParserOptions = {
|
|
3
|
+
allow?: string;
|
|
4
|
+
numbers?: boolean;
|
|
5
|
+
lambdas?: boolean;
|
|
6
|
+
terms?: {
|
|
7
|
+
[key: string]: Expr | string;
|
|
8
|
+
} | string[];
|
|
9
|
+
annotate?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ParseOptions = {
|
|
12
|
+
env?: {
|
|
13
|
+
[key: string]: Expr;
|
|
14
|
+
};
|
|
15
|
+
scope?: object;
|
|
16
|
+
numbers?: boolean;
|
|
17
|
+
lambdas?: boolean;
|
|
18
|
+
allow?: string;
|
|
19
|
+
};
|
|
20
|
+
export type AddOptions = {
|
|
21
|
+
note?: string;
|
|
22
|
+
canonize?: boolean;
|
|
23
|
+
fancy?: string;
|
|
24
|
+
arity?: number;
|
|
25
|
+
};
|
|
26
|
+
export declare class Parser {
|
|
2
27
|
/**
|
|
3
28
|
*
|
|
4
29
|
* @param {{
|
|
@@ -9,23 +34,14 @@ export class SKI {
|
|
|
9
34
|
* annotate?: boolean,
|
|
10
35
|
* }} [options]
|
|
11
36
|
*/
|
|
12
|
-
constructor(options?: {
|
|
13
|
-
allow?: string;
|
|
14
|
-
numbers?: boolean;
|
|
15
|
-
lambdas?: boolean;
|
|
16
|
-
terms?: {
|
|
17
|
-
[key: string]: typeof classes.Expr | string;
|
|
18
|
-
} | string[];
|
|
19
|
-
annotate?: boolean;
|
|
20
|
-
});
|
|
21
37
|
annotate: boolean;
|
|
22
38
|
known: {
|
|
23
|
-
[
|
|
39
|
+
[name: string]: Expr;
|
|
24
40
|
};
|
|
41
|
+
allow: Set<string>;
|
|
25
42
|
hasNumbers: boolean;
|
|
26
43
|
hasLambdas: boolean;
|
|
27
|
-
|
|
28
|
-
allow: Set<string>;
|
|
44
|
+
constructor(options?: ParserOptions);
|
|
29
45
|
/**
|
|
30
46
|
* @desc Declare a new term
|
|
31
47
|
* If the first argument is an Alias, it is added as is.
|
|
@@ -49,7 +65,7 @@ export class SKI {
|
|
|
49
65
|
* @param {number} [options.arity] - custom arity for the term, default is inferred from the implementation
|
|
50
66
|
* @return {SKI} chainable
|
|
51
67
|
*/
|
|
52
|
-
add(term:
|
|
68
|
+
add(term: Alias | string, impl?: Expr | string | ((arg: Expr) => Invocation), options?: AddOptions | string): this;
|
|
53
69
|
/**
|
|
54
70
|
* @desc Internal helper for add() that creates an Alias or Native term from the given arguments.
|
|
55
71
|
* @param {Alias|string} term
|
|
@@ -57,7 +73,7 @@ export class SKI {
|
|
|
57
73
|
* @returns {Native|Alias}
|
|
58
74
|
* @private
|
|
59
75
|
*/
|
|
60
|
-
|
|
76
|
+
_named(term: Alias | string, impl?: Expr | string | ((arg: Expr) => Invocation)): Native | Alias;
|
|
61
77
|
/**
|
|
62
78
|
* @desc Declare a new term if it is not known, otherwise just allow it.
|
|
63
79
|
* Currently only used by quests.
|
|
@@ -68,7 +84,7 @@ export class SKI {
|
|
|
68
84
|
* @param {string|Expr|function(Expr):Partial} impl
|
|
69
85
|
* @returns {SKI}
|
|
70
86
|
*/
|
|
71
|
-
maybeAdd(name: string
|
|
87
|
+
maybeAdd(name: string, impl: Expr | string | ((arg: Expr) => Invocation)): this;
|
|
72
88
|
/**
|
|
73
89
|
* @desc Declare and remove multiple terms at once
|
|
74
90
|
* term=impl adds term
|
|
@@ -76,7 +92,7 @@ export class SKI {
|
|
|
76
92
|
* @param {string[]} list
|
|
77
93
|
* @return {SKI} chainable
|
|
78
94
|
*/
|
|
79
|
-
bulkAdd(list: string[]):
|
|
95
|
+
bulkAdd(list: string[]): this;
|
|
80
96
|
/**
|
|
81
97
|
* Restrict the interpreter to given terms. Terms prepended with '+' will be added
|
|
82
98
|
* and terms preceeded with '-' will be removed.
|
|
@@ -87,7 +103,7 @@ export class SKI {
|
|
|
87
103
|
* @param {string} spec
|
|
88
104
|
* @return {SKI} chainable
|
|
89
105
|
*/
|
|
90
|
-
restrict(spec: string):
|
|
106
|
+
restrict(spec: string): this;
|
|
91
107
|
/**
|
|
92
108
|
*
|
|
93
109
|
* @param {string} spec
|
|
@@ -99,13 +115,13 @@ export class SKI {
|
|
|
99
115
|
* @param {String} name
|
|
100
116
|
* @return {SKI}
|
|
101
117
|
*/
|
|
102
|
-
remove(name: string):
|
|
118
|
+
remove(name: string): this;
|
|
103
119
|
/**
|
|
104
120
|
*
|
|
105
121
|
* @return {{[key:string]: Native|Alias}}
|
|
106
122
|
*/
|
|
107
123
|
getTerms(): {
|
|
108
|
-
[key: string]:
|
|
124
|
+
[key: string]: Expr;
|
|
109
125
|
};
|
|
110
126
|
/**
|
|
111
127
|
* @desc Export term declarations for use in bulkAdd().
|
|
@@ -124,15 +140,7 @@ export class SKI {
|
|
|
124
140
|
* @param {string} [options.allow]
|
|
125
141
|
* @return {Expr}
|
|
126
142
|
*/
|
|
127
|
-
parse
|
|
128
|
-
env?: {
|
|
129
|
-
[keys: string]: typeof classes.Expr;
|
|
130
|
-
};
|
|
131
|
-
scope?: T_1;
|
|
132
|
-
numbers?: boolean;
|
|
133
|
-
lambdas?: boolean;
|
|
134
|
-
allow?: string;
|
|
135
|
-
}): typeof classes.Expr;
|
|
143
|
+
parse(source: string, options?: ParseOptions): Expr;
|
|
136
144
|
/**
|
|
137
145
|
* @desc Parse a single line of source code, without splitting it into declarations.
|
|
138
146
|
* Internal, always use parse() instead.
|
|
@@ -147,17 +155,9 @@ export class SKI {
|
|
|
147
155
|
* @param {string} [options.allow]
|
|
148
156
|
* @return {Expr} parsed expression
|
|
149
157
|
*/
|
|
150
|
-
parseLine
|
|
151
|
-
[
|
|
152
|
-
}, options?:
|
|
153
|
-
env?: {
|
|
154
|
-
[keys: string]: typeof classes.Expr;
|
|
155
|
-
};
|
|
156
|
-
scope?: T_1;
|
|
157
|
-
numbers?: boolean;
|
|
158
|
-
lambdas?: boolean;
|
|
159
|
-
allow?: string;
|
|
160
|
-
}): typeof classes.Expr;
|
|
158
|
+
parseLine(source: string, env?: {
|
|
159
|
+
[key: string]: Expr;
|
|
160
|
+
}, options?: ParseOptions): Expr;
|
|
161
161
|
toJSON(): {
|
|
162
162
|
version: string;
|
|
163
163
|
allow: string;
|
|
@@ -167,46 +167,3 @@ export class SKI {
|
|
|
167
167
|
terms: string[];
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
|
-
export namespace SKI {
|
|
171
|
-
/**
|
|
172
|
-
* Public static shortcuts to common functions (see also ./extras.js)
|
|
173
|
-
*/
|
|
174
|
-
/**
|
|
175
|
-
* @desc Create a proxy object that generates variables on demand,
|
|
176
|
-
* with names corresponding to the property accessed.
|
|
177
|
-
* Different invocations will return distinct variables,
|
|
178
|
-
* even if with the same name.
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* @example const {x, y, z} = SKI.vars();
|
|
182
|
-
* x.name; // 'x'
|
|
183
|
-
* x instanceof FreeVar; // true
|
|
184
|
-
* x.apply(y).apply(z); // x(y)(z)
|
|
185
|
-
*
|
|
186
|
-
* @template T
|
|
187
|
-
* @param {T} [scope] - optional context to bind the generated variables to
|
|
188
|
-
* @return {{[key: string]: FreeVar}}
|
|
189
|
-
*/
|
|
190
|
-
export function vars<T_1>(scope?: T_1): {
|
|
191
|
-
[key: string]: typeof import("./expr").FreeVar;
|
|
192
|
-
};
|
|
193
|
-
/**
|
|
194
|
-
* Convert a number to Church encoding
|
|
195
|
-
* @param {number} n
|
|
196
|
-
* @return {Church}
|
|
197
|
-
*/
|
|
198
|
-
export function church(n: number): typeof import("./expr").Church;
|
|
199
|
-
export { classes };
|
|
200
|
-
export { native };
|
|
201
|
-
export let control: {
|
|
202
|
-
descend: (arg0: T) => TraverseControl<T>;
|
|
203
|
-
prune: (arg0: T) => TraverseControl<T>;
|
|
204
|
-
redo: (arg0: T) => TraverseControl<T>;
|
|
205
|
-
stop: (arg0: T) => TraverseControl<T>;
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
import classes = require("./expr");
|
|
209
|
-
declare const native: {
|
|
210
|
-
[key: string]: classes.Native;
|
|
211
|
-
};
|
|
212
|
-
export {};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Parser } from './parser';
|
|
2
|
+
import { Expr, FreeVar, Alias } from './expr';
|
|
3
|
+
export type CaseResult = {
|
|
4
|
+
pass: boolean;
|
|
5
|
+
reason?: string;
|
|
6
|
+
steps: number;
|
|
7
|
+
start: Expr;
|
|
8
|
+
found: Expr;
|
|
9
|
+
expected?: Expr;
|
|
10
|
+
note?: string;
|
|
11
|
+
args: Expr[];
|
|
12
|
+
case: Case;
|
|
13
|
+
};
|
|
14
|
+
export type Capability = {
|
|
15
|
+
linear?: boolean;
|
|
16
|
+
affine?: boolean;
|
|
17
|
+
normal?: boolean;
|
|
18
|
+
proper?: boolean;
|
|
19
|
+
discard?: boolean;
|
|
20
|
+
duplicate?: boolean;
|
|
21
|
+
arity?: number;
|
|
22
|
+
};
|
|
23
|
+
export type TestCase = [
|
|
24
|
+
string,
|
|
25
|
+
string
|
|
26
|
+
] | [{
|
|
27
|
+
max?: number;
|
|
28
|
+
}, string, string] | [{
|
|
29
|
+
caps: Capability;
|
|
30
|
+
max?: number;
|
|
31
|
+
}, string];
|
|
32
|
+
export type InputSpec = {
|
|
33
|
+
name: string;
|
|
34
|
+
fancy?: string;
|
|
35
|
+
allow?: string;
|
|
36
|
+
numbers?: boolean;
|
|
37
|
+
lambdas?: boolean;
|
|
38
|
+
};
|
|
39
|
+
export type QuestResult = {
|
|
40
|
+
pass: boolean;
|
|
41
|
+
details: CaseResult[];
|
|
42
|
+
expr?: Expr;
|
|
43
|
+
input: Expr[] | string[];
|
|
44
|
+
exception?: Error;
|
|
45
|
+
steps: number;
|
|
46
|
+
weight?: number;
|
|
47
|
+
};
|
|
48
|
+
type QuestMeta = {
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
};
|
|
51
|
+
export type QuestSpec = {
|
|
52
|
+
input: string | InputSpec | (string | InputSpec)[];
|
|
53
|
+
cases: TestCase[];
|
|
54
|
+
allow?: string;
|
|
55
|
+
numbers?: boolean;
|
|
56
|
+
lambdas?: boolean;
|
|
57
|
+
env?: string[];
|
|
58
|
+
engine?: Parser;
|
|
59
|
+
engineFull?: Parser;
|
|
60
|
+
id?: string | number;
|
|
61
|
+
name?: string;
|
|
62
|
+
intro?: string | string[];
|
|
63
|
+
} & QuestMeta;
|
|
64
|
+
export type SelfCheck = {
|
|
65
|
+
accepted?: string[][];
|
|
66
|
+
rejected?: string[][];
|
|
67
|
+
};
|
|
68
|
+
type AddCaseOptions = {
|
|
69
|
+
engine?: Parser;
|
|
70
|
+
env?: {
|
|
71
|
+
[key: string]: Expr;
|
|
72
|
+
};
|
|
73
|
+
max?: number;
|
|
74
|
+
note?: string;
|
|
75
|
+
caps?: Capability;
|
|
76
|
+
};
|
|
77
|
+
export declare class Quest {
|
|
78
|
+
/**
|
|
79
|
+
* @description A combinator problem with a set of test cases for the proposed solution.
|
|
80
|
+
* @param {QuestSpec} options
|
|
81
|
+
* @example const quest = new Quest({
|
|
82
|
+
* input: 'identity',
|
|
83
|
+
* cases: [
|
|
84
|
+
* ['identity x', 'x'],
|
|
85
|
+
* ],
|
|
86
|
+
* allow: 'SK',
|
|
87
|
+
* intro: 'Find a combinator that behaves like the identity function.',
|
|
88
|
+
* });
|
|
89
|
+
* quest.check('S K K'); // { pass: true, details: [...], ... }
|
|
90
|
+
* quest.check('K S'); // { pass: false, details: [...], ... }
|
|
91
|
+
* quest.check('K x'); // fail! internal variable x is not equal to free variable x,
|
|
92
|
+
* // despite having the same name.
|
|
93
|
+
* quest.check('I'); // fail! I not in the allowed list.
|
|
94
|
+
*/
|
|
95
|
+
input: (InputSpec & {
|
|
96
|
+
placeholder: FreeVar;
|
|
97
|
+
})[];
|
|
98
|
+
cases: Case[];
|
|
99
|
+
engineFull: Parser;
|
|
100
|
+
engine: Parser;
|
|
101
|
+
restrict: {
|
|
102
|
+
allow?: string;
|
|
103
|
+
numbers?: boolean;
|
|
104
|
+
lambdas?: boolean;
|
|
105
|
+
};
|
|
106
|
+
env: {
|
|
107
|
+
[key: string]: Expr;
|
|
108
|
+
};
|
|
109
|
+
envFull: {
|
|
110
|
+
[key: string]: Expr;
|
|
111
|
+
};
|
|
112
|
+
id?: string | number;
|
|
113
|
+
name?: string;
|
|
114
|
+
intro?: string;
|
|
115
|
+
meta?: QuestMeta;
|
|
116
|
+
constructor(options: QuestSpec);
|
|
117
|
+
/**
|
|
118
|
+
* Display allowed terms based on what engine thinks of this.env + this.restrict.allow
|
|
119
|
+
* @return {string}
|
|
120
|
+
*/
|
|
121
|
+
allowed(): string;
|
|
122
|
+
addInput(term: string | InputSpec): void;
|
|
123
|
+
/**
|
|
124
|
+
*
|
|
125
|
+
* @param {{} | string} opt
|
|
126
|
+
* @param {string} terms
|
|
127
|
+
* @return {Quest}
|
|
128
|
+
*/
|
|
129
|
+
add(opt: AddCaseOptions | string, ...terms: string[]): this;
|
|
130
|
+
/**
|
|
131
|
+
* @description Statefully parse a list of strings into expressions or fancy aliases thereof.
|
|
132
|
+
* @param {string[]} input
|
|
133
|
+
* @return {{terms: Expr[], weight: number}}
|
|
134
|
+
*/
|
|
135
|
+
prepare(...input: string[]): {
|
|
136
|
+
prepared: (FreeVar | Alias)[];
|
|
137
|
+
weight: number;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* @param {string} input
|
|
142
|
+
* @return {QuestResult}
|
|
143
|
+
*/
|
|
144
|
+
check(...input: string[]): QuestResult;
|
|
145
|
+
verify(options: {
|
|
146
|
+
solutions?: SelfCheck | {
|
|
147
|
+
[key: string | number]: SelfCheck;
|
|
148
|
+
};
|
|
149
|
+
seen?: Set<string | number>;
|
|
150
|
+
date?: boolean;
|
|
151
|
+
}): {
|
|
152
|
+
[key: string]: unknown;
|
|
153
|
+
} | null;
|
|
154
|
+
/**
|
|
155
|
+
* @desc Verify that solutions that are expected to pass/fail do so.
|
|
156
|
+
* @param {SelfCheck|{[key: string]: SelfCheck}} dataset
|
|
157
|
+
* @return {{shouldPass: {input: string[], result: QuestResult}[], shouldFail: {input: string[], result: QuestResult}[]} | null}
|
|
158
|
+
*/
|
|
159
|
+
verifySolutions(dataset: SelfCheck | {
|
|
160
|
+
[key: string | number]: SelfCheck;
|
|
161
|
+
}): {
|
|
162
|
+
shouldPass: {
|
|
163
|
+
input: string[];
|
|
164
|
+
result: QuestResult;
|
|
165
|
+
}[];
|
|
166
|
+
shouldFail: {
|
|
167
|
+
input: string[];
|
|
168
|
+
result: QuestResult;
|
|
169
|
+
}[];
|
|
170
|
+
} | null;
|
|
171
|
+
verifyMeta(options?: {
|
|
172
|
+
date?: boolean;
|
|
173
|
+
}): {
|
|
174
|
+
[key: string]: unknown;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @return {TestCase[]}
|
|
179
|
+
*/
|
|
180
|
+
show(): Case[];
|
|
181
|
+
static Group: new (options: {
|
|
182
|
+
name?: string;
|
|
183
|
+
intro?: string | string[];
|
|
184
|
+
id?: string | number;
|
|
185
|
+
content?: (Quest | QuestSpec)[];
|
|
186
|
+
}) => {
|
|
187
|
+
verify: (options: {
|
|
188
|
+
seen?: Set<string | number>;
|
|
189
|
+
date?: boolean;
|
|
190
|
+
}) => {
|
|
191
|
+
[key: string]: unknown;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
static Case: new (input: FreeVar[], options: AddCaseOptions) => Case;
|
|
195
|
+
}
|
|
196
|
+
declare class Case {
|
|
197
|
+
/**
|
|
198
|
+
* @param {FreeVar[]} input
|
|
199
|
+
* @param {{
|
|
200
|
+
* max?: number,
|
|
201
|
+
* note?: string,
|
|
202
|
+
* env?: {[key:string]: Expr},
|
|
203
|
+
* engine: Parser
|
|
204
|
+
* }} options
|
|
205
|
+
*/
|
|
206
|
+
max: number;
|
|
207
|
+
note: string;
|
|
208
|
+
env: {
|
|
209
|
+
[key: string]: Expr;
|
|
210
|
+
};
|
|
211
|
+
input: FreeVar[];
|
|
212
|
+
engine: Parser;
|
|
213
|
+
constructor(input: FreeVar[], options: AddCaseOptions);
|
|
214
|
+
parse(src: string): Subst;
|
|
215
|
+
/**
|
|
216
|
+
* @param {Expr} expr
|
|
217
|
+
* @return {CaseResult}
|
|
218
|
+
*/
|
|
219
|
+
check(..._expr: Expr[]): CaseResult;
|
|
220
|
+
}
|
|
221
|
+
declare class Subst {
|
|
222
|
+
/**
|
|
223
|
+
* @descr A placeholder object with exactly n free variables to be substituted later.
|
|
224
|
+
* @param {Expr} expr
|
|
225
|
+
* @param {FreeVar[]} env
|
|
226
|
+
*/
|
|
227
|
+
expr: Expr;
|
|
228
|
+
env: FreeVar[];
|
|
229
|
+
constructor(expr: Expr, env: FreeVar[]);
|
|
230
|
+
apply(list: Expr[]): Expr;
|
|
231
|
+
}
|
|
232
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Expr, Named } from './expr';
|
|
2
|
+
/**
|
|
3
|
+
* @desc Sort a list in such a way that dependent terms come after the (named) terms they depend on.
|
|
4
|
+
* If env is given, only terms listed there are taken into account.
|
|
5
|
+
* If env is omitted, it will be implied from the list.
|
|
6
|
+
* If list is omitted, it will default to values of env.
|
|
7
|
+
* If just one term is given instead of a list, it will be coerced into a list.
|
|
8
|
+
*
|
|
9
|
+
* No terms outside env + list may ever appear in the result.
|
|
10
|
+
*
|
|
11
|
+
* The terms in env must be named and their names must match their keys.
|
|
12
|
+
*
|
|
13
|
+
* @param {Expr|Expr[]} list
|
|
14
|
+
* @param {{[s:string]: Named}} env
|
|
15
|
+
* @returns {{list: Expr[], env: {[s:string]: Named}}}
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const expr = ski.parse(src);
|
|
19
|
+
* toposort([expr], ski.getTerms()); // returns all terms appearing in Expr in correct order
|
|
20
|
+
*/
|
|
21
|
+
type ToposortResult = {
|
|
22
|
+
list: Expr[];
|
|
23
|
+
env: {
|
|
24
|
+
[s: string]: Named;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export declare function toposort(list: Expr[] | Expr | undefined, env: {
|
|
28
|
+
[s: string]: Named;
|
|
29
|
+
} | undefined): ToposortResult;
|
|
30
|
+
export {};
|