eyeprolog 1.1.21 → 1.1.23

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.
@@ -11,7 +11,7 @@ This report summarizes the file-based conformance corpus under `test/conformance
11
11
  | builtins | 11 | 0 | 0 | 0 | 11 |
12
12
  | context | 11 | 0 | 0 | 0 | 11 |
13
13
  | control | 15 | 0 | 0 | 0 | 15 |
14
- | iso | 165 | 209 | 0 | 0 | 374 |
14
+ | iso | 166 | 209 | 0 | 0 | 375 |
15
15
  | lists | 52 | 3 | 0 | 0 | 55 |
16
16
  | modules | 2 | 0 | 0 | 0 | 2 |
17
17
  | negation | 8 | 0 | 19 | 0 | 27 |
@@ -23,4 +23,4 @@ This report summarizes the file-based conformance corpus under `test/conformance
23
23
  | terms | 26 | 3 | 0 | 0 | 29 |
24
24
  | unification | 18 | 0 | 0 | 0 | 18 |
25
25
  | variables | 16 | 9 | 0 | 0 | 25 |
26
- | **Total** | **480** | **260** | **19** | **21** | **780** |
26
+ | **Total** | **481** | **260** | **19** | **21** | **781** |
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.1.21",
6
+ "version": "1.1.23",
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
@@ -39,6 +39,7 @@ const graphicAtomChars = '#$&*+-./<=>@^~\\:';
39
39
  // canonical notation. Commas remain separators except inside parentheses.
