eyeprolog 1.2.14 → 1.2.16
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/README.md +6 -4
- package/package.json +1 -1
- package/src/parser.js +40 -9
- package/src/solver.js +4 -3
- package/test/run-regression.mjs +61 -17
- package/the-art-of-eyeprolog.md +16 -5
package/README.md
CHANGED
|
@@ -79,10 +79,12 @@ member_test ?- member(X, [prolog, logic]).
|
|
|
79
79
|
; X = logic.
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
A label
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
A label is simply the first argument of the ordinary `(?-)/2` term, so it may
|
|
83
|
+
use any normal Prolog term syntax; EyeProlog only requires it to be ground when
|
|
84
|
+
the quad is checked. A non-ground label is a quad failure, not a source syntax
|
|
85
|
+
error, and later quads still run. When a query has multiple indented answer
|
|
86
|
+
descriptions, each description is checked and counted independently, so one
|
|
87
|
+
failed expectation does not prevent the later ones from running.
|
|
86
88
|
|
|
87
89
|
## Strict ISO/IEC 13211-1 core
|
|
88
90
|
|
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -794,13 +794,47 @@ class Parser {
|
|
|
794
794
|
accept(clause);
|
|
795
795
|
continue;
|
|
796
796
|
}
|
|
797
|
+
// Program clauses historically parse comma separately from the head, so
|
|
798
|
+
// keep that grammar unchanged. A quad id, however, is simply the first
|
|
799
|
+
// argument of the ordinary ?-/2 term. If the initial head parse stops at
|
|
800
|
+
// a comma, tentatively reparse that left operand with the normal term
|
|
801
|
+
// grammar (where comma is the predefined priority-1000 xfy operator).
|
|
802
|
+
// Only when the resulting term is actually followed by ?- do we keep the
|
|
803
|
+
// tentative parse; otherwise restore the parser and retain the existing
|
|
804
|
+
// clause/DCG handling below.
|
|
805
|
+
const headState = {
|
|
806
|
+
pos: this.pos,
|
|
807
|
+
line: this.line,
|
|
808
|
+
anonymous: this.anonymous,
|
|
809
|
+
variables: new Map(this.variables),
|
|
810
|
+
previousToken: this.previousToken,
|
|
811
|
+
token: this.token,
|
|
812
|
+
};
|
|
813
|
+
const restoreHeadState = () => {
|
|
814
|
+
this.pos = headState.pos;
|
|
815
|
+
this.line = headState.line;
|
|
816
|
+
this.anonymous = headState.anonymous;
|
|
817
|
+
this.variables = new Map(headState.variables);
|
|
818
|
+
this.previousToken = headState.previousToken;
|
|
819
|
+
this.token = headState.token;
|
|
820
|
+
};
|
|
821
|
+
|
|
797
822
|
let head = this.parseTerm(3);
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
823
|
+
if (this.token.type === TOK.COMMA && !this.strictIso) {
|
|
824
|
+
restoreHeadState();
|
|
825
|
+
const quadId = this.parseTerm(3, true);
|
|
826
|
+
if (this.operatorTokenName() === '?-') {
|
|
827
|
+
this.advance();
|
|
828
|
+
this.parseQuad(quadId, line, accept);
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
restoreHeadState();
|
|
832
|
+
head = this.parseTerm(3);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// Outside quad syntax, preserve the existing program-level comma rule,
|
|
836
|
+
// including the TS 13211-3 semicontext boundary. This is deliberately
|
|
837
|
+
// not a grammar for quad ids.
|
|
804
838
|
if (this.token.type === TOK.COMMA) {
|
|
805
839
|
const items = [head];
|
|
806
840
|
let extraCommaLine = null;
|
|
@@ -809,9 +843,6 @@ class Parser {
|
|
|
809
843
|
this.advance();
|
|
810
844
|
items.push(this.parseTerm(3));
|
|
811
845
|
}
|
|
812
|
-
// Historically the program grammar admitted exactly one comma in a
|
|
813
|
-
// DCG semicontext. Keep that boundary: the broader comma sequence is
|
|
814
|
-
// specifically the quad-label extension, not a new DCG syntax.
|
|
815
846
|
if (extraCommaLine != null && this.operatorTokenName() !== '?-') {
|
|
816
847
|
throw new Error(`parse line ${extraCommaLine}: expected ., got ,`);
|
|
817
848
|
}
|
package/src/solver.js
CHANGED
|
@@ -490,10 +490,11 @@ function normalizeHostResourceError(error) {
|
|
|
490
490
|
const message = String(error?.message ?? '');
|
|
491
491
|
// V8 reports exhausted Map/Set capacity as a host RangeError. ISO 7.12.2 h
|
|
492
492
|
// requires processor resource exhaustion to surface as resource_error/1,
|
|
493
|
-
// with the resource atom implementation dependent.
|
|
494
|
-
//
|
|
493
|
+
// with the resource atom implementation dependent. A finite host capacity
|
|
494
|
+
// ceiling is reported as `memory`; reserve `finite_memory` for the separate
|
|
495
|
+
// convention where no finite amount of memory can complete the computation.
|
|
495
496
|
if (/^(?:Map|Set) maximum size exceeded$/.test(message)) {
|
|
496
|
-
return new PrologError('resource_error(
|
|
497
|
+
return new PrologError('resource_error(memory)');
|
|
497
498
|
}
|
|
498
499
|
return error;
|
|
499
500
|
}
|
package/test/run-regression.mjs
CHANGED
|
@@ -279,8 +279,11 @@ why(
|
|
|
279
279
|
},
|
|
280
280
|
},
|
|
281
281
|
{
|
|
282
|
-
name: 'quad
|
|
282
|
+
name: 'quad ids use ordinary term syntax and each answer description is independent',
|
|
283
283
|
run: () => {
|
|
284
|
+
// Issue #21: the first argument of ?-/2 is an ordinary Prolog term.
|
|
285
|
+
// The commas here are the normal priority-1000 comma operator, not a
|
|
286
|
+
// special metadata grammar owned by the quad parser.
|
|
284
287
|
const source = `9, "✳54·43", passes
|
|
285
288
|
` +
|
|
286
289
|
`?- X is 1+1.
|
|
@@ -298,14 +301,50 @@ why(
|
|
|
298
301
|
const program = Program.parseSources([{ text: source, filename: 'issue-21.pl' }]);
|
|
299
302
|
assertEqual(program.quads.length, 1, 'query group count');
|
|
300
303
|
assertEqual(program.quads[0].answers.length, 4, 'answer-description count');
|
|
301
|
-
assertEqual(program.quads[0].id.name, ',', 'outer
|
|
302
|
-
assertEqual(program.quads[0].id.args[1].name, ',', 'right-associated
|
|
304
|
+
assertEqual(program.quads[0].id.name, ',', 'ordinary outer comma operator');
|
|
305
|
+
assertEqual(program.quads[0].id.args[1].name, ',', 'ordinary right-associated comma operator');
|
|
303
306
|
const result = publicApi.runQuads(program);
|
|
304
307
|
assertEqual(result.total, 4, 'answer-description total');
|
|
305
308
|
assertEqual(result.passed, 4, 'answer-description passed');
|
|
306
309
|
assertEqual(result.failed, 0, 'answer-description failed');
|
|
307
310
|
assertEqual(result.stdout, 'quads: 4 run, 4 passed, 0 failed.\n', 'issue #21 report');
|
|
308
311
|
|
|
312
|
+
// No convention is imposed on the id term. Functional/list/curly and
|
|
313
|
+
// non-comma operator forms all go through the same ordinary term parser.
|
|
314
|
+
const ordinaryIds = Program.parse(
|
|
315
|
+
`meta(9, passes) ?- true.
|
|
316
|
+
true.
|
|
317
|
+
` +
|
|
318
|
+
`[9, passes] ?- true.
|
|
319
|
+
true.
|
|
320
|
+
` +
|
|
321
|
+
`{passes} ?- true.
|
|
322
|
+
true.
|
|
323
|
+
` +
|
|
324
|
+
`(alpha ; beta) ?- true.
|
|
325
|
+
true.
|
|
326
|
+
`,
|
|
327
|
+
);
|
|
328
|
+
assertEqual(ordinaryIds.quads.length, 4, 'ordinary id term count');
|
|
329
|
+
assertEqual(publicApi.runQuads(ordinaryIds).stdout, 'quads: 4 run, 4 passed, 0 failed.\n',
|
|
330
|
+
'ordinary id term report');
|
|
331
|
+
|
|
332
|
+
// Groundness is a quad semantic check, not source syntax. A bad id is
|
|
333
|
+
// reported as a test failure and processing continues to the next quad.
|
|
334
|
+
const nonGround = publicApi.runQuads(
|
|
335
|
+
`Id ?- true.
|
|
336
|
+
true.
|
|
337
|
+
` +
|
|
338
|
+
`ok ?- true.
|
|
339
|
+
true.
|
|
340
|
+
`,
|
|
341
|
+
);
|
|
342
|
+
assertEqual(nonGround.total, 2, 'non-ground id does not abort parsing');
|
|
343
|
+
assertEqual(nonGround.passed, 1, 'following quad still passes');
|
|
344
|
+
assertEqual(nonGround.failed, 1, 'non-ground id is a quad failure');
|
|
345
|
+
assertIncludes(nonGround.stdout, 'quads: BAD_ID Id, <input>:1', 'non-ground id diagnostic');
|
|
346
|
+
assertIncludes(nonGround.stdout, 'quads: 2 run, 1 passed, 1 failed.', 'non-ground continuation summary');
|
|
347
|
+
|
|
309
348
|
const continuing = publicApi.runQuads(
|
|
310
349
|
`case ?- X is 1+1.
|
|
311
350
|
X = 3.
|
|
@@ -2182,22 +2221,27 @@ open(X) :- candidate(X), \\+ closed(X).
|
|
|
2182
2221
|
},
|
|
2183
2222
|
},
|
|
2184
2223
|
{
|
|
2185
|
-
name: 'host Map capacity errors become
|
|
2224
|
+
name: 'host Map/Set capacity errors become resource_error(memory)',
|
|
2186
2225
|
run: () => {
|
|
2187
|
-
const
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
caught =
|
|
2226
|
+
for (const [predicate, message] of [
|
|
2227
|
+
['exhaust_map', 'Map maximum size exceeded'],
|
|
2228
|
+
['exhaust_set', 'Set maximum size exceeded'],
|
|
2229
|
+
]) {
|
|
2230
|
+
const registry = new BuiltinRegistry();
|
|
2231
|
+
registry.add(predicate, 0, function* () {
|
|
2232
|
+
throw new RangeError(message);
|
|
2233
|
+
});
|
|
2234
|
+
const solver = new Solver(Program.parse(''), { registry });
|
|
2235
|
+
const goal = parseGoalText(predicate);
|
|
2236
|
+
let caught = null;
|
|
2237
|
+
try {
|
|
2238
|
+
[...solver.solve([goal], new Env(), 0)];
|
|
2239
|
+
} catch (error) {
|
|
2240
|
+
caught = error;
|
|
2241
|
+
}
|
|
2242
|
+
assertEqual(caught?.name, 'PrologError', `${predicate} normalized error type`);
|
|
2243
|
+
assertEqual(caught?.formal, 'resource_error(memory)', `${predicate} normalized resource error`);
|
|
2198
2244
|
}
|
|
2199
|
-
assertEqual(caught?.name, 'PrologError', 'normalized error type');
|
|
2200
|
-
assertEqual(caught?.formal, 'resource_error(finite_memory)', 'normalized resource error');
|
|
2201
2245
|
},
|
|
2202
2246
|
},
|
|
2203
2247
|
{
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -1823,8 +1823,12 @@ sorted-list operation. No process-global variable registry or creation ordinal
|
|
|
1823
1823
|
is retained or exposed through later comparisons.
|
|
1824
1824
|
|
|
1825
1825
|
Host capacity failures that V8 reports as `Map maximum size exceeded` or `Set`
|
|
1826
|
-
`maximum size exceeded` are normalized at the solver boundary to
|
|
1827
|
-
`resource_error(
|
|
1826
|
+
`maximum size exceeded` are normalized at the solver boundary to
|
|
1827
|
+
`resource_error(memory)` instead of leaking a JavaScript `RangeError`. ISO
|
|
1828
|
+
13211-1 leaves the resource atom implementation dependent. EyeProlog uses
|
|
1829
|
+
`memory` for a finite host allocation/capacity ceiling and reserves the
|
|
1830
|
+
`finite_memory` spelling for the distinct convention where no finite amount of
|
|
1831
|
+
memory could complete the computation.
|
|
1828
1832
|
|
|
1829
1833
|
### Implementation boundary
|
|
1830
1834
|
|
|
@@ -5170,8 +5174,11 @@ from the parsed `?-/1` or `?-/2` term rather than from one privileged surface
|
|
|
5170
5174
|
spelling. Thus `Label ?- Query.`, `?-(Label, Query).`, mixed forms such as
|
|
5171
5175
|
`?-((Label), Query).`, quoted-functor notation, and a parenthesized whole
|
|
5172
5176
|
`(?-(Label, Query)).` denote the same quad when followed by indented answer
|
|
5173
|
-
descriptions.
|
|
5174
|
-
|
|
5177
|
+
descriptions. `Label` itself is parsed with the ordinary Prolog term grammar:
|
|
5178
|
+
there is no quad-specific comma or metadata syntax. The runner requires the
|
|
5179
|
+
resulting first argument to be ground; if it is not, that quad is reported as
|
|
5180
|
+
`BAD_ID` and later quads are still processed. In `--iso-strict` mode this quad
|
|
5181
|
+
interpretation is disabled, and `?-/2` remains ordinary Prolog term syntax.
|
|
5175
5182
|
|
|
5176
5183
|
Run [`iso-dynamic-database.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-dynamic-database.pl)
|
|
5177
5184
|
for an explicitly stateful queue and
|
|
@@ -6401,7 +6408,11 @@ colors ?- color(X).
|
|
|
6401
6408
|
```
|
|
6402
6409
|
|
|
6403
6410
|
Run all quads in a file with `eyeprolog --quads file.pl` or `eyeprolog -q
|
|
6404
|
-
file.pl`. A label such as `colors` is optional.
|
|
6411
|
+
file.pl`. A label such as `colors` is optional. A label is not a separate
|
|
6412
|
+
mini-language: it is the ordinary first argument of `(?-)/2`, and therefore may
|
|
6413
|
+
be any Prolog term admitted there by the normal term grammar. Quad execution
|
|
6414
|
+
requires that argument to be ground; a non-ground label is reported as a quad
|
|
6415
|
+
failure rather than aborting source parsing. Loading the file normally only
|
|
6405
6416
|
records its quads; it does not execute them or add their queries and answers as
|
|
6406
6417
|
program clauses. A quad run prints a summary and exits with status `1` when any
|
|
6407
6418
|
description fails. Quad mode imports `library(prologue)` as a compatibility
|