eyeprolog 1.5.101 → 1.5.102

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.101",
6
+ "version": "1.5.102",
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
@@ -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 (filename !== standardLibrarySources.get('clpz')?.filename) return null;
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)) ||