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.
- package/conformance-report.md +2 -2
- package/package.json +1 -1
- package/src/parser.js +28 -8
- package/src/program.js +5 -2
- package/test/conformance/README.md +1 -1
- package/test/conformance/cases/iso/operator_atoms_as_arguments.pl +14 -0
- package/test/conformance/expected/iso/operator_atoms_as_arguments.pl +3 -0
- package/test/run-regression.mjs +36 -0
- package/the-art-of-eyeprolog.md +15 -3
package/conformance-report.md
CHANGED
|
@@ -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 |
|
|
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** | **
|
|
26
|
+
| **Total** | **481** | **260** | **19** | **21** | **781** |
|
package/package.json
CHANGED
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
|
-
['
|
|
81
|
-
['
|
|
82
|
-
['
|
|
83
|
-
['
|
|
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 [
|
|
105
|
-
|
|
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
|
|
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, ?-).
|
package/test/run-regression.mjs
CHANGED
|
@@ -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: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -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
|
|
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
|