@dallaylaen/ski-interpreter 1.0.1 → 1.1.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 +57 -0
- package/README.md +1 -0
- package/lib/expr.js +321 -235
- package/lib/parser.js +3 -0
- package/lib/quest.js +72 -16
- package/lib/util.js +10 -6
- package/package.json +11 -4
- package/types/lib/expr.d.ts +75 -46
- package/types/lib/quest.d.ts +29 -7
- package/types/lib/util.d.ts +1 -1
package/lib/parser.js
CHANGED
|
@@ -191,6 +191,9 @@ class SKI {
|
|
|
191
191
|
* @return {Expr}
|
|
192
192
|
*/
|
|
193
193
|
parse (source, vars = {}, options = {}) {
|
|
194
|
+
if (typeof source !== 'string')
|
|
195
|
+
throw new Error('parse: source must be a string, got ' + typeof source);
|
|
196
|
+
|
|
194
197
|
const lines = source.replace(/\/\/[^\n]*$/gm, '')
|
|
195
198
|
.split(/\s*;[\s;]*/).filter( s => s.match(/\S/));
|
|
196
199
|
|
package/lib/quest.js
CHANGED
|
@@ -15,6 +15,26 @@ const { Expr, FreeVar, Alias, Lambda } = SKI.classes;
|
|
|
15
15
|
* }} CaseResult
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {{
|
|
20
|
+
* linear: boolean?,
|
|
21
|
+
* affine: boolean?,
|
|
22
|
+
* normal: boolean?,
|
|
23
|
+
* proper: boolean?,
|
|
24
|
+
* discard: boolean?,
|
|
25
|
+
* duplicate: boolean?,
|
|
26
|
+
* arity: number?,
|
|
27
|
+
* }} Capability
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {
|
|
32
|
+
* [string, string]
|
|
33
|
+
* | [{max: number?}, string, string]
|
|
34
|
+
* | [{caps: Capability, max: number?}, string]
|
|
35
|
+
* } TestCase
|
|
36
|
+
*/
|
|
37
|
+
|
|
18
38
|
class Quest {
|
|
19
39
|
/**
|
|
20
40
|
* @description A combinator problem with a set of test cases for the proposed solution.
|
|
@@ -27,7 +47,7 @@ class Quest {
|
|
|
27
47
|
* vars: string[]?,
|
|
28
48
|
* engine: SKI?,
|
|
29
49
|
* engineFull: SKI?,
|
|
30
|
-
* cases: [
|
|
50
|
+
* cases: TestCase[],
|
|
31
51
|
* }} options
|
|
32
52
|
*/
|
|
33
53
|
constructor (options = {}) {
|
|
@@ -124,8 +144,8 @@ class Quest {
|
|
|
124
144
|
|
|
125
145
|
const input = this.input.map( t => t.placeholder );
|
|
126
146
|
this.cases.push(
|
|
127
|
-
opt.
|
|
128
|
-
? new
|
|
147
|
+
opt.caps
|
|
148
|
+
? new PropertyCase(input, opt, terms)
|
|
129
149
|
: new ExprCase(input, opt, terms)
|
|
130
150
|
);
|
|
131
151
|
return this;
|
|
@@ -275,30 +295,66 @@ class ExprCase extends Case {
|
|
|
275
295
|
}
|
|
276
296
|
}
|
|
277
297
|
|
|
278
|
-
|
|
298
|
+
const knownCaps = {
|
|
299
|
+
normal: true,
|
|
300
|
+
proper: true,
|
|
301
|
+
discard: true,
|
|
302
|
+
duplicate: true,
|
|
303
|
+
linear: true,
|
|
304
|
+
affine: true,
|
|
305
|
+
arity: true,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
class PropertyCase extends Case {
|
|
279
309
|
// test that an expression uses all of its inputs exactly once
|
|
280
310
|
constructor (input, options, terms) {
|
|
281
311
|
super(input, options);
|
|
312
|
+
if (terms.length > 1)
|
|
313
|
+
throw new Error('PropertyCase accepts exactly 1 string');
|
|
314
|
+
if (!options.caps || typeof options.caps !== 'object' || !Object.keys(options.caps).length)
|
|
315
|
+
throw new Error('PropertyCase requires a caps object with at least one capability');
|
|
316
|
+
const unknown = Object.keys(options.caps).filter( c => !knownCaps[c] );
|
|
317
|
+
if (unknown.length)
|
|
318
|
+
throw new Error('PropertyCase: don\'t know how to test these capabilities: ' + unknown.join(', '));
|
|
319
|
+
|
|
282
320
|
this.expr = this.parse(terms[0]);
|
|
321
|
+
this.caps = options.caps;
|
|
322
|
+
|
|
323
|
+
if (this.caps.linear) {
|
|
324
|
+
delete this.caps.linear;
|
|
325
|
+
this.caps.duplicate = false;
|
|
326
|
+
this.caps.discard = false;
|
|
327
|
+
this.caps.normal = true;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this.caps.affine) {
|
|
331
|
+
delete this.caps.affine;
|
|
332
|
+
this.caps.normal = true;
|
|
333
|
+
this.caps.duplicate = false;
|
|
334
|
+
}
|
|
283
335
|
}
|
|
284
336
|
|
|
285
337
|
check (...expr) {
|
|
286
338
|
const start = this.expr.apply(...expr);
|
|
287
339
|
const r = start.run({ max: this.max });
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
340
|
+
const guess = r.expr.guess({ max: this.max });
|
|
341
|
+
|
|
342
|
+
const reason = [];
|
|
343
|
+
for (const cap in this.caps) {
|
|
344
|
+
if (guess[cap] !== this.caps[cap])
|
|
345
|
+
reason.push('expected property ' + cap + ' to be ' + this.caps[cap] + ', found ' + guess[cap]);
|
|
346
|
+
}
|
|
347
|
+
|
|
292
348
|
return {
|
|
293
|
-
pass:
|
|
294
|
-
reason,
|
|
295
|
-
steps:
|
|
349
|
+
pass: !reason.length,
|
|
350
|
+
reason: reason ? reason.join('\n') : null,
|
|
351
|
+
steps: r.steps,
|
|
296
352
|
start,
|
|
297
|
-
found:
|
|
298
|
-
case:
|
|
299
|
-
note:
|
|
300
|
-
args:
|
|
301
|
-
}
|
|
353
|
+
found: r.expr,
|
|
354
|
+
case: this,
|
|
355
|
+
note: this.note,
|
|
356
|
+
args: expr,
|
|
357
|
+
};
|
|
302
358
|
}
|
|
303
359
|
}
|
|
304
360
|
|
package/lib/util.js
CHANGED
|
@@ -54,13 +54,17 @@ function restrict (set, spec) {
|
|
|
54
54
|
return out;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function
|
|
58
|
-
const
|
|
57
|
+
function skipDup (arr, map) {
|
|
58
|
+
const skip = new Set();
|
|
59
|
+
const dup = new Set();
|
|
59
60
|
for (let n = 0; n < arr.length; n++) {
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
const count = map.get(arr[n]) ?? 0;
|
|
62
|
+
if (!count)
|
|
63
|
+
skip.add(n);
|
|
64
|
+
else if (count > 1)
|
|
65
|
+
dup.add(n);
|
|
62
66
|
}
|
|
63
|
-
return
|
|
67
|
+
return [skip, dup];
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
function isSubset (a, b) {
|
|
@@ -71,4 +75,4 @@ function isSubset (a, b) {
|
|
|
71
75
|
return true;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
module.exports = { Tokenizer, restrict,
|
|
78
|
+
module.exports = { Tokenizer, restrict, skipDup, isSubset };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dallaylaen/ski-interpreter",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Simple Kombinator Interpreter - a combinatory logic & lambda calculus parser and interpreter. Supports SKI, BCKW, Church numerals, and setting up assertions ('quests') involving all of the above.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"combinatory logic",
|
|
@@ -15,12 +15,17 @@
|
|
|
15
15
|
"ski": "bin/ski.js"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"
|
|
18
|
+
"lint": "npx eslint lib --ext .js,.ts",
|
|
19
|
+
"types": "npx -p typescript tsc index.js --declaration --allowJs --emitDeclarationOnly --outDir types",
|
|
20
|
+
"test": "npx nyc mocha",
|
|
21
|
+
"minify": "npx esbuild --bundle ./index.js --outfile=docs/build/js/ski-interpreter.min.js --minify --sourcemap",
|
|
22
|
+
"build": "npm run lint && npm run types && npm run test && npm run minify"
|
|
19
23
|
},
|
|
20
24
|
"files": [
|
|
21
25
|
"package.json",
|
|
22
26
|
"README.md",
|
|
23
27
|
"LICENSE",
|
|
28
|
+
"CHANGELOG.md",
|
|
24
29
|
"lib",
|
|
25
30
|
"types"
|
|
26
31
|
],
|
|
@@ -35,12 +40,14 @@
|
|
|
35
40
|
},
|
|
36
41
|
"homepage": "https://dallaylaen.github.io/ski-interpreter/index.html",
|
|
37
42
|
"devDependencies": {
|
|
43
|
+
"chai": "^4.3.7",
|
|
44
|
+
"esbuild": "^0.25.10",
|
|
38
45
|
"eslint": "^8.57.0",
|
|
39
46
|
"eslint-config-standard": "^17.1.0",
|
|
40
47
|
"eslint-plugin-align-assignments": "^1.1.2",
|
|
41
48
|
"eslint-plugin-import": "^2.29.1",
|
|
42
|
-
"typescript": "^5.8.2",
|
|
43
49
|
"mocha": "^10.2.0",
|
|
44
|
-
"nyc": "^15.1.0"
|
|
50
|
+
"nyc": "^15.1.0",
|
|
51
|
+
"typescript": "^5.8.2"
|
|
45
52
|
}
|
|
46
53
|
}
|
package/types/lib/expr.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export type AnyArity = (arg0: Expr) => Expr | AnyArity;
|
|
2
2
|
export class Expr {
|
|
3
|
-
arity: number;
|
|
4
3
|
/**
|
|
5
4
|
* postprocess term after parsing. typically return self but may return other term or die
|
|
6
5
|
* @return {Expr}
|
|
@@ -31,37 +30,67 @@ export class Expr {
|
|
|
31
30
|
* @return {Map<Expr, number>}
|
|
32
31
|
*/
|
|
33
32
|
getSymbols(): Map<Expr, number>;
|
|
33
|
+
/**
|
|
34
|
+
* @desc Given a list of pairs of term, replaces every subtree
|
|
35
|
+
* that is equivalent to the first term in pair with the second one.
|
|
36
|
+
* If a simgle term is given, it is duplicated into a pair.
|
|
37
|
+
*
|
|
38
|
+
* @example S(SKK)(SKS).replace('I') = SII // we found 2 subtrees equivalent to I
|
|
39
|
+
* and replaced them with I
|
|
40
|
+
*
|
|
41
|
+
* @param {(Expr | [find: Expr, replace: Expr])[]} terms
|
|
42
|
+
* @param {Object} [opt] - options
|
|
43
|
+
* @return {Expr}
|
|
44
|
+
*/
|
|
45
|
+
replace(terms: (Expr | [find: Expr, replace: Expr])[], opt?: any): Expr;
|
|
46
|
+
_replace(pairs: any, opt: any): any;
|
|
34
47
|
/**
|
|
35
48
|
* @desc rought estimate of the complexity of the term
|
|
36
49
|
* @return {number}
|
|
37
50
|
*/
|
|
38
51
|
weight(): number;
|
|
39
52
|
/**
|
|
53
|
+
* @desc Try to find an equivalent lambda term for the expression,
|
|
54
|
+
* returning also the term's arity and some other properties.
|
|
55
|
+
*
|
|
56
|
+
* This is used internally when declaring a Native term,
|
|
57
|
+
* unless {canonize: false} is used.
|
|
58
|
+
*
|
|
59
|
+
* As of current it only recognizes terms that have a normal form,
|
|
60
|
+
* perhaps after adding some variables. This may change in the future.
|
|
61
|
+
*
|
|
62
|
+
* Use lambdify() if you want to get a lambda term in any case.
|
|
40
63
|
*
|
|
41
|
-
* @param {{max: number?, maxArgs: number
|
|
64
|
+
* @param {{max: number?, maxArgs: number?}} options
|
|
42
65
|
* @return {{
|
|
43
|
-
*
|
|
44
|
-
*
|
|
66
|
+
* normal: boolean,
|
|
67
|
+
* steps: number,
|
|
68
|
+
* expr: Expr?,
|
|
45
69
|
* arity: number?,
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* skip: Set<number
|
|
70
|
+
* proper: boolean?,
|
|
71
|
+
* discard: boolean?,
|
|
72
|
+
* duplicate: boolean?,
|
|
73
|
+
* skip: Set<number>?,
|
|
74
|
+
* dup: Set<number>?
|
|
50
75
|
* }}
|
|
51
76
|
*/
|
|
52
|
-
|
|
77
|
+
guess(options?: {
|
|
53
78
|
max: number | null;
|
|
54
79
|
maxArgs: number | null;
|
|
55
|
-
bestGuess?: Expr;
|
|
56
80
|
}): {
|
|
57
|
-
|
|
58
|
-
|
|
81
|
+
normal: boolean;
|
|
82
|
+
steps: number;
|
|
83
|
+
expr: Expr | null;
|
|
59
84
|
arity: number | null;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
85
|
+
proper: boolean | null;
|
|
86
|
+
discard: boolean | null;
|
|
87
|
+
duplicate: boolean | null;
|
|
63
88
|
skip: Set<number> | null;
|
|
89
|
+
dup: Set<number> | null;
|
|
64
90
|
};
|
|
91
|
+
_guess(options: any, preArgs?: any[], steps?: number): any;
|
|
92
|
+
_aslist(): this[];
|
|
93
|
+
_firstVar(): boolean;
|
|
65
94
|
/**
|
|
66
95
|
* @desc Returns a series of lambda terms equivalent to the given expression,
|
|
67
96
|
* up to the provided computation steps limit,
|
|
@@ -110,13 +139,6 @@ export class Expr {
|
|
|
110
139
|
*/
|
|
111
140
|
renameVars(seq: IterableIterator<string>): Expr;
|
|
112
141
|
_rski(options: any): this;
|
|
113
|
-
/**
|
|
114
|
-
* @desc Whether the term will reduce further if given more arguments.
|
|
115
|
-
* In practice, equivalent to "starts with a FreeVar"
|
|
116
|
-
* Used by canonize (duh...)
|
|
117
|
-
* @return {boolean}
|
|
118
|
-
*/
|
|
119
|
-
wantsArgs(): boolean;
|
|
120
142
|
/**
|
|
121
143
|
* Apply self to list of given args.
|
|
122
144
|
* Normally, only native combinators know how to do it.
|
|
@@ -172,13 +194,18 @@ export class Expr {
|
|
|
172
194
|
steps: number;
|
|
173
195
|
}>;
|
|
174
196
|
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
197
|
+
*
|
|
198
|
+
* @param {Expr} other
|
|
199
|
+
* @return {boolean}
|
|
200
|
+
*/
|
|
179
201
|
equals(other: Expr): boolean;
|
|
180
202
|
contains(other: any): boolean;
|
|
181
|
-
|
|
203
|
+
/**
|
|
204
|
+
* @desc Assert expression equality. Can be used in tests.
|
|
205
|
+
* @param {Expr} expected
|
|
206
|
+
* @param {string} comment
|
|
207
|
+
*/
|
|
208
|
+
expect(expected: Expr, comment?: string): void;
|
|
182
209
|
/**
|
|
183
210
|
* @param {{terse: boolean?, html: boolean?}} [options]
|
|
184
211
|
* @return {string} string representation of the expression
|
|
@@ -188,10 +215,11 @@ export class Expr {
|
|
|
188
215
|
html: boolean | null;
|
|
189
216
|
}): string;
|
|
190
217
|
/**
|
|
191
|
-
*
|
|
218
|
+
* @desc Whether the expression needs parentheses when printed.
|
|
219
|
+
* @param {boolean} [first] - whether this is the first term in a sequence
|
|
192
220
|
* @return {boolean}
|
|
193
221
|
*/
|
|
194
|
-
needsParens(): boolean;
|
|
222
|
+
needsParens(first?: boolean): boolean;
|
|
195
223
|
/**
|
|
196
224
|
*
|
|
197
225
|
* @return {string}
|
|
@@ -209,22 +237,16 @@ export class App extends Expr {
|
|
|
209
237
|
* @param {Expr} args
|
|
210
238
|
*/
|
|
211
239
|
constructor(fun: Expr, ...args: Expr);
|
|
212
|
-
|
|
213
|
-
|
|
240
|
+
arg: any;
|
|
241
|
+
fun: any;
|
|
214
242
|
final: boolean;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
canonical?: Expr;
|
|
223
|
-
steps: number | null;
|
|
224
|
-
skip: Set<number> | null;
|
|
225
|
-
};
|
|
226
|
-
renameVars(seq: any): Expr;
|
|
227
|
-
subst(plug: any, value: any): Expr;
|
|
243
|
+
arity: any;
|
|
244
|
+
weight(): any;
|
|
245
|
+
_firstVar(): any;
|
|
246
|
+
apply(...args: any[]): App;
|
|
247
|
+
expand(): any;
|
|
248
|
+
renameVars(seq: any): any;
|
|
249
|
+
subst(plug: any, value: any): any;
|
|
228
250
|
/**
|
|
229
251
|
* @return {{expr: Expr, steps: number}}
|
|
230
252
|
*/
|
|
@@ -232,8 +254,12 @@ export class App extends Expr {
|
|
|
232
254
|
expr: Expr;
|
|
233
255
|
steps: number;
|
|
234
256
|
};
|
|
257
|
+
reduce(args: any): any;
|
|
235
258
|
split(): any[];
|
|
236
|
-
|
|
259
|
+
_aslist(): any[];
|
|
260
|
+
equals(other: any): any;
|
|
261
|
+
contains(other: any): any;
|
|
262
|
+
needsParens(first: any): boolean;
|
|
237
263
|
toString(opt?: {}): string;
|
|
238
264
|
}
|
|
239
265
|
export class FreeVar extends Named {
|
|
@@ -250,6 +276,7 @@ export class Lambda extends Expr {
|
|
|
250
276
|
constructor(arg: FreeVar | FreeVar[], impl: Expr);
|
|
251
277
|
arg: FreeVar;
|
|
252
278
|
impl: Expr;
|
|
279
|
+
arity: number;
|
|
253
280
|
reduce(input: any): Expr;
|
|
254
281
|
subst(plug: any, value: any): Lambda;
|
|
255
282
|
expand(): Lambda;
|
|
@@ -257,6 +284,7 @@ export class Lambda extends Expr {
|
|
|
257
284
|
_rski(options: any): any;
|
|
258
285
|
equals(other: any): boolean;
|
|
259
286
|
toString(opt?: {}): string;
|
|
287
|
+
needsParens(first: any): boolean;
|
|
260
288
|
}
|
|
261
289
|
/**
|
|
262
290
|
* @typedef {function(Expr): Expr | AnyArity} AnyArity
|
|
@@ -283,7 +311,7 @@ export class Native extends Named {
|
|
|
283
311
|
arity: any;
|
|
284
312
|
note: any;
|
|
285
313
|
apply(...args: any[]): Expr;
|
|
286
|
-
_rski(options: any):
|
|
314
|
+
_rski(options: any): any;
|
|
287
315
|
reduce(args: any): any;
|
|
288
316
|
}
|
|
289
317
|
export class Alias extends Named {
|
|
@@ -319,6 +347,7 @@ export class Alias extends Named {
|
|
|
319
347
|
equals(other: any): any;
|
|
320
348
|
_rski(options: any): Expr;
|
|
321
349
|
toString(opt: any): string;
|
|
350
|
+
needsParens(first: any): boolean;
|
|
322
351
|
}
|
|
323
352
|
export class Church extends Native {
|
|
324
353
|
constructor(n: any);
|
package/types/lib/quest.d.ts
CHANGED
|
@@ -9,6 +9,15 @@ export type CaseResult = {
|
|
|
9
9
|
args: typeof import("./expr").Expr[];
|
|
10
10
|
case: Case;
|
|
11
11
|
};
|
|
12
|
+
export type Capability = {
|
|
13
|
+
linear: boolean | null;
|
|
14
|
+
affine: boolean | null;
|
|
15
|
+
normal: boolean | null;
|
|
16
|
+
proper: boolean | null;
|
|
17
|
+
discard: boolean | null;
|
|
18
|
+
duplicate: boolean | null;
|
|
19
|
+
arity: number | null;
|
|
20
|
+
};
|
|
12
21
|
/**
|
|
13
22
|
* @typedef {{
|
|
14
23
|
* pass: boolean,
|
|
@@ -22,6 +31,24 @@ export type CaseResult = {
|
|
|
22
31
|
* case: Case
|
|
23
32
|
* }} CaseResult
|
|
24
33
|
*/
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {{
|
|
36
|
+
* linear: boolean?,
|
|
37
|
+
* affine: boolean?,
|
|
38
|
+
* normal: boolean?,
|
|
39
|
+
* proper: boolean?,
|
|
40
|
+
* discard: boolean?,
|
|
41
|
+
* duplicate: boolean?,
|
|
42
|
+
* arity: number?,
|
|
43
|
+
* }} Capability
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {
|
|
47
|
+
* [string, string]
|
|
48
|
+
* | [{max: number?}, string, string]
|
|
49
|
+
* | [{caps: Capability, max: number?}, string]
|
|
50
|
+
* } TestCase
|
|
51
|
+
*/
|
|
25
52
|
export class Quest {
|
|
26
53
|
/**
|
|
27
54
|
* @description A combinator problem with a set of test cases for the proposed solution.
|
|
@@ -34,7 +61,7 @@ export class Quest {
|
|
|
34
61
|
* vars: string[]?,
|
|
35
62
|
* engine: SKI?,
|
|
36
63
|
* engineFull: SKI?,
|
|
37
|
-
* cases: [
|
|
64
|
+
* cases: TestCase[],
|
|
38
65
|
* }} options
|
|
39
66
|
*/
|
|
40
67
|
constructor(options?: {
|
|
@@ -46,12 +73,7 @@ export class Quest {
|
|
|
46
73
|
vars: string[] | null;
|
|
47
74
|
engine: SKI | null;
|
|
48
75
|
engineFull: SKI | null;
|
|
49
|
-
cases: [
|
|
50
|
-
max: number | null;
|
|
51
|
-
note: string | null;
|
|
52
|
-
feedInput: boolean;
|
|
53
|
-
lambdas: boolean | null;
|
|
54
|
-
} | string[], ...string[][]] | null;
|
|
76
|
+
cases: TestCase[];
|
|
55
77
|
});
|
|
56
78
|
engine: SKI;
|
|
57
79
|
engineFull: SKI;
|
package/types/lib/util.d.ts
CHANGED
|
@@ -9,5 +9,5 @@ export class Tokenizer {
|
|
|
9
9
|
split(str: string): string[];
|
|
10
10
|
}
|
|
11
11
|
export function restrict(set: any, spec: any): any;
|
|
12
|
-
export function
|
|
12
|
+
export function skipDup(arr: any, map: any): any[];
|
|
13
13
|
export function isSubset(a: any, b: any): boolean;
|