eyeprolog 1.2.14 → 1.2.15

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 CHANGED
@@ -79,10 +79,12 @@ member_test ?- member(X, [prolog, logic]).
79
79
  ; X = logic.
80
80
  ```
81
81
 
82
- A label may contain several comma-separated metadata fields. When a query has
83
- multiple indented answer descriptions, each description is checked and counted
84
- independently, so one failed expectation does not prevent the later ones from
85
- running.
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
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.2.14",
6
+ "version": "1.2.15",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
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
- // Both a quad label and a TS 13211-3 semicontext may contain one or more
799
- // unparenthesized commas before their priority-1200 operator. Parse the
800
- // complete comma sequence here instead of stopping after one separator;
801
- // portable quad labels commonly carry several metadata fields, e.g.
802
- // `9, "case", passes ?- Goal.`. Build the standard right-associative
803
- // comma term so this is the same label as `(9, "case", passes)`.
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
  }
@@ -279,8 +279,11 @@ why(
279
279
  },
280
280
  },
281
281
  {
282
- name: 'quad labels accept multiple metadata fields and each answer description is independent',
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 label comma');
302
- assertEqual(program.quads[0].id.args[1].name, ',', 'right-associated label comma');
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.
@@ -5170,8 +5170,11 @@ from the parsed `?-/1` or `?-/2` term rather than from one privileged surface
5170
5170
  spelling. Thus `Label ?- Query.`, `?-(Label, Query).`, mixed forms such as
5171
5171
  `?-((Label), Query).`, quoted-functor notation, and a parenthesized whole
5172
5172
  `(?-(Label, Query)).` denote the same quad when followed by indented answer
5173
- descriptions. In `--iso-strict` mode this quad interpretation is disabled, and
5174
- `?-/2` remains ordinary Prolog term syntax.
5173
+ descriptions. `Label` itself is parsed with the ordinary Prolog term grammar:
5174
+ there is no quad-specific comma or metadata syntax. The runner requires the
5175
+ resulting first argument to be ground; if it is not, that quad is reported as
5176
+ `BAD_ID` and later quads are still processed. In `--iso-strict` mode this quad
5177
+ interpretation is disabled, and `?-/2` remains ordinary Prolog term syntax.
5175
5178
 
5176
5179
  Run [`iso-dynamic-database.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-dynamic-database.pl)
5177
5180
  for an explicitly stateful queue and
@@ -6401,7 +6404,11 @@ colors ?- color(X).
6401
6404
  ```
6402
6405
 
6403
6406
  Run all quads in a file with `eyeprolog --quads file.pl` or `eyeprolog -q
6404
- file.pl`. A label such as `colors` is optional. Loading the file normally only
6407
+ file.pl`. A label such as `colors` is optional. A label is not a separate
6408
+ mini-language: it is the ordinary first argument of `(?-)/2`, and therefore may
6409
+ be any Prolog term admitted there by the normal term grammar. Quad execution
6410
+ requires that argument to be ground; a non-ground label is reported as a quad
6411
+ failure rather than aborting source parsing. Loading the file normally only
6405
6412
  records its quads; it does not execute them or add their queries and answers as
6406
6413
  program clauses. A quad run prints a summary and exits with status `1` when any
6407
6414
  description fails. Quad mode imports `library(prologue)` as a compatibility