eyeprolog 1.2.0 → 1.2.1
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/package.json +1 -1
- package/src/parser.js +32 -23
- package/test/run-regression.mjs +37 -0
- package/the-art-of-eyeprolog.md +9 -2
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -663,8 +663,7 @@ class Parser {
|
|
|
663
663
|
}
|
|
664
664
|
return this.source[start] === ' ' || this.source[start] === '\t';
|
|
665
665
|
}
|
|
666
|
-
|
|
667
|
-
const query = this.parseTerm(0, true);
|
|
666
|
+
parseQuadAnswers(id, query, line, accept) {
|
|
668
667
|
this.expect(TOK.DOT, '.');
|
|
669
668
|
this.advance();
|
|
670
669
|
|
|
@@ -684,28 +683,38 @@ class Parser {
|
|
|
684
683
|
source: { filename: this.filename, line },
|
|
685
684
|
});
|
|
686
685
|
}
|
|
686
|
+
parseQuad(id, line, accept) {
|
|
687
|
+
const query = this.parseTerm(0, true);
|
|
688
|
+
this.parseQuadAnswers(id, query, line, accept);
|
|
689
|
+
}
|
|
690
|
+
parseQuadTerm(term, line, accept) {
|
|
691
|
+
if (term.type !== COMPOUND || term.name !== '?-' || ![1, 2].includes(term.arity)) return false;
|
|
692
|
+
const id = term.arity === 2 ? term.args[0] : null;
|
|
693
|
+
const query = term.args[term.arity - 1];
|
|
694
|
+
this.parseQuadAnswers(id, query, line, accept);
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
687
697
|
parseProgram(emit = null) {
|
|
688
698
|
const clauses = emit ? null : [];
|
|
689
699
|
let clauseNumber = 0;
|
|
690
700
|
const accept = emit ?? ((clause) => clauses.push(clause));
|
|
691
701
|
while (this.token.type !== TOK.EOF) {
|
|
692
702
|
const line = this.token.line;
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
703
|
+
// In the normal EyeProlog profile, canonical functional ?-/1 and
|
|
704
|
+
// ?-/2 notation denotes the same quad marker as the corresponding
|
|
705
|
+
// operator notation. Keep the existing operator-form query parsing so
|
|
706
|
+
// a query containing comma remains wholly to the right of `?-`, while
|
|
707
|
+
// functional notation is parsed as a term and then decomposed by arity.
|
|
708
|
+
if (this.operatorTokenName() === '?-' && !this.strictIso) {
|
|
709
|
+
if (this.peek() === '(') {
|
|
710
|
+
const quadTerm = this.parseTerm(0, true);
|
|
711
|
+
if (!this.parseQuadTerm(quadTerm, line, accept)) {
|
|
712
|
+
throw new Error(`parse line ${line}: bad quad term`);
|
|
713
|
+
}
|
|
714
|
+
} else {
|
|
700
715
|
this.advance();
|
|
701
|
-
|
|
702
|
-
clauseNumber++;
|
|
703
|
-
if (this.sourceMetadata) clause.source = { filename: this.filename, line, clause: clauseNumber };
|
|
704
|
-
accept(clause);
|
|
705
|
-
continue;
|
|
716
|
+
this.parseQuad(null, line, accept);
|
|
706
717
|
}
|
|
707
|
-
this.advance();
|
|
708
|
-
this.parseQuad(null, line, accept);
|
|
709
718
|
continue;
|
|
710
719
|
}
|
|
711
720
|
if (this.token.type === TOK.IF) {
|
|
@@ -740,6 +749,13 @@ class Parser {
|
|
|
740
749
|
continue;
|
|
741
750
|
}
|
|
742
751
|
let head = this.parseTerm(3);
|
|
752
|
+
// Both a quad label and a TS 13211-3 semicontext may contain an
|
|
753
|
+
// unparenthesized comma before their priority-1200 operator. Assemble
|
|
754
|
+
// that left operand before deciding whether the marker is ?- or -->.
|
|
755
|
+
if (this.token.type === TOK.COMMA) {
|
|
756
|
+
this.advance();
|
|
757
|
+
head = compound(',', [head, this.parseTerm(3)]);
|
|
758
|
+
}
|
|
743
759
|
if (this.operatorTokenName() === '?-') {
|
|
744
760
|
if (this.strictIso) {
|
|
745
761
|
// There is no predefined infix ?-/2 in strict core mode. If a
|
|
@@ -761,13 +777,6 @@ class Parser {
|
|
|
761
777
|
this.parseQuad(head, line, accept);
|
|
762
778
|
continue;
|
|
763
779
|
}
|
|
764
|
-
// ISO/IEC TS 13211-3 grammar rules use -->/2 at the same top-level
|
|
765
|
-
// priority as clauses. The left side may include an unparenthesized
|
|
766
|
-
// semicontext: NonTerminal, Terminals --> Body.
|
|
767
|
-
if (this.token.type === TOK.COMMA) {
|
|
768
|
-
this.advance();
|
|
769
|
-
head = compound(',', [head, this.parseTerm(3)]);
|
|
770
|
-
}
|
|
771
780
|
if (this.operatorTokenName() === '-->') {
|
|
772
781
|
this.advance();
|
|
773
782
|
const grammarBody = this.parseTerm(0, true);
|
package/test/run-regression.mjs
CHANGED
|
@@ -221,6 +221,43 @@ why(
|
|
|
221
221
|
assertEqual(program.quads[1].query.args[1].name, '.', 'dot atom');
|
|
222
222
|
},
|
|
223
223
|
},
|
|
224
|
+
{
|
|
225
|
+
name: 'quad parser treats operator and functional ?- notation equivalently',
|
|
226
|
+
run: () => {
|
|
227
|
+
const labelled = Program.parse(
|
|
228
|
+
`0,passes
|
|
229
|
+
?- X = 1.
|
|
230
|
+
X = 1.
|
|
231
|
+
`,
|
|
232
|
+
);
|
|
233
|
+
assertEqual(labelled.quads.length, 1, 'labelled quad count');
|
|
234
|
+
assertEqual(labelled.clauses.length, 0, 'labelled quad clause count');
|
|
235
|
+
assertEqual(labelled.quads[0].id.name, ',', 'comma label functor');
|
|
236
|
+
assertEqual(labelled.quads[0].query.name, '=', 'labelled quad query');
|
|
237
|
+
const labelledReport = publicApi.runQuads(labelled);
|
|
238
|
+
assertEqual(labelledReport.stdout, 'quads: 1 run, 1 passed, 0 failed.\n', 'labelled quad report');
|
|
239
|
+
|
|
240
|
+
const functional = Program.parse(
|
|
241
|
+
`?-(','(0,passes),=(X,1)).
|
|
242
|
+
X = 1.
|
|
243
|
+
`,
|
|
244
|
+
);
|
|
245
|
+
assertEqual(functional.quads.length, 1, 'functional quad count');
|
|
246
|
+
assertEqual(functional.clauses.length, 0, 'functional quad clause count');
|
|
247
|
+
assertEqual(functional.quads[0].id.name, ',', 'functional comma label functor');
|
|
248
|
+
assertEqual(functional.quads[0].query.name, '=', 'functional quad query');
|
|
249
|
+
assertEqual(termToString(functional.quads[0].id), termToString(labelled.quads[0].id),
|
|
250
|
+
'operator and functional labels are equivalent');
|
|
251
|
+
assertEqual(termToString(functional.quads[0].query), termToString(labelled.quads[0].query),
|
|
252
|
+
'operator and functional queries are equivalent');
|
|
253
|
+
const functionalReport = publicApi.runQuads(functional);
|
|
254
|
+
assertEqual(functionalReport.stdout, 'quads: 1 run, 1 passed, 0 failed.\n', 'functional quad report');
|
|
255
|
+
|
|
256
|
+
const strict = Program.parse(`?-(','(0,passes),=(X,1)).\n`, { isoStrict: true });
|
|
257
|
+
assertEqual(strict.quads.length, 0, 'strict mode has no quads');
|
|
258
|
+
assertEqual(strict.clauses.length, 1, 'strict functional ?-/2 remains an ordinary term');
|
|
259
|
+
},
|
|
260
|
+
},
|
|
224
261
|
{
|
|
225
262
|
name: 'runQuads checks portable answer descriptions',
|
|
226
263
|
run: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5146,7 +5146,11 @@ contains `?-` at priority 1200 with specifier `fx`, so
|
|
|
5146
5146
|
an optional label before the query marker (`Label ?- Query.`), so while quad
|
|
5147
5147
|
syntax is supported it additionally exposes `?-` at priority 1200 with
|
|
5148
5148
|
specifier `xfx` as an implementation-specific operator. Consequently
|
|
5149
|
-
`current_op(Priority, Specifier, ?-)` enumerates both definitions.
|
|
5149
|
+
`current_op(Priority, Specifier, ?-)` enumerates both definitions. At top level in the normal EyeProlog profile, the quad marker is recognized
|
|
5150
|
+
after term parsing, so equivalent syntax stays equivalent: `Label ?- Query.`
|
|
5151
|
+
and canonical `?-(Label, Query).` denote the same labelled quad when followed
|
|
5152
|
+
by its indented answer descriptions. In `--iso-strict` mode this quad
|
|
5153
|
+
interpretation is disabled, and `?-/2` remains ordinary Prolog term syntax.
|
|
5150
5154
|
|
|
5151
5155
|
Run [`iso-dynamic-database.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-dynamic-database.pl)
|
|
5152
5156
|
for an explicitly stateful queue and
|
|
@@ -6361,7 +6365,10 @@ console.log(report.passed, report.failed, report.stdout);
|
|
|
6361
6365
|
The syntax follows the “queries using answer descriptions” convention used by
|
|
6362
6366
|
Trealla and the ISO Prolog working examples. Because answer descriptions are
|
|
6363
6367
|
layout-sensitive, indent every description while keeping ordinary clause heads
|
|
6364
|
-
and the next quad query at the left margin.
|
|
6368
|
+
and the next quad query at the left margin. A comma may be part of a quad label,
|
|
6369
|
+
including across layout before `?-`. Canonical functional notation is
|
|
6370
|
+
semantically equivalent: `?-(Label, Query).` followed by the same indented
|
|
6371
|
+
answer descriptions creates the same labelled quad as `Label ?- Query.`.
|
|
6365
6372
|
|
|
6366
6373
|
Statistics are comparative evidence, not a score in isolation. Preserve the
|
|
6367
6374
|
program, input, runtime version, selected query, answers, and counters together.
|