40
40
  const INFIX_OPERATORS = new Map([
41
41
  [':-', { precedence: 1, associativity: 'none' }],
42
+ ['?-', { precedence: 1, associativity: 'none' }], // quad label extension
42
43
  ['-->', { precedence: 1, associativity: 'none' }],
43
44
  ['|', { precedence: 96, associativity: 'right' }],
44
45
  [';', { precedence: 101, associativity: 'right' }],
@@ -77,14 +78,15 @@ const INFIX_OPERATORS = new Map([
77
78
  ['^', { precedence: 1001, associativity: 'right' }],
78
79
  ]);
79
80
  const PREFIX_OPERATORS = new Map([
80
- ['\\+', 301],
81
- ['+', 1001],
82
- ['-', 1001],
83
- ['\\', 1001],
81
+ ['?-', { precedence: 1, strict: true }],
82
+ ['\\+', { precedence: 301, strict: false }],
83
+ ['+', { precedence: 1001, strict: false }],
84
+ ['-', { precedence: 1001, strict: false }],
85
+ ['\\', { precedence: 1001, strict: false }],
84
86
  ]);
85
87
 
86
88
  export const ISO_OPERATOR_DEFINITIONS = [
87
- [1200, 'xfx', ':-'], [1200, 'fx', ':-'], [1200, 'xfx', '-->'],
89
+ [1200, 'xfx', ':-'], [1200, 'fx', ':-'], [1200, 'fx', '?-'], [1200, 'xfx', '-->'],
88
90
  [1105, 'xfy', '|'],
89
91
  [1100, 'xfy', ';'], [1050, 'xfy', '->'], [1000, 'xfy', ','],
90
92
  [900, 'fy', '\\+'],
@@ -97,6 +99,13 @@ export const ISO_OPERATOR_DEFINITIONS = [
97
99
  [200, 'fy', '+'], [200, 'fy', '-'], [200, 'fy', '\\'],
98
100
  ];
99
101
 
102
+ // EyeProlog's embedded quad syntax permits an optional label before `?-`.
103
+ // That makes `?-` an implementation-specific xfx operator in addition to its
104
+ // ISO 1200 fx definition.
105
+ export const QUAD_OPERATOR_DEFINITIONS = [
106
+ [1200, 'xfx', '?-'],
107
+ ];
108
+
100
109
  const CLPZ_OPERATOR_DEFINITIONS = [
101
110
  [760, 'yfx', '#<==>'], [750, 'xfy', '#==>'], [750, 'yfx', '#<=='],
102
111
  [740, 'yfx', '#\\/'], [730, 'yfx', '#\\'], [720, 'yfx', '#/\\'],
@@ -133,9 +142,7 @@ function defineParserOperator(state, priority, specifier, name) {
133
142
  export function createParserOperatorState(definitions = [], includeDefaults = true) {
134
143
  const state = {
135
144
  infixOperators: includeDefaults ? new Map(INFIX_OPERATORS) : new Map(),
136
- prefixOperators: includeDefaults
137
- ? new Map([...PREFIX_OPERATORS].map(([name, precedence]) => [name, { precedence, strict: false }]))
138
- : new Map(),
145
+ prefixOperators: includeDefaults ? new Map(PREFIX_OPERATORS) : new Map(),
139
146
  postfixOperators: new Map(),
140
147
  };
141
148
  for (const definition of definitions) {
@@ -225,6 +232,11 @@ class Parser {
225
232
  }
226
233
  operatorTokenName(token = this.token) {
227
234
  if (token.type === TOK.ATOM) return token.text;
235
+ // `:-` has its own token because it also introduces clauses/directives,
236
+ // but ISO 6.3.3.1 still permits an operator atom as an argument. Treat the
237
+ // token as the ordinary operator name while parsing terms; the surrounding
238
+ // grammar decides whether it is operator notation or atom data.
239
+ if (token.type === TOK.IF) return ':-';
228
240
  if (token.type === TOK.STRING && this.parserFlagState.doubleQuotes === 'atom') return token.text;
229
241
  return null;
230
242
  }
@@ -533,6 +545,14 @@ class Parser {
533
545
  return left;
534
546
  }
535
547
  parsePrefixTerm(minPrecedence = 0, allowBar = true) {
548
+ // `:-` is tokenized specially so the program grammar can recognize clause
549
+ // and directive markers. In term argument position, however, ISO 6.3.3.1
550
+ // permits an operator atom directly as an `arg`; a leading `:-` cannot be
551
+ // prefix operator notation at argument priority, so it denotes the atom.
552
+ if (this.token.type === TOK.IF) {
553
+ this.advance();
554
+ return atom(':-');
555
+ }
536
556
  const operatorName = this.operatorTokenName();
537
557
  if (operatorName != null && this.prefixOperators.get(operatorName)?.precedence >= minPrecedence) {
538
558
  const op = operatorName;
package/src/program.js CHANGED
@@ -4,6 +4,7 @@ import { ATOM, COMPOUND, VAR, Env, atom, compound, deref, flattenConjunction, is
4
4
  import { formatTermForWrite } from './write.js';
5
5
  import {
6
6
  ISO_OPERATOR_DEFINITIONS,
7
+ QUAD_OPERATOR_DEFINITIONS,
7
8
  createParserOperatorState,
8
9
  parseClauses,
9
10
  parseClausesInto,
@@ -101,8 +102,10 @@ export class Program {
101
102
  this.moduleMetaPredicates = new Map();
102
103
  this.dynamicPredicates = new Set();
103
104
  this.operators = new Map();
104
- for (const [priority, specifier, name] of ISO_OPERATOR_DEFINITIONS) {
105
- this.defineOperator(priority, specifier, name);
105
+ for (const definitions of [ISO_OPERATOR_DEFINITIONS, QUAD_OPERATOR_DEFINITIONS]) {
106
+ for (const [priority, specifier, name] of definitions) {
107
+ this.defineOperator(priority, specifier, name);
108
+ }
106
109
  }
107
110
  this.initializations = [];
108
111
  this.quads = [];
@@ -94,7 +94,7 @@ Selected cases are adapted from the ISO and standard-core suites of Logtalk,
94
94
  Scryer Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
95
95
  are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
96
96
 
97
- The corpus has 374 cases in `iso/` and 780 file-based conformance cases in
97
+ The corpus has 375 cases in `iso/` and 781 file-based conformance cases in
98
98
  total. The generated `conformance-report.md` is the authoritative source for
99
99
  current category totals. Together with regression, documentation-sync, API,
100
100
  example, and book-example checks, `npm test` is the release gate.
@@ -0,0 +1,14 @@
1
+ % ISO 6.3.3.1: an arg may be an atom which is an operator.
2
+
3
+ %% goal: operator_atoms(Priority, Specifier, List)
4
+
5
+ operator_atoms(Priority, Specifier, List) :-
6
+ current_op(Priority, Specifier, :-),
7
+ List = [:-,-].
8
+
9
+ % ISO 6.3.4.4, Table 7: ?- is a predefined 1200 fx operator.
10
+
11
+ %% goal: query_prefix_operator(ok)
12
+
13
+ query_prefix_operator(ok) :-
14
+ current_op(1200, fx, ?-).
@@ -0,0 +1,3 @@
1
+ operator_atoms(1200, xfx, [':-', '-']).
2
+ operator_atoms(1200, fx, [':-', '-']).
3
+ query_prefix_operator(ok).
@@ -940,6 +940,42 @@ c4 ?- call((!;1)).
940
940
  assertEqual(program.findGroup('b', 0).clauses.length, 1, 'b/0 count');
941
941
  },
942
942
  },
943
+ {
944
+ name: 'ISO operator atoms are valid functional and list arguments',
945
+ run: () => {
946
+ const source = [
947
+ 'operator_argument(ok) :- current_op(1200, xfx, :-), [:-,-] = [:-,-].',
948
+ '',
949
+ ].join('\n');
950
+ assertEqual(run(source, { goal: 'operator_argument(ok)' }).stdout, 'operator_argument(ok).\n', 'operator argument syntax');
951
+ },
952
+ },
953
+ {
954
+ name: 'ISO query operator and quad infix extension are visible through current_op/3',
955
+ run: () => {
956
+ assertEqual(
957
+ run('', { goal: 'current_op(Priority, Specifier, ?-)' }).stdout,
958
+ "current_op(1200, fx, '?-').\ncurrent_op(1200, xfx, '?-').\n",
959
+ 'query operator definitions',
960
+ );
961
+ assertEqual(
962
+ run('', { goal: 'current_op(1200, fx, ?-)' }).stdout,
963
+ "current_op(1200, fx, '?-').\n",
964
+ 'ISO query prefix operator',
965
+ );
966
+ assertEqual(
967
+ run('', { goal: 'current_op(1200, xfx, ?-)' }).stdout,
968
+ "current_op(1200, xfx, '?-').\n",
969
+ 'quad query infix operator',
970
+ );
971
+ const prefix = parseGoalText('(?- true)');
972
+ assertEqual(prefix.name, '?-', 'prefix query functor');
973
+ assertEqual(prefix.arity, 1, 'prefix query arity');
974
+ const infix = parseGoalText('(label ?- true)');
975
+ assertEqual(infix.name, '?-', 'quad query functor');
976
+ assertEqual(infix.arity, 2, 'quad query arity');
977
+ },
978
+ },
943
979
  {
944
980
  name: 'term input keeps dotted operators intact and uses program operators',
945
981
  run: () => {
@@ -5120,7 +5120,17 @@ The fact is exactly `reports(sensor_7, temperature)`. Priority determines
5120
5120
  binding strength, and `fx`, `fy`, `xf`, `yf`, `xfx`, `xfy`, and `yfx`
5121
5121
  determine position and associativity. `current_op/3` inspects the table;
5122
5122
  `op(0, Specifier, Name)` removes a definition. Because declarations affect
5123
- parsing of subsequent text, place them before their first use.
5123
+ parsing of subsequent text, place them before their first use. ISO argument
5124
+ syntax also permits an atom that is currently an operator to appear directly
5125
+ as a functional argument or list element, so forms such as
5126
+ `current_op(Priority, Specifier, :-)` and `[:-,-]` are valid without quoting
5127
+ or parenthesizing those operator atoms. The ISO initial operator table also
5128
+ contains `?-` at priority 1200 with specifier `fx`, so
5129
+ `current_op(1200, fx, ?-)` succeeds. EyeProlog's embedded quad syntax permits
5130
+ an optional label before the query marker (`Label ?- Query.`), so while quad
5131
+ syntax is supported it additionally exposes `?-` at priority 1200 with
5132
+ specifier `xfx` as an implementation-specific operator. Consequently
5133
+ `current_op(Priority, Specifier, ?-)` enumerates both definitions.
5124
5134
 
5125
5135
  Run [`iso-dynamic-database.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-dynamic-database.pl)
5126
5136
  for an explicitly stateful queue and
@@ -5286,8 +5296,10 @@ clause ends in a period. The grammar above gives the canonical term shapes.
5286
5296
  The initial operator table contains the following ISO-style operators, all
5287
5297
  lowered to ordinary compound terms:
5288
5298
 
5289
- - prefix: `\+`, unary `+`, unary `-`, and `\`;
5299
+ - prefix: ISO `?-`, `\+`, unary `+`, unary `-`, and `\`;
5290
5300
  - control: `,`, `;`, and `->`;
5301
+ - quad syntax extension: `?-` is also a priority-1200 `xfx` operator so a
5302
+ label may precede a quad query;
5291
5303
  - grammar rules: `-->` and the Part 3 alternative `|`;
5292
5304
  - unification and comparison: `=`, `\=`, `==`, `\==`, `@<`, `@=<`, `@>`,
5293
5305
  `@>=`, `is`, `=:=`, `=\=`, `<`, `=<`, `>`, and `>=`;
@@ -6845,7 +6857,7 @@ node test/run-conformance-report.mjs
6845
6857
  ```
6846
6858
 
6847
6859
  The complete suite must pass before release. The file-based conformance corpus
6848
- contains 780 cases, including 374 focused ISO
6860
+ contains 781 cases, including 375 focused ISO
6849
6861
  cases derived from the success, failure, mode, and error behavior in
6850
6862
  ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
6851
6863
  Separate exact-output suites check 189 normal