eyeprolog 1.5.101 → 1.5.103
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/iso.js +15 -4
- package/src/parser.js +16 -0
- package/src/program.js +36 -1
- package/test/run-iso-strict.mjs +5 -0
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -674,10 +674,21 @@ function* termVariablesBuiltin({ goal, env }) {
|
|
|
674
674
|
}
|
|
675
675
|
|
|
676
676
|
function validPredicateIndicator(term) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
677
|
+
if (term.type !== COMPOUND || term.name !== '/' || term.arity !== 2) return false;
|
|
678
|
+
const [name, arity] = term.args;
|
|
679
|
+
// Name/Arity denotes a predicate indicator only when some instance of it
|
|
680
|
+
// could be one: Name an atom, Arity a non-negative integer. The same
|
|
681
|
+
// shared variable in both positions (current_predicate(X/X), issue #115)
|
|
682
|
+
// looks valid argument-by-argument -- each position individually accepts
|
|
683
|
+
// an unbound variable -- but no instantiation can satisfy both at once,
|
|
684
|
+
// since binding that one variable to an atom makes Arity an atom too, not
|
|
685
|
+
// an integer. stc#79 treats this as no valid instance existing at all,
|
|
686
|
+
// the same as a bare atom argument, so it must raise
|
|
687
|
+
// type_error(predicate_indicator, _) rather than just finding no matches.
|
|
688
|
+
if (name.type === VAR && arity.type === VAR && name.name === arity.name) return false;
|
|
689
|
+
return (name.type === VAR || name.type === ATOM) &&
|
|
690
|
+
(arity.type === VAR ||
|
|
691
|
+
(arity.type === NUMBER && isDecimalInteger(arity.name) && BigInt(arity.name) >= 0n));
|
|
681
692
|
}
|
|
682
693
|
|
|
683
694
|
const currentPredicateBuiltin = pendingBuiltin(currentPredicateSolutions);
|
package/src/parser.js
CHANGED
|
@@ -203,6 +203,21 @@ const CLPZ_OPERATOR_DEFINITIONS = [
|
|
|
203
203
|
[450, 'xfx', '..'],
|
|
204
204
|
];
|
|
205
205
|
|
|
206
|
+
// Names of bundled library(Name) modules whose own operators are pre-seeded
|
|
207
|
+
// into the live parser's operator table the moment `:- use_module(library(
|
|
208
|
+
// Name))` is parsed (applyImportedLibraryOperators below), rather than only
|
|
209
|
+
// once that module's own `:- op(...)` directives are themselves parsed. A
|
|
210
|
+
// library's clauses are parsed and processed in one pass (see
|
|
211
|
+
// loadSourceIntoBuilder in program.js), so without this a file that both
|
|
212
|
+
// imports one of these libraries and uses its custom operator syntax later
|
|
213
|
+
// in the very same source text would fail to parse. Any bundled library
|
|
214
|
+
// whose own source declares `:- op(...)` must be listed here, or it cannot
|
|
215
|
+
// safely be served from the prepared-clause cache in program.js: a cache
|
|
216
|
+
// hit replays already-parsed clause objects directly and never re-runs a
|
|
217
|
+
// live Parser over that library's text, so a cached op/3 directive from an
|
|
218
|
+
// unlisted library would never reach a live parser's operator table.
|
|
219
|
+
export const PRESEEDED_LIBRARY_OPERATOR_NAMES = new Set(['clpz', 'atts', 'tabling', 'debug']);
|
|
220
|
+
|
|
206
221
|
function operatorStrength(priority) {
|
|
207
222
|
return 1201 - priority;
|
|
208
223
|
}
|
|
@@ -421,6 +436,7 @@ class Parser {
|
|
|
421
436
|
const designation = directive.args[0];
|
|
422
437
|
if (designation?.type !== COMPOUND || designation.name !== 'library' || designation.arity !== 1 ||
|
|
423
438
|
designation.args[0]?.type !== ATOM) return;
|
|
439
|
+
if (!PRESEEDED_LIBRARY_OPERATOR_NAMES.has(designation.args[0].name)) return;
|
|
424
440
|
if (designation.args[0].name === 'clpz') {
|
|
425
441
|
for (const [priority, specifier, name] of CLPZ_OPERATOR_DEFINITIONS) {
|
|
426
442
|
this.defineOperator(priority, specifier, name);
|
package/src/program.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ISO_OPERATOR_DEFINITIONS,
|
|
7
7
|
PART2_OPERATOR_DEFINITIONS,
|
|
8
8
|
PART3_OPERATOR_DEFINITIONS,
|
|
9
|
+
PRESEEDED_LIBRARY_OPERATOR_NAMES,
|
|
9
10
|
QUAD_OPERATOR_DEFINITIONS,
|
|
10
11
|
createParserOperatorState,
|
|
11
12
|
parseClauses,
|
|
@@ -108,9 +109,43 @@ function hasDirectCutTailRecursion(group) {
|
|
|
108
109
|
return sawRecursive;
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
// library(clpz) alone pulls in about a dozen further bundled libraries
|
|
113
|
+
// (assoc, pairs, between, lists, atts, iso_ext, dcgs, terms, error, si,
|
|
114
|
+
// freeze, arithmetic, debug, format) via its own use_module/1-2 directives,
|
|
115
|
+
// each recursively loaded and prepared (parsed, DCG- and goal-expanded,
|
|
116
|
+
// dependency-analyzed) from scratch on every single program that needs
|
|
117
|
+
// clpz. Matching against every bundled filename, not just clpz.pl's own,
|
|
118
|
+
// lets each of those dependencies -- and every other bundled library used
|
|
119
|
+
// this way -- be prepared once per process and reused, the same as clpz.pl
|
|
120
|
+
// itself already was.
|
|
121
|
+
//
|
|
122
|
+
// A cache hit replays already-prepared clause objects directly (see below)
|
|
123
|
+
// and never re-runs a live Parser over that library's own source text. A
|
|
124
|
+
// library whose own source declares `:- op(...)` relies on exactly that
|
|
125
|
+
// live parse to install its operators into the importing file's operator
|
|
126
|
+
// table before the rest of that file is parsed (loadSourceIntoBuilder
|
|
127
|
+
// processes one whole source's clauses only after fully parsing it), so
|
|
128
|
+
// serving such a library from this cache would silently stop a sibling
|
|
129
|
+
// source term, later in the very same file as its use_module/1 directive,
|
|
130
|
+
// from parsing with that library's operator syntax. Only a library the
|
|
131
|
+
// parser separately pre-seeds regardless of caching
|
|
132
|
+
// (PRESEEDED_LIBRARY_OPERATOR_NAMES) is safe to include despite declaring
|
|
133
|
+
// its own operators.
|
|
134
|
+
const OWN_OPERATOR_DIRECTIVE = /:-(?:\s|\/\*[\s\S]*?\*\/)*op\s*\(/;
|
|
135
|
+
const bundledLibraryFilenames = new Set(
|
|
136
|
+
Array.from(standardLibrarySources.entries())
|
|
137
|
+
.filter(([name, entry]) => PRESEEDED_LIBRARY_OPERATOR_NAMES.has(name) || !OWN_OPERATOR_DIRECTIVE.test(entry.source))
|
|
138
|
+
.map(([, entry]) => entry.filename),
|
|
139
|
+
);
|
|
140
|
+
|
|
111
141
|
function preparedBundledLibraryCacheKey(program, options) {
|
|
112
142
|
const filename = String(options.filename ?? '');
|
|
113
|
-
if (
|
|
143
|
+
if (!bundledLibraryFilenames.has(filename)) return null;
|
|
144
|
+
// Strict ISO Part 1 skips the normal-profile clause preparation
|
|
145
|
+
// (expandClauseGoals, normalizeQualifiedClauseHead, DCG expansion) that
|
|
146
|
+
// this cache stores the result of, so a strict-mode load must never be
|
|
147
|
+
// served from -- or poison -- a normal-mode cache entry for the same file.
|
|
148
|
+
if (program.strictIso) return null;
|
|
114
149
|
// A user hook deliberately defined before use_module/1 is allowed to
|
|
115
150
|
// transform bundled source. Such a program needs its own ordinary expansion.
|
|
116
151
|
if (program.groups.has(modulePredicateKey('user', 'term_expansion', 2)) ||
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -1541,6 +1541,11 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
1541
1541
|
['clause(X,_)', 'instantiation_error', 'clause/2 variable head'],
|
|
1542
1542
|
['clause(4,_)', 'type_error(callable)', 'clause/2 head type'],
|
|
1543
1543
|
['current_predicate(4)', 'type_error(predicate_indicator)', 'current_predicate/1 indicator type'],
|
|
1544
|
+
// The same variable filling both Name and Arity (issue #115, stc#79)
|
|
1545
|
+
// looks valid position-by-position -- each alone accepts an unbound
|
|
1546
|
+
// variable -- but no instantiation can satisfy both at once, since
|
|
1547
|
+
// binding it to an atom makes Arity an atom too, not an integer.
|
|
1548
|
+
['current_predicate(X/X)', 'type_error(predicate_indicator)', 'current_predicate/1 aliased name/arity variable'],
|
|
1544
1549
|
['asserta(_)', 'instantiation_error', 'asserta/1 variable head'],
|
|
1545
1550
|
['asserta(4)', 'type_error(callable)', 'asserta/1 head type'],
|
|
1546
1551
|
['asserta((p:-4))', 'type_error(callable)', 'asserta/1 body conversion'],
|