eyeprolog 1.3.41 → 1.3.42
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 +12 -4
- package/examples/book/README.md +1 -1
- package/examples/book/chapter-38/02-look_ahead.pl +1 -1
- package/package.json +1 -1
- package/src/iso.js +5 -0
- package/src/parser.js +126 -61
- package/src/program.js +2 -2
- package/src/solver.js +2 -2
- package/src/standard-library.js +1 -1
- package/test/conformance/ISO-COMPLIANCE.md +24 -4
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +9 -8
- package/test/conformance/ISO-MATRIX.md +6 -4
- package/test/conformance/README.md +3 -4
- package/test/conformance/cases/iso/dcg_module_nonterminal_indicator.pl +1 -1
- package/test/conformance/expected/iso/exceptions_and_flags.pl +1 -1
- package/test/fixtures/README.md +7 -0
- package/test/run-iso-strict.mjs +20 -0
- package/test/run-regression.mjs +13 -6
- package/the-art-of-eyeprolog.md +38 -24
- package/why-eyeprolog.md +14 -13
package/README.md
CHANGED
|
@@ -221,10 +221,18 @@ newly upgraded cases run directly against the upstream Codex expectation.
|
|
|
221
221
|
EyeProlog does not yet claim independent certification or closure of every
|
|
222
222
|
normative Part 1 requirement.
|
|
223
223
|
|
|
224
|
-
##
|
|
224
|
+
## Module and definite clause grammar compatibility profiles
|
|
225
225
|
|
|
226
|
-
EyeProlog
|
|
227
|
-
`
|
|
226
|
+
Normal EyeProlog supports the widely used `module/2`, `use_module/1-2`,
|
|
227
|
+
`meta_predicate/1`, and `Module:Goal` interface reflected in later WG17 module
|
|
228
|
+
amendment work. This is a practical module compatibility profile; EyeProlog
|
|
229
|
+
does **not** currently claim a clause-by-clause implementation or certification
|
|
230
|
+
of the complete ISO/IEC 13211-2:2000 module model.
|
|
231
|
+
|
|
232
|
+
Definite clause grammar support follows the ISO/IEC TS 13211-3 grammar-rule and
|
|
233
|
+
`phrase/2-3` model and is exercised by the conformance corpus. As with Part 1,
|
|
234
|
+
that implementation evidence is not an independent certification of every
|
|
235
|
+
Part 3 requirement. For example:
|
|
228
236
|
|
|
229
237
|
```prolog
|
|
230
238
|
sentence --> [hello], noun.
|
|
@@ -254,7 +262,7 @@ EyeProlog also adds 128 public library predicate indicators to its 129-entry ISO
|
|
|
254
262
|
profile. **88 are implemented entirely as ordinary Prolog clauses** in focused
|
|
255
263
|
modules under `src/lib/`; the remaining control predicates and finite-domain
|
|
256
264
|
`library(clpz)` kernel use backtrackable host support.
|
|
257
|
-
They are
|
|
265
|
+
They are ordinary Prolog modules using EyeProlog's documented module compatibility surface, loaded explicitly by purpose, such as
|
|
258
266
|
`library(lists)`, `library(lambda)`, `library(strings)`, `library(aggregate)`, or
|
|
259
267
|
`library(clpz)`.
|
|
260
268
|
Portable text
|
package/examples/book/README.md
CHANGED
|
@@ -234,7 +234,7 @@ npm run generate
|
|
|
234
234
|
## Chapter 38: Language and ISO profile
|
|
235
235
|
|
|
236
236
|
- [01-city.pl](chapter-38/01-city.pl)
|
|
237
|
-
- [02-look_ahead.pl](chapter-38/02-look_ahead.pl) —
|
|
237
|
+
- [02-look_ahead.pl](chapter-38/02-look_ahead.pl) — Part 3-oriented definite clause grammars
|
|
238
238
|
|
|
239
239
|
## Chapter 39: Built-in predicates by programming role
|
|
240
240
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
% From The Art of EyeProlog, Chapter 38 —
|
|
1
|
+
% From The Art of EyeProlog, Chapter 38 — Part 3-oriented definite clause grammars.
|
|
2
2
|
look_ahead(X), [X] --> [X].
|
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -662,6 +662,11 @@ function* currentPrologFlagBuiltin({ solver, goal, env }) {
|
|
|
662
662
|
throw new PrologError('domain_error(prolog_flag)', flag);
|
|
663
663
|
}
|
|
664
664
|
for (const [name, definition] of solver.prologFlags) {
|
|
665
|
+
// ISO 7.11.1.1: when bounded=false, max_integer and min_integer have no
|
|
666
|
+
// current value and current_prolog_flag/2 must therefore not enumerate
|
|
667
|
+
// them. The definitions remain registered so attempts to change these
|
|
668
|
+
// non-changeable flags still receive the normal flag error handling.
|
|
669
|
+
if (definition.value == null) continue;
|
|
665
670
|
const next = env.clone();
|
|
666
671
|
if (unify(goal.args[0], atom(name), next) && unify(goal.args[1], definition.value, next)) yield next;
|
|
667
672
|
}
|
package/src/parser.js
CHANGED
|
@@ -207,7 +207,11 @@ class Parser {
|
|
|
207
207
|
this.strictIso = options.isoStrict === true;
|
|
208
208
|
this.parserFlagState = options.parserFlagState ?? {
|
|
209
209
|
doubleQuotes: options.doubleQuotes ?? 'chars',
|
|
210
|
+
charConversion: 'on',
|
|
211
|
+
charConversions: new Map(),
|
|
210
212
|
};
|
|
213
|
+
this.parserFlagState.charConversion ??= 'on';
|
|
214
|
+
this.parserFlagState.charConversions ??= new Map();
|
|
211
215
|
if (!['chars', 'codes', 'atom'].includes(this.parserFlagState.doubleQuotes)) {
|
|
212
216
|
throw new Error(`invalid double_quotes parser flag: ${this.parserFlagState.doubleQuotes}`);
|
|
213
217
|
}
|
|
@@ -223,6 +227,26 @@ class Parser {
|
|
|
223
227
|
this.previousToken = null;
|
|
224
228
|
this.token = this.nextToken();
|
|
225
229
|
}
|
|
230
|
+
convertCharacter(character) {
|
|
231
|
+
if (this.parserFlagState.charConversion !== 'on' || !character) return character;
|
|
232
|
+
return this.parserFlagState.charConversions.get(character) ?? character;
|
|
233
|
+
}
|
|
234
|
+
rawPeek(offset = 0) {
|
|
235
|
+
return this.source[this.pos + offset] ?? '';
|
|
236
|
+
}
|
|
237
|
+
rawTake() {
|
|
238
|
+
const ch = this.rawPeek();
|
|
239
|
+
if (ch) {
|
|
240
|
+
this.pos++;
|
|
241
|
+
if (ch === '\n') this.line++;
|
|
242
|
+
}
|
|
243
|
+
return ch;
|
|
244
|
+
}
|
|
245
|
+
convertedSlice(start, end) {
|
|
246
|
+
let out = '';
|
|
247
|
+
for (const ch of this.source.slice(start, end)) out += this.convertCharacter(ch);
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
226
250
|
terminatingFullStop(index = this.pos) {
|
|
227
251
|
// read/1 tries each possible full stop in turn. While parsing a later
|
|
228
252
|
// candidate, a preceding dot after a graphic character belongs to that
|
|
@@ -231,12 +255,12 @@ class Parser {
|
|
|
231
255
|
// designated read-term candidate.
|
|
232
256
|
if (this.readTermEnd != null) {
|
|
233
257
|
if (index === this.readTermEnd) return true;
|
|
234
|
-
if (index < this.readTermEnd && continuesGraphicToken(this.source, index)) {
|
|
235
|
-
const next = this.source[index + 1] ?? '';
|
|
258
|
+
if (index < this.readTermEnd && continuesGraphicToken(this.source, index, (ch) => this.convertCharacter(ch))) {
|
|
259
|
+
const next = this.convertCharacter(this.source[index + 1] ?? '');
|
|
236
260
|
if (next === '%' || /^[\u0009-\u000d\u0020]$/.test(next)) return false;
|
|
237
261
|
}
|
|
238
262
|
}
|
|
239
|
-
return isTerminatingFullStop(this.source, index);
|
|
263
|
+
return isTerminatingFullStop(this.source, index, (ch) => this.convertCharacter(ch));
|
|
240
264
|
}
|
|
241
265
|
defineOperator(priority, specifier, name) {
|
|
242
266
|
defineParserOperator(this, priority, specifier, name);
|
|
@@ -273,12 +297,30 @@ class Parser {
|
|
|
273
297
|
for (const name of names) this.defineOperator(priority, specifierTerm.name, name);
|
|
274
298
|
return true;
|
|
275
299
|
}
|
|
276
|
-
applyParserFlagDirective(directive) {
|
|
277
|
-
if (directive.type !== 'compound' || directive.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
300
|
+
applyParserFlagDirective(directive, line = this.line) {
|
|
301
|
+
if (directive.type !== 'compound' || directive.arity !== 2) return;
|
|
302
|
+
if (directive.name === 'set_prolog_flag') {
|
|
303
|
+
const [flag, value] = directive.args;
|
|
304
|
+
if (flag.type === 'atom' && flag.name === 'double_quotes' &&
|
|
305
|
+
value.type === 'atom' && ['chars', 'codes', 'atom'].includes(value.name)) {
|
|
306
|
+
this.parserFlagState.doubleQuotes = value.name;
|
|
307
|
+
} else if (flag.type === 'atom' && flag.name === 'char_conversion' &&
|
|
308
|
+
value.type === 'atom' && ['on', 'off'].includes(value.name)) {
|
|
309
|
+
this.parserFlagState.charConversion = value.name;
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (directive.name === 'char_conversion') {
|
|
314
|
+
const [input, output] = directive.args;
|
|
315
|
+
const validCharacter = (term) => term.type === 'atom' && Array.from(term.name).length === 1;
|
|
316
|
+
if (input.type === 'var' || output.type === 'var') {
|
|
317
|
+
throw new Error(`parse line ${line}: char_conversion/2 arguments must be instantiated characters`);
|
|
318
|
+
}
|
|
319
|
+
if (!validCharacter(input) || !validCharacter(output)) {
|
|
320
|
+
throw new Error(`parse line ${line}: char_conversion/2 requires one-character atoms`);
|
|
321
|
+
}
|
|
322
|
+
if (input.name === output.name) this.parserFlagState.charConversions.delete(input.name);
|
|
323
|
+
else this.parserFlagState.charConversions.set(input.name, output.name);
|
|
282
324
|
}
|
|
283
325
|
}
|
|
284
326
|
applyImportedLibraryOperators(directive) {
|
|
@@ -301,46 +343,45 @@ class Parser {
|
|
|
301
343
|
return null;
|
|
302
344
|
}
|
|
303
345
|
peek(offset = 0) {
|
|
304
|
-
return this.
|
|
346
|
+
return this.convertCharacter(this.rawPeek(offset));
|
|
305
347
|
}
|
|
306
348
|
take() {
|
|
307
|
-
const
|
|
308
|
-
|
|
349
|
+
const raw = this.rawPeek();
|
|
350
|
+
const ch = this.convertCharacter(raw);
|
|
351
|
+
if (raw) {
|
|
309
352
|
this.pos++;
|
|
310
|
-
if (
|
|
353
|
+
if (raw === '\n') this.line++;
|
|
311
354
|
}
|
|
312
355
|
return ch;
|
|
313
356
|
}
|
|
314
357
|
skipWhitespaceAndComments() {
|
|
315
|
-
const source = this.source;
|
|
316
|
-
const len = source.length;
|
|
317
358
|
while (true) {
|
|
318
|
-
while (this.
|
|
319
|
-
const
|
|
320
|
-
if (!isWhitespaceCode(
|
|
321
|
-
|
|
322
|
-
this.pos++;
|
|
359
|
+
while (this.rawPeek()) {
|
|
360
|
+
const ch = this.peek();
|
|
361
|
+
if (!isWhitespaceCode(ch.charCodeAt(0))) break;
|
|
362
|
+
this.take();
|
|
323
363
|
}
|
|
324
|
-
if (
|
|
325
|
-
while (this.
|
|
364
|
+
if (this.peek() === '%') {
|
|
365
|
+
while (this.rawPeek() && this.peek() !== '\n') this.take();
|
|
326
366
|
continue;
|
|
327
367
|
}
|
|
328
|
-
if (
|
|
368
|
+
if (this.peek() === '/' && this.peek(1) === '*') {
|
|
329
369
|
const line = this.line;
|
|
330
|
-
this.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
this.pos += 2;
|
|
370
|
+
this.take();
|
|
371
|
+
this.take();
|
|
372
|
+
while (this.rawPeek() && !(this.peek() === '*' && this.peek(1) === '/')) this.take();
|
|
373
|
+
if (!this.rawPeek()) throw new Error(`parse line ${line}: unterminated block comment`);
|
|
374
|
+
this.take();
|
|
375
|
+
this.take();
|
|
337
376
|
continue;
|
|
338
377
|
}
|
|
339
378
|
break;
|
|
340
379
|
}
|
|
341
380
|
}
|
|
342
381
|
readEscape(line, options = {}) {
|
|
343
|
-
const
|
|
382
|
+
const takeChar = () => options.raw ? this.rawTake() : this.take();
|
|
383
|
+
const peekChar = () => options.raw ? this.rawPeek() : this.peek();
|
|
384
|
+
const escaped = takeChar();
|
|
344
385
|
if (!escaped) throw new Error(`parse line ${line}: unterminated escape sequence`);
|
|
345
386
|
|
|
346
387
|
// ISO 6.4.2 permits a continuation escape only inside quoted tokens: a
|
|
@@ -350,9 +391,9 @@ class Parser {
|
|
|
350
391
|
if (options.allowContinuation !== false) return '';
|
|
351
392
|
throw new Error(`parse line ${line}: bad escape sequence`);
|
|
352
393
|
}
|
|
353
|
-
if (escaped === '\r' &&
|
|
394
|
+
if (escaped === '\r' && peekChar() === '\n') {
|
|
354
395
|
if (options.allowContinuation !== false) {
|
|
355
|
-
|
|
396
|
+
takeChar();
|
|
356
397
|
return '';
|
|
357
398
|
}
|
|
358
399
|
throw new Error(`parse line ${line}: bad escape sequence`);
|
|
@@ -363,8 +404,8 @@ class Parser {
|
|
|
363
404
|
|
|
364
405
|
if (escaped === 'x') {
|
|
365
406
|
let digits = '';
|
|
366
|
-
while (/^[0-9A-Fa-f]$/.test(
|
|
367
|
-
if (!digits ||
|
|
407
|
+
while (/^[0-9A-Fa-f]$/.test(peekChar())) digits += takeChar();
|
|
408
|
+
if (!digits || takeChar() !== '\\') throw new Error(`parse line ${line}: bad hexadecimal escape`);
|
|
368
409
|
const code = Number.parseInt(digits, 16);
|
|
369
410
|
if (code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) {
|
|
370
411
|
throw new Error(`parse line ${line}: character escape out of range`);
|
|
@@ -373,8 +414,8 @@ class Parser {
|
|
|
373
414
|
}
|
|
374
415
|
if (/^[0-7]$/.test(escaped)) {
|
|
375
416
|
let digits = escaped;
|
|
376
|
-
while (/^[0-7]$/.test(
|
|
377
|
-
if (
|
|
417
|
+
while (/^[0-7]$/.test(peekChar())) digits += takeChar();
|
|
418
|
+
if (takeChar() !== '\\') throw new Error(`parse line ${line}: bad octal escape`);
|
|
378
419
|
const code = Number.parseInt(digits, 8);
|
|
379
420
|
if (code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) {
|
|
380
421
|
throw new Error(`parse line ${line}: character escape out of range`);
|
|
@@ -411,7 +452,7 @@ class Parser {
|
|
|
411
452
|
this.take();
|
|
412
453
|
while (isGraphicAtomCode(this.peek().charCodeAt(0)) &&
|
|
413
454
|
!this.terminatingFullStop()) this.take();
|
|
414
|
-
return { type: TOK.ATOM, text: this.
|
|
455
|
+
return { type: TOK.ATOM, text: this.convertedSlice(start, this.pos), line };
|
|
415
456
|
}
|
|
416
457
|
if (ch === '!') {
|
|
417
458
|
this.take();
|
|
@@ -442,20 +483,29 @@ class Parser {
|
|
|
442
483
|
}
|
|
443
484
|
|
|
444
485
|
if (ch === '"' || ch === "'") {
|
|
486
|
+
// Whether an input character is quoted is determined from the target
|
|
487
|
+
// source before character conversion (ISO 8.14.1 note 1). A literal
|
|
488
|
+
// quote therefore protects its contents from Convc; if a conversion
|
|
489
|
+
// itself produces a quote token, the following raw characters remain
|
|
490
|
+
// subject to conversion while that resulting token is parsed.
|
|
491
|
+
const rawOpening = this.rawPeek();
|
|
445
492
|
const quote = this.take();
|
|
493
|
+
const literalQuote = rawOpening === quote;
|
|
494
|
+
const peekQuoted = () => literalQuote ? this.rawPeek() : this.peek();
|
|
495
|
+
const takeQuoted = () => literalQuote ? this.rawTake() : this.take();
|
|
446
496
|
let text = '';
|
|
447
497
|
while (true) {
|
|
448
|
-
if (!
|
|
449
|
-
let value =
|
|
498
|
+
if (!peekQuoted()) throw new Error(`parse line ${line}: unterminated quoted term`);
|
|
499
|
+
let value = takeQuoted();
|
|
450
500
|
if (value === quote) {
|
|
451
|
-
if (
|
|
452
|
-
|
|
501
|
+
if (peekQuoted() === quote) {
|
|
502
|
+
takeQuoted();
|
|
453
503
|
value = quote;
|
|
454
504
|
} else {
|
|
455
505
|
break;
|
|
456
506
|
}
|
|
457
|
-
} else if (value === '\\' &&
|
|
458
|
-
value = this.readEscape(line);
|
|
507
|
+
} else if (value === '\\' && peekQuoted()) {
|
|
508
|
+
value = this.readEscape(line, { raw: literalQuote });
|
|
459
509
|
} else if (value !== ' ' && isWhitespaceCode(value.charCodeAt(0))) {
|
|
460
510
|
// ISO 6.4.2.1 allows an ordinary space in a quoted character, but
|
|
461
511
|
// not literal layout characters such as tab or newline. Newlines
|
|
@@ -489,17 +539,20 @@ class Parser {
|
|
|
489
539
|
(this.peek(2) !== "'" || this.peek(3) === "'") &&
|
|
490
540
|
!(this.peek(2) === '\\' && this.peek(3) === '\n');
|
|
491
541
|
if (startsQuotedCharacter) {
|
|
542
|
+
const rawQuotedCharacter = this.rawPeek() === '0' && this.rawPeek(1) === "'";
|
|
492
543
|
this.take();
|
|
493
544
|
this.take();
|
|
494
|
-
|
|
545
|
+
const takeCharacter = () => rawQuotedCharacter ? this.rawTake() : this.take();
|
|
546
|
+
const peekCharacter = () => rawQuotedCharacter ? this.rawPeek() : this.peek();
|
|
547
|
+
let value = takeCharacter();
|
|
495
548
|
if (value) {
|
|
496
549
|
const firstCode = value.charCodeAt(0);
|
|
497
550
|
if (firstCode >= 0xd800 && firstCode <= 0xdbff) {
|
|
498
|
-
const secondCode =
|
|
551
|
+
const secondCode = peekCharacter().charCodeAt(0);
|
|
499
552
|
if (secondCode < 0xdc00 || secondCode > 0xdfff) {
|
|
500
553
|
throw new Error(`parse line ${line}: bad character code constant`);
|
|
501
554
|
}
|
|
502
|
-
value +=
|
|
555
|
+
value += takeCharacter();
|
|
503
556
|
} else if (firstCode >= 0xdc00 && firstCode <= 0xdfff) {
|
|
504
557
|
throw new Error(`parse line ${line}: bad character code constant`);
|
|
505
558
|
}
|
|
@@ -512,10 +565,10 @@ class Parser {
|
|
|
512
565
|
// apostrophe is doubled just as it is inside a quoted atom. Thus
|
|
513
566
|
// 0''' is one numeric token denoting character code 39, while the
|
|
514
567
|
// undoubled 0'' is not a complete single quoted character.
|
|
515
|
-
if (
|
|
516
|
-
|
|
568
|
+
if (peekCharacter() !== "'") throw new Error(`parse line ${line}: bad character code constant`);
|
|
569
|
+
takeCharacter();
|
|
517
570
|
} else if (value === '\\') {
|
|
518
|
-
value = this.readEscape(line, { allowContinuation: false });
|
|
571
|
+
value = this.readEscape(line, { allowContinuation: false, raw: rawQuotedCharacter });
|
|
519
572
|
}
|
|
520
573
|
const code = value.codePointAt(0);
|
|
521
574
|
return { type: TOK.NUMBER, text: String(negative ? -code : code), line };
|
|
@@ -550,14 +603,14 @@ class Parser {
|
|
|
550
603
|
// followed by the name E9 and is not a valid term without an operator.
|
|
551
604
|
if (hasFraction && (this.peek() === 'e' || this.peek() === 'E')) {
|
|
552
605
|
let idx = this.pos + 1;
|
|
553
|
-
if (
|
|
554
|
-
if (isDigitCode((this.source[idx] ?? '').charCodeAt(0))) {
|
|
606
|
+
if (['+', '-'].includes(this.convertCharacter(this.source[idx] ?? ''))) idx++;
|
|
607
|
+
if (isDigitCode(this.convertCharacter(this.source[idx] ?? '').charCodeAt(0))) {
|
|
555
608
|
this.take();
|
|
556
609
|
if (this.peek() === '+' || this.peek() === '-') this.take();
|
|
557
610
|
while (isDigitCode(this.peek().charCodeAt(0))) this.take();
|
|
558
611
|
}
|
|
559
612
|
}
|
|
560
|
-
let text = this.
|
|
613
|
+
let text = this.convertedSlice(start, this.pos);
|
|
561
614
|
if (!hasFraction) text = BigInt(text).toString();
|
|
562
615
|
else text = finiteFloatTokenText(text);
|
|
563
616
|
return { type: TOK.NUMBER, text, line };
|
|
@@ -567,7 +620,7 @@ class Parser {
|
|
|
567
620
|
const start = this.pos;
|
|
568
621
|
this.take();
|
|
569
622
|
while (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
|
|
570
|
-
const text = this.
|
|
623
|
+
const text = this.convertedSlice(start, this.pos);
|
|
571
624
|
return { type: TOK.VAR, text, line };
|
|
572
625
|
}
|
|
573
626
|
|
|
@@ -575,7 +628,7 @@ class Parser {
|
|
|
575
628
|
const start = this.pos;
|
|
576
629
|
this.take();
|
|
577
630
|
while (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
|
|
578
|
-
return { type: TOK.ATOM, text: this.
|
|
631
|
+
return { type: TOK.ATOM, text: this.convertedSlice(start, this.pos), line };
|
|
579
632
|
}
|
|
580
633
|
|
|
581
634
|
if (isGraphicAtomCode(ch.charCodeAt(0))) {
|
|
@@ -583,7 +636,7 @@ class Parser {
|
|
|
583
636
|
this.take();
|
|
584
637
|
while (isGraphicAtomCode(this.peek().charCodeAt(0)) &&
|
|
585
638
|
!this.terminatingFullStop()) this.take();
|
|
586
|
-
return { type: TOK.ATOM, text: this.
|
|
639
|
+
return { type: TOK.ATOM, text: this.convertedSlice(start, this.pos), line };
|
|
587
640
|
}
|
|
588
641
|
|
|
589
642
|
throw new Error(`parse line ${line}: bad character ${JSON.stringify(ch)}`);
|
|
@@ -934,7 +987,7 @@ class Parser {
|
|
|
934
987
|
throw new Error(`parse line ${line}: bad term`);
|
|
935
988
|
}
|
|
936
989
|
this.expect(TOK.DOT, '.');
|
|
937
|
-
this.applyParserFlagDirective(directive);
|
|
990
|
+
this.applyParserFlagDirective(directive, line);
|
|
938
991
|
this.applyImportedLibraryOperators(directive);
|
|
939
992
|
this.advance();
|
|
940
993
|
const clause = { head: compound(':-', [directive]), body: [] };
|
|
@@ -1080,12 +1133,16 @@ export function parseClauses(source, options = {}) {
|
|
|
1080
1133
|
const ownsParserFlagState = options.parserFlagState == null;
|
|
1081
1134
|
const initialDoubleQuotes = options.doubleQuotes ?? 'chars';
|
|
1082
1135
|
const parserOptions = ownsParserFlagState
|
|
1083
|
-
? { ...options, parserFlagState: { doubleQuotes: initialDoubleQuotes } }
|
|
1136
|
+
? { ...options, parserFlagState: { doubleQuotes: initialDoubleQuotes, charConversion: 'on', charConversions: new Map() } }
|
|
1084
1137
|
: options;
|
|
1085
1138
|
if (options.sourceMetadata === false && options.readTermEnd == null) {
|
|
1086
1139
|
const clauses = parseClausesFastNoSource(source, null, null, parserOptions);
|
|
1087
1140
|
if (clauses) return clauses;
|
|
1088
|
-
if (ownsParserFlagState)
|
|
1141
|
+
if (ownsParserFlagState) {
|
|
1142
|
+
parserOptions.parserFlagState.doubleQuotes = initialDoubleQuotes;
|
|
1143
|
+
parserOptions.parserFlagState.charConversion = 'on';
|
|
1144
|
+
parserOptions.parserFlagState.charConversions.clear();
|
|
1145
|
+
}
|
|
1089
1146
|
}
|
|
1090
1147
|
return new Parser(source, parserOptions).parseProgram();
|
|
1091
1148
|
}
|
|
@@ -1381,11 +1438,19 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1381
1438
|
return head && bodyGoal ? { head, body: [bodyGoal] } : null;
|
|
1382
1439
|
};
|
|
1383
1440
|
|
|
1441
|
+
const preparationConversionActive = () =>
|
|
1442
|
+
options.parserFlagState?.charConversion === 'on' &&
|
|
1443
|
+
(options.parserFlagState?.charConversions?.size ?? 0) > 0;
|
|
1444
|
+
|
|
1384
1445
|
const flush = () => {
|
|
1385
1446
|
const text = chunk.trim();
|
|
1386
1447
|
chunk = '';
|
|
1387
1448
|
if (!text) return true;
|
|
1388
|
-
|
|
1449
|
+
// Once a preparation-time char_conversion/2 mapping is active, every
|
|
1450
|
+
// subsequent source character must pass through the full tokenizer. The
|
|
1451
|
+
// compact parser deliberately operates on raw source ranges, so using it
|
|
1452
|
+
// here would silently bypass Convc for otherwise simple clauses.
|
|
1453
|
+
const simple = preparationConversionActive() ? null : parseSimple(text);
|
|
1389
1454
|
if (simple) {
|
|
1390
1455
|
accept(simple);
|
|
1391
1456
|
return true;
|
|
@@ -1408,7 +1473,7 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null, option
|
|
|
1408
1473
|
if (contentEnd > contentStart && source.charCodeAt(contentEnd - 1) === 13) contentEnd--;
|
|
1409
1474
|
[contentStart, contentEnd] = trimRange(source, contentStart, contentEnd);
|
|
1410
1475
|
if (contentStart < contentEnd && source.charCodeAt(contentStart) !== 37) {
|
|
1411
|
-
if (!chunk && source.charCodeAt(contentEnd - 1) === 46) {
|
|
1476
|
+
if (!chunk && source.charCodeAt(contentEnd - 1) === 46 && !preparationConversionActive()) {
|
|
1412
1477
|
if (emitFastBinaryRange(source, contentStart, contentEnd)) {
|
|
1413
1478
|
// The program builder accepted a compact binary clause directly.
|
|
1414
1479
|
} else {
|
package/src/program.js
CHANGED
|
@@ -650,7 +650,7 @@ export function autoloadProgramGoals(program, inputs, options = {}) {
|
|
|
650
650
|
false,
|
|
651
651
|
{ isoStrict: program.strictIso },
|
|
652
652
|
);
|
|
653
|
-
const parserFlagState = { doubleQuotes: program.doubleQuotes ?? options.doubleQuotes ?? 'chars' };
|
|
653
|
+
const parserFlagState = { doubleQuotes: program.doubleQuotes ?? options.doubleQuotes ?? 'chars', charConversion: 'on', charConversions: new Map() };
|
|
654
654
|
const goals = parseInteropGoalInputs(inputs, {
|
|
655
655
|
...options,
|
|
656
656
|
isoStrict: program.strictIso,
|
|
@@ -684,7 +684,7 @@ function loadSourcesIntoBuilder(builder, sources, options, fast) {
|
|
|
684
684
|
const ensured = new Set();
|
|
685
685
|
const loadedModules = new Set();
|
|
686
686
|
const operatorState = createParserOperatorState([], true, { isoStrict: options.isoStrict === true });
|
|
687
|
-
const parserFlagState = { doubleQuotes: options.doubleQuotes ?? 'chars' };
|
|
687
|
+
const parserFlagState = { doubleQuotes: options.doubleQuotes ?? 'chars', charConversion: 'on', charConversions: new Map() };
|
|
688
688
|
const prepared = sources.map((source) => ({
|
|
689
689
|
source,
|
|
690
690
|
options: { ...sourceOptionsFor(source, options), operatorState, parserFlagState },
|
package/src/solver.js
CHANGED
|
@@ -955,8 +955,8 @@ function defaultPrologFlags(unknown = 'error', strictIso = false) {
|
|
|
955
955
|
['integer_rounding_function', { value: compound('toward_zero', []), allowed: ['toward_zero'], changeable: false }],
|
|
956
956
|
['char_conversion', { value: compound('on', []), allowed: ['on', 'off'], changeable: true }],
|
|
957
957
|
['debug', { value: compound('off', []), allowed: ['on', 'off'], changeable: true }],
|
|
958
|
-
['max_integer', { value:
|
|
959
|
-
['min_integer', { value:
|
|
958
|
+
['max_integer', { value: null, allowed: ['unbounded'], changeable: false }],
|
|
959
|
+
['min_integer', { value: null, allowed: ['unbounded'], changeable: false }],
|
|
960
960
|
['max_arity', { value: compound('unbounded', []), allowed: ['unbounded'], changeable: false }],
|
|
961
961
|
['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
|
|
962
962
|
['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
|
package/src/standard-library.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Prolog library modules shipped with EyeProlog and loaded through its documented module compatibility surface.
|
|
2
2
|
// The sources are registered here so library(Name) works in Node and browsers.
|
|
3
3
|
// Modules are loaded on demand by explicit use_module/1-2 or by the conservative
|
|
4
4
|
// source-level interop autoloader declared below.
|
|
@@ -32,13 +32,13 @@ error-ordering alternative to an individual executable assertion.
|
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| Clause 6 — tokens, terms, lists, operators, quoted text | audit | Complete vendored WG17 syntax matrix, `lexical_and_curly_terms`, `scryer_lexical_terms`, operator suites, syntax-error cases, quoted-layout/escape error cases, and writer/read-back regressions. |
|
|
34
34
|
| 7.1-7.3 — term types, term order, unification | audit | Standard-order, identity, finite-tree and occurs-check suites, Corrigendum 2 term predicates. |
|
|
35
|
-
| 7.4 — Prolog text and directives | audit | All Part 1 directive indicators are parsed; include/ensure-loaded/operator/flag/character-conversion behavior has executable coverage. Cross-text `multifile/1` and ordering constraints require explicit shall-by-shall audit. |
|
|
35
|
+
| 7.4 — Prolog text and directives | audit | All Part 1 directive indicators are parsed; include/ensure-loaded/operator/flag/character-conversion behavior has executable coverage. Preparation-time `char_conversion/2` now affects later unquoted source text and respects `char_conversion=off`. Cross-text `multifile/1` and ordering constraints still require explicit shall-by-shall audit. |
|
|
36
36
|
| 7.5-7.6 — database and term/clause conversion | audit | Dynamic database and logical-update-view suites. Strict mode restores Part 1 private-static/public-dynamic `clause/2` access. Public/private and multi-text requirements still need complete mapping. |
|
|
37
37
|
| 7.7 — execution and backtracking | audit | Control/search suites. Strict mode disables EyeProlog automatic tabling, cycle guards, and recursive numeric shortcuts so core execution uses ordinary clause selection/backtracking. |
|
|
38
38
|
| 7.8 — control constructs and exceptions | audit | call, cut, conjunction, disjunction (including failed branches after callee-local cuts), if-then, catch/throw, renamed-copy tests. |
|
|
39
39
|
| 7.9 — expression evaluation | audit | Arithmetic/evaluation/error suites, including Corrigenda. Exceptional-value/error-precedence matrix remains to be exhaustively enumerated. |
|
|
40
40
|
| 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, Corrigendum 3 writer cases. Full option cross-product remains open. |
|
|
41
|
-
| 7.11 — flags | audit | Required Part 1 flags implemented. Normal and strict modes use the ISO `unknown=error` default; strict mode excludes the EyeProlog `occurs_check` extension. |
|
|
41
|
+
| 7.11 — flags | audit | Required Part 1 flags implemented. Normal and strict modes use the ISO `unknown=error` default; strict mode excludes the EyeProlog `occurs_check` extension. With `bounded=false`, `max_integer` and `min_integer` correctly have no current value. |
|
|
42
42
|
| 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases. Complete prescribed-error ordering remains open. |
|
|
43
43
|
| 8.2-8.17 — built-in predicates | audit | Predicate-family coverage is mapped in ISO-MATRIX.md; Corrigendum 2 additions (`subsumes_term/2`, `term_variables/2`, `call/2..8`, `false/0`) are in the strict registry. Mode/error matrix is not yet one-row-per-standard-row. |
|
|
44
44
|
| Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Host floating-point representation choices remain documented implementation-defined behavior. |
|
|
@@ -46,6 +46,24 @@ error-ordering alternative to an individual executable assertion.
|
|
|
46
46
|
| Corrigendum 2 | covered | Added predicates/functors, catch corrections, bar/operator and uninstantiation corrections have dedicated cases. |
|
|
47
47
|
| Corrigendum 3 | covered | Writer options, `variable_names/1`, canonical list output and negative-power corrections have dedicated cases. |
|
|
48
48
|
|
|
49
|
+
## Issue #65 conformance corrections
|
|
50
|
+
|
|
51
|
+
The issue #65 audit against the licensed Part 1 text and Corrigenda closed two
|
|
52
|
+
concrete mismatches without changing the remaining audit rows into blanket
|
|
53
|
+
conformance claims:
|
|
54
|
+
|
|
55
|
+
- `bounded=false` no longer exposes implementation-specific `unbounded` values
|
|
56
|
+
for `max_integer` or `min_integer`; the corresponding
|
|
57
|
+
`current_prolog_flag/2` queries fail as specified by 7.11.1.1;
|
|
58
|
+
- preparation-time `char_conversion/2` now converts later unquoted source text
|
|
59
|
+
when the `char_conversion` flag is `on`, leaves quoted characters unchanged,
|
|
60
|
+
and feeds the same mapping into execution-time term input.
|
|
61
|
+
|
|
62
|
+
The processor-character-set/collation rows in
|
|
63
|
+
[ISO-IMPLEMENTATION-DEFINED.md](ISO-IMPLEMENTATION-DEFINED.md) remain explicit
|
|
64
|
+
`audit gap`s. They were not papered over by narrowing strict mode without full
|
|
65
|
+
WG17 and read/write/error validation.
|
|
66
|
+
|
|
49
67
|
## Strict-core boundary
|
|
50
68
|
|
|
51
69
|
`isoStrict: true` is intentionally a **Part 1 + Corrigenda 1-3** mode. It does
|
|
@@ -64,9 +82,11 @@ remain ordinary operator syntax in strict core mode. A conforming `op/3`
|
|
|
64
82
|
directive may still add an infix `?-` definition; strict mode reads that as an
|
|
65
83
|
ordinary term rather than as a quad.
|
|
66
84
|
|
|
67
|
-
|
|
85
|
+
Module and DCG compatibility features remain supported and tested in the
|
|
68
86
|
normal EyeProlog profile. They are tracked separately in ISO-MATRIX.md rather
|
|
69
|
-
than being silently folded into the Part 1 strict-core claim.
|
|
87
|
+
than being silently folded into the Part 1 strict-core claim. The project does
|
|
88
|
+
not currently assert that this evidence closes every requirement of ISO/IEC
|
|
89
|
+
13211-2:2000 or ISO/IEC TS 13211-3.
|
|
70
90
|
|
|
71
91
|
## Release gate
|
|
72
92
|
|
|
@@ -35,7 +35,7 @@ Status values are:
|
|
|
35
35
|
| 7.1.2.2 | Mapping between a character code and bytes | Text file streams decode and encode UTF-8. Binary streams expose bytes 0..255 directly. | **defined** — `src/io.js`. |
|
|
36
36
|
| 7.1.4.1 | Set `C` of characters represented by one-char atoms | Character predicates accept Unicode scalar values U+0000..U+10FFFF excluding surrogate code points. | **defined** — `src/iso.js` character-code validation. |
|
|
37
37
|
| 7.4.2.4 | Whether `op/3` directives affect other Prolog texts or execution | An `op/3` directive changes parsing of subsequent text loaded into the same `Program`; the resulting operator table is also used by execution-time term I/O. Separately created `Program` objects are independent. | **defined** — `src/parser.js`, `src/program.js`, `src/iso.js`. |
|
|
38
|
-
| 7.4.2.5 | Whether directive-created `Convc` affects other text/execution |
|
|
38
|
+
| 7.4.2.5 | Whether directive-created `Convc` affects other text/execution | Yes. A `char_conversion/2` directive updates preparation-time conversion for later unquoted source characters and the recorded mapping initializes execution-time term input. Quoted characters are not converted; `char_conversion=off` disables following preparation-time conversion. | **defined** — `src/parser.js`, `src/program.js`, `src/solver.js`; strict-core regression coverage. |
|
|
39
39
|
| 7.4.2.6 | Order of `initialization/1` goals | Initialization goals run once, in source/inclusion order, before requested goals; each must obtain a first solution. | **defined** — `Program.initializations`, `Solver.runInitializations()`. |
|
|
40
40
|
| 7.4.2.7 | Ground term designating a Prolog text for `include/1` | A source designation is an atom interpreted as a file path; relative paths resolve from the including file's directory (or the current working directory for unanchored input). | **defined** — `src/program.js` (`readIncludedSource`). |
|
|
41
41
|
| 7.4.2.8 | Ground term designating a Prolog text for `ensure_loaded/1` | Same atom/file-path designation as `include/1`. | **defined** — `src/program.js`. |
|
|
@@ -59,17 +59,17 @@ Status values are:
|
|
|
59
59
|
| 7.10.2.13 | `eof_action(Action)` property when the stream uses its default action | Reports the same selected action: `error` for ordinary opened files and `reset` for the predefined standard streams. | **defined** — `streamProperties()` in `src/iso.js`, defaults in `src/io.js`. |
|
|
60
60
|
| 7.10.2.11 | Whether `reposition(false)` streams may nevertheless be repositioned | No. `set_stream_position/2` raises a permission error unless the stream was created with `reposition(true)`. | **defined** — `src/iso.js`. |
|
|
61
61
|
| 7.11.1.1 | Default `bounded` flag | `false`: EyeProlog's Prolog integer model uses arbitrary-precision `BigInt`, subject to host resources. | **defined** — `defaultPrologFlags()` in `src/solver.js`. |
|
|
62
|
-
| 7.11.1.2 | Default `max_integer` when `bounded=true` |
|
|
63
|
-
| 7.11.1.3 | Default `min_integer` when `bounded=true` |
|
|
62
|
+
| 7.11.1.2 | Default `max_integer` when `bounded=true` | Not applicable because EyeProlog selects `bounded=false`; consequently `current_prolog_flag(max_integer, _)` fails. | **not applicable** — `src/solver.js`, `current_prolog_flag/2`; strict-core regression coverage. |
|
|
63
|
+
| 7.11.1.3 | Default `min_integer` when `bounded=true` | Not applicable because EyeProlog selects `bounded=false`; consequently `current_prolog_flag(min_integer, _)` fails. | **not applicable** — `src/solver.js`, `current_prolog_flag/2`; strict-core regression coverage. |
|
|
64
64
|
| 7.11.1.4 | Default `integer_rounding_function` flag | `toward_zero`; `//` uses truncation toward zero. `div` is provided separately with downward/floor division semantics. | **defined** — `src/solver.js`, `src/iso-arithmetic.js`. |
|
|
65
65
|
| 9.1.3.1 | Integer division rounding function `rndI` | Truncation toward zero, matching the `integer_rounding_function=toward_zero` flag. | **defined** — BigInt division for `//` in `src/iso-arithmetic.js`. |
|
|
66
|
-
| 7.11.2.1 | Whether preparation-time `Convc` affects execution-time `Convc` | Yes: mappings
|
|
66
|
+
| 7.11.2.1 | Whether preparation-time `Convc` affects execution-time `Convc` | Yes: mappings created while Prolog text is prepared are retained and initialize the solver's execution-time conversion map. | **defined** — `src/parser.js`, `src/program.js`, `src/solver.js`. |
|
|
67
67
|
| 7.11.2.2 | Effect when `debug=on` | The flag is accepted and stored; it does not change goal semantics or enable a debugger. | **defined** — `src/solver.js`; no semantic branch depends on `debug`. |
|
|
68
68
|
| 7.11.2.3 | Default `max_arity` | `unbounded` in the Prolog model, subject to host memory and practical JavaScript array/index limits. | **defined** — `src/solver.js`; relevant guards report representation/resource errors. |
|
|
69
69
|
| 7.11.2.5 | Default `double_quotes` | `chars`. | **defined** — `src/solver.js`, parser flag state. |
|
|
70
70
|
| 7.12.1 | Second argument of `error/2` | The default context term is the atom `eyeprolog`. A few implementation-specific diagnostics may deliberately supply a more specific context term. | **defined** — `formalErrorTerm()` in `src/iso.js`. |
|
|
71
71
|
| 7.12.2(f) | Implementation-defined representation limits | Character and character-code operations use Unicode scalar limits; arity/integer values are modeled as unbounded but may hit host/resource limits. Float input overflow uses the implementation-specific `max_float`/`min_float` representation names documented by the STC-oriented tests. | **defined** — parser/ISO numeric and character guards. |
|
|
72
|
-
| 8.17.1 | Implementation-defined flag value ranges | Strict mode exposes only Part 1 core flags and their standard value sets. Normal mode additionally exposes EyeProlog's `occurs_check` flag
|
|
72
|
+
| 8.17.1 | Implementation-defined flag value ranges | Strict mode exposes only Part 1 core flags and their standard value sets. Normal mode additionally exposes EyeProlog's `occurs_check` flag. With `bounded=false`, `max_integer` and `min_integer` have no current value and their `current_prolog_flag/2` queries fail. | **defined** — strict registry/flag filtering in `src/solver.js`. |
|
|
73
73
|
| 8.17.3 | Other effects of `halt/0` | Terminates EyeProlog execution and returns host/process status `0`; it produces no Prolog solution. | **defined** — `HaltSignal`, `haltBuiltin()`, CLI/runner handling. |
|
|
74
74
|
| 8.17.4 | Meaning/effects of `halt(Status)` | Integer `Status` is converted to the host process/runner halt code; it produces no Prolog solution. | **defined** — `haltBuiltin()`, `src/execute.js`, `src/cli.js`. |
|
|
75
75
|
| 9.1.4.1 | Floating-point rounding function `rndF` | Floating values and operations use ECMAScript `Number` (IEEE-754 binary64) and the host's specified binary64 arithmetic/conversions. | **defined** — `src/iso-arithmetic.js`, `src/number-value.js`. |
|
|
@@ -118,9 +118,10 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
|
|
|
118
118
|
| 5.5.11 Reserved atoms | None | None. |
|
|
119
119
|
| Cor.3 5.5.12 Options | Extra library/host options may exist outside core option lists | Strict core accepts the standard option names implemented for Part 1; invalid options follow Corrigendum 3 validation. |
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
the Part 1 strict-core boundary
|
|
121
|
+
Normal mode provides documented module and DCG compatibility profiles whose
|
|
122
|
+
features overlap standardized Part 2 and Part 3 facilities. They are extensions
|
|
123
|
+
relative to the Part 1 strict-core boundary and are tested separately; this
|
|
124
|
+
ledger does not assert complete Part 2 or Part 3 conformance.
|
|
124
125
|
|
|
125
126
|
## Important implementation-dependent behavior (not the 5.4 mandatory table)
|
|
126
127
|
|
|
@@ -19,8 +19,8 @@ compliance audit and the remaining work before a full conformance claim.
|
|
|
19
19
|
| 8.16 atomic processing | atoms, characters, codes and number conversion with prescribed errors | `atomic_term_processing`, focused forward/reverse cases, parenthesized-number rejection, Logtalk-derived error cases |
|
|
20
20
|
| 8.17 flags and hooks | required flags, mutation permissions, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases |
|
|
21
21
|
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power` |
|
|
22
|
-
| ISO/IEC 13211-2
|
|
23
|
-
|
|
|
22
|
+
| Module compatibility profile (related to ISO/IEC 13211-2 and later WG17 amendment work) | module declarations, exports, imports, qualification, meta-predicate context | `modules/qualified_call`, `modules/selective_library_import`, `dcg_module_nonterminal_indicator` |
|
|
23
|
+
| Part 3-oriented DCG compatibility profile | `-->`, terminal and partial sequences, grammar control constructs, semicontexts, nonterminal indicators, modules, `phrase/2-3`, steadfastness and errors | `dcg_terminals_and_remainder`, `dcg_control_constructs`, `dcg_partial_sequences`, `dcg_phrase_steadfastness`, `dcg_dynamic_nonterminal_indicator`, `logtalk_dcg_phrase_identity`, `logtalk_dcg_semicontexts`, DCG error and precedence cases |
|
|
24
24
|
|
|
25
25
|
Corrigendum-specific coverage includes double-quoted atom operator priority
|
|
26
26
|
(Cor.1); the added predicates and evaluable functors, bar-operator rules,
|
|
@@ -36,7 +36,9 @@ particular, the index records the unbounded integer model, `double_quotes=chars`
|
|
|
36
36
|
9.1.4.2 choice to round tiny arithmetic results rather than raise underflow),
|
|
37
37
|
stream/character decisions, and the normal-profile extension boundary.
|
|
38
38
|
|
|
39
|
-
This is an executable conformance matrix, not a certification issued by an
|
|
40
|
-
independent standards body.
|
|
39
|
+
This is an executable conformance/compatibility matrix, not a certification issued by an
|
|
40
|
+
independent standards body. The Part 2/Part 3-related rows describe tested
|
|
41
|
+
normal-mode compatibility surfaces rather than a claim that every requirement
|
|
42
|
+
of those standards has been closed. Release gating runs the ISO cases and the dedicated
|
|
41
43
|
Part 1 strict-core suite together with the full regression, API, documentation,
|
|
42
44
|
example, and browser suites.
|
|
@@ -8,7 +8,7 @@ supported ISO Prolog profile, built-ins, extensions, and reasoner behavior.
|
|
|
8
8
|
The suite is intentionally file-based. Exact standard output, errors, warnings,
|
|
9
9
|
and proof output test the behavior of the JavaScript implementation.
|
|
10
10
|
[ISO-MATRIX.md](ISO-MATRIX.md) maps Part 1 normative clause families, all three
|
|
11
|
-
corrigenda,
|
|
11
|
+
corrigenda, plus EyeProlog's module and DCG compatibility profiles, to representative
|
|
12
12
|
executable cases. [WG17-SYNTAX-STATUS.md](WG17-SYNTAX-STATUS.md) records the
|
|
13
13
|
complete one-to-one trace for the vendored active upstream WG17 syntax cases.
|
|
14
14
|
[STC-DRAFT-STATUS.md](STC-DRAFT-STATUS.md) separately tracks executable
|
|
@@ -17,9 +17,8 @@ review evidence, not normative ISO claims.
|
|
|
17
17
|
|
|
18
18
|
“Conformance” here means conformance to EyeProlog's documented ISO compatibility
|
|
19
19
|
profile and implementation extensions. The default registry covers the exact
|
|
20
|
-
predicate indicators listed in Appendix B of the book across the
|
|
21
|
-
|
|
22
|
-
standard families. [ISO-COMPLIANCE.md](ISO-COMPLIANCE.md) is the explicit
|
|
20
|
+
predicate indicators listed in Appendix B of the book across the Part 1 strict-core
|
|
21
|
+
target and the normal-mode module/DCG compatibility families. [ISO-COMPLIANCE.md](ISO-COMPLIANCE.md) is the explicit
|
|
23
22
|
processor-requirement ledger for the Part 1 strict-core audit. This suite is
|
|
24
23
|
still not an independent certification of every processor requirement, lexical
|
|
25
24
|
edge, option combination, or prescribed error precedence. Cases under `iso/`
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
answer(7, red, caught(eyeprolog), first, caught(eyeprolog)).
|
|
2
2
|
answer(7, red, caught(eyeprolog), second, caught(eyeprolog)).
|
|
3
|
-
flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(
|
|
3
|
+
flags(off, on, [pair(bounded, false), pair(integer_rounding_function, toward_zero), pair(char_conversion, on), pair(debug, off), pair(max_arity, unbounded), pair(unknown, fail), pair(double_quotes, chars), pair(occurs_check, true)]).
|
package/test/fixtures/README.md
CHANGED
|
@@ -21,3 +21,10 @@ The corpus snapshot was retrieved on 2026-08-11.
|
|
|
21
21
|
`prologue_quad_runner.pl` loads EyeProlog's `library(prologue)` and includes
|
|
22
22
|
the unmodified corpus, mirroring the draft's requirement that a Prologue be
|
|
23
23
|
included before its examples are run.
|
|
24
|
+
|
|
25
|
+
The upstream Prologue snapshot contains one `max_integer` quad that accepts an
|
|
26
|
+
implementation-specific `Max = unbounded` result. EyeProlog deliberately does
|
|
27
|
+
not patch that vendored fixture: with its ISO `bounded=false` choice, Part 1
|
|
28
|
+
7.11.1.1 requires `current_prolog_flag(max_integer, _)` to fail. The regression
|
|
29
|
+
gate therefore records this single standards-driven divergence explicitly while
|
|
30
|
+
requiring the other 32 quads to pass.
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -36,6 +36,26 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
36
36
|
equal(solver.prologFlags.get('unknown')?.value?.name, 'error', 'unknown default');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
reporter.test('unbounded integer profile does not expose max_integer or min_integer values', () => {
|
|
40
|
+
const program = Program.parse('', { isoStrict: true });
|
|
41
|
+
const solver = new Solver(program, { isoStrict: true });
|
|
42
|
+
const answers = (text) => [...solver.solve([parseGoalText(text, { isoStrict: true })], new Env(), 0)].length;
|
|
43
|
+
equal(answers('current_prolog_flag(bounded,false)'), 1, 'bounded=false');
|
|
44
|
+
equal(answers('current_prolog_flag(max_integer,_)'), 0, 'max_integer unavailable');
|
|
45
|
+
equal(answers('current_prolog_flag(min_integer,_)'), 0, 'min_integer unavailable');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
reporter.test('preparation-time char_conversion affects later unquoted source only', () => {
|
|
50
|
+
const program = Program.parse(
|
|
51
|
+
":- char_conversion(x,y).\np(x).\nquoted('x').\n:- set_prolog_flag(char_conversion,off).\nraw(x).\n",
|
|
52
|
+
{ isoStrict: true },
|
|
53
|
+
);
|
|
54
|
+
equal(Boolean(program.findGroup('p', 1)?.clauses.some((clause) => clause.head.args[0]?.name === 'y')), true, 'converted p/1');
|
|
55
|
+
equal(Boolean(program.findGroup('quoted', 1)?.clauses.some((clause) => clause.head.args[0]?.name === 'x')), true, 'quoted atom unchanged');
|
|
56
|
+
equal(Boolean(program.findGroup('raw', 1)?.clauses.some((clause) => clause.head.args[0]?.name === 'x')), true, 'conversion disabled');
|
|
57
|
+
});
|
|
58
|
+
|
|
39
59
|
reporter.test('uses the Part 1 predefined operator table', () => {
|
|
40
60
|
const program = Program.parse('', { isoStrict: true });
|
|
41
61
|
equal(program.operators.has('fx\u0000?-'), true, 'fx ?-');
|
package/test/run-regression.mjs
CHANGED
|
@@ -1218,7 +1218,7 @@ c4 ?- call((!;1)).
|
|
|
1218
1218
|
},
|
|
1219
1219
|
},
|
|
1220
1220
|
{
|
|
1221
|
-
name: '
|
|
1221
|
+
name: 'vendored Prolog Prologue corpus records the bounded=false max_integer divergence',
|
|
1222
1222
|
run: () => {
|
|
1223
1223
|
const filename = path.join(testRoot, 'fixtures', 'prologue_quad_runner.pl');
|
|
1224
1224
|
const source = fs.readFileSync(filename, 'utf8');
|
|
@@ -1228,8 +1228,15 @@ c4 ?- call((!;1)).
|
|
|
1228
1228
|
baseDir: path.dirname(filename),
|
|
1229
1229
|
}]));
|
|
1230
1230
|
assertEqual(result.total, 33, 'quad total');
|
|
1231
|
-
|
|
1232
|
-
|
|
1231
|
+
// The upstream working-draft quad accepts either integer overflow or
|
|
1232
|
+
// Max=unbounded. ISO/IEC 13211-1 7.11.1.1 instead says that when
|
|
1233
|
+
// bounded=false, current_prolog_flag(max_integer, N) fails. Preserve
|
|
1234
|
+
// the upstream fixture unchanged and make that one deliberate
|
|
1235
|
+
// standards-driven divergence explicit in the regression gate.
|
|
1236
|
+
assertEqual(result.passed, 32, 'quad passed');
|
|
1237
|
+
assertEqual(result.failed, 1, 'quad failed');
|
|
1238
|
+
assertIncludes(result.stdout, 'current_prolog_flag(max_integer, Max)', 'max_integer divergence');
|
|
1239
|
+
assertIncludes(result.stdout, 'quads: 33 run, 32 passed, 1 failed.', 'quad report');
|
|
1233
1240
|
},
|
|
1234
1241
|
},
|
|
1235
1242
|
{
|
|
@@ -4613,7 +4620,7 @@ answer(ok) :-
|
|
|
4613
4620
|
},
|
|
4614
4621
|
},
|
|
4615
4622
|
{
|
|
4616
|
-
name: '
|
|
4623
|
+
name: 'module compatibility libraries load Prolog clauses explicitly',
|
|
4617
4624
|
run: () => {
|
|
4618
4625
|
const program = Program.parse(':- use_module(library(lists)).\n:- use_module(library(random)).\nanswer(X) :- append([a], [b], X).');
|
|
4619
4626
|
const solver = new Solver(program);
|
|
@@ -4749,7 +4756,7 @@ repeated(X) :- X in 1..3, all_distinct([X, X]).
|
|
|
4749
4756
|
},
|
|
4750
4757
|
},
|
|
4751
4758
|
{
|
|
4752
|
-
name: '
|
|
4759
|
+
name: 'module compatibility profile isolates predicates and supports selective imports',
|
|
4753
4760
|
run: () => {
|
|
4754
4761
|
const directory = path.join(tmp, `modules-${++tmpCounter}`);
|
|
4755
4762
|
fs.mkdirSync(directory);
|
|
@@ -4774,7 +4781,7 @@ repeated(X) :- X in 1..3, all_distinct([X, X]).
|
|
|
4774
4781
|
},
|
|
4775
4782
|
},
|
|
4776
4783
|
{
|
|
4777
|
-
name: 'Part 3 nonterminal indicators import through
|
|
4784
|
+
name: 'Part 3 nonterminal indicators import through module compatibility profile',
|
|
4778
4785
|
run: () => {
|
|
4779
4786
|
const directory = path.join(tmp, `dcg-modules-${++tmpCounter}`);
|
|
4780
4787
|
fs.mkdirSync(directory);
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -1943,7 +1943,7 @@ The source layout mirrors the language boundary while keeping the JavaScript
|
|
|
1943
1943
|
runtime flat under `src/`. `src/iso.js` remains the stable ISO facade and
|
|
1944
1944
|
built-in registry; arithmetic evaluation lives in `src/iso-arithmetic.js`, and
|
|
1945
1945
|
processor control/error classes live in `src/errors.js`. `src/dcg.js`
|
|
1946
|
-
implements the shared
|
|
1946
|
+
implements the shared Part 3-oriented grammar-rule and dynamic-body expansion
|
|
1947
1947
|
without depending back on the ISO registry. This keeps the low-level syntax and
|
|
1948
1948
|
error layers acyclic while preserving the existing `src/iso.js` exports.
|
|
1949
1949
|
|
|
@@ -1964,8 +1964,8 @@ being split through extra strategy objects or callbacks. Architectural cleanup
|
|
|
1964
1964
|
is required to preserve benchmark performance as well as semantics.
|
|
1965
1965
|
|
|
1966
1966
|
Focused files under `src/lib/` contain the portable extensions, with
|
|
1967
|
-
`src/lib/lists.pl` supplying common list relations. They are ordinary
|
|
1968
|
-
|
|
1967
|
+
`src/lib/lists.pl` supplying common list relations. They are ordinary Prolog modules using EyeProlog's documented module compatibility
|
|
1968
|
+
surface, organized like Trealla's `library/` and registered for
|
|
1969
1969
|
`library(Name)` by `src/standard-library.js` in Node and the browser. The
|
|
1970
1970
|
browser entry point `src/playground-worker.js` uses that same program and
|
|
1971
1971
|
module-loading path in a dedicated worker. `src/ARCHITECTURE.md` records the
|
|
@@ -2587,8 +2587,10 @@ correct program without losing its meaning, and organize a decision system
|
|
|
2587
2587
|
whose conclusions remain auditable.
|
|
2588
2588
|
|
|
2589
2589
|
EyeProlog supplies the Part 1 control, dynamic-database, operator, and I/O
|
|
2590
|
-
facilities together with
|
|
2591
|
-
`use_module/2`, and `Module:Goal`.
|
|
2590
|
+
facilities together with its normal-profile module forms `module/2`,
|
|
2591
|
+
`use_module/1`, `use_module/2`, and `Module:Goal`. These forms are treated as a
|
|
2592
|
+
module compatibility surface, not as a claim of complete ISO/IEC 13211-2:2000
|
|
2593
|
+
conformance. Definite-clause grammar notation remains
|
|
2592
2594
|
outside this profile. The examples still prefer explicit domain
|
|
2593
2595
|
relations, state, and syntax trees where that makes assumptions easier to
|
|
2594
2596
|
inspect.
|
|
@@ -5485,11 +5487,13 @@ The long catalogs are meant to be entered locally, not memorized linearly.
|
|
|
5485
5487
|
|
|
5486
5488
|
## 38. Language and ISO profile
|
|
5487
5489
|
|
|
5488
|
-
The
|
|
5489
|
-
Corrigenda 1:2007, 2:2012, and 3:2017
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5490
|
+
The normative strict-core baseline is ISO/IEC 13211-1:1995, as corrected by
|
|
5491
|
+
Technical Corrigenda 1:2007, 2:2012, and 3:2017. Normal EyeProlog additionally
|
|
5492
|
+
provides a practical module interface aligned with later WG17 module amendment
|
|
5493
|
+
work and a definite-clause-grammar profile following ISO/IEC TS 13211-3. Those
|
|
5494
|
+
normal-mode profiles are documented and tested compatibility surfaces; they are
|
|
5495
|
+
not currently claimed as complete clause-by-clause certifications of Part 2 or
|
|
5496
|
+
Part 3.
|
|
5493
5497
|
|
|
5494
5498
|
Prolog source accepted by EyeProlog is UTF-8. `%` starts a line comment and
|
|
5495
5499
|
`/* ... */` delimits a block comment. Plain atoms begin with a
|
|
@@ -5618,10 +5622,11 @@ EyeProlog supports cut, operator declarations, dynamic database updates, grouped
|
|
|
5618
5622
|
solutions, exceptions, flags, initialization and inclusion directives, and
|
|
5619
5623
|
standard stream and term I/O. Normal mode additionally provides lifecycle-aware
|
|
5620
5624
|
`call_cleanup/2` and `setup_call_cleanup/3`; these cleanup controls are
|
|
5621
|
-
EyeProlog extensions and are excluded by `--iso-strict`.
|
|
5622
|
-
|
|
5625
|
+
EyeProlog extensions and are excluded by `--iso-strict`. Normal mode also
|
|
5626
|
+
adds the documented module compatibility surface and a Part 3-oriented DCG
|
|
5627
|
+
profile.
|
|
5623
5628
|
|
|
5624
|
-
###
|
|
5629
|
+
### Module compatibility profile
|
|
5625
5630
|
|
|
5626
5631
|
A module gives predicate identity one more component: module name, predicate
|
|
5627
5632
|
name, and arity. The first directive in a module source names the module and
|
|
@@ -5657,7 +5662,7 @@ an empty list when only qualified calls are wanted. `Module:Goal` selects a
|
|
|
5657
5662
|
module explicitly. Repeated module loads are idempotent, while conflicting
|
|
5658
5663
|
imports and requests for predicates that a module does not export are errors.
|
|
5659
5664
|
|
|
5660
|
-
###
|
|
5665
|
+
### Part 3-oriented definite clause grammars
|
|
5661
5666
|
|
|
5662
5667
|
A grammar rule `Head --> Body.` is prepared as an ordinary predicate with two
|
|
5663
5668
|
additional difference-list arguments. Parameterized nonterminals retain their
|
|
@@ -5670,7 +5675,7 @@ Nonterminal indicators can be exported and imported through modules:
|
|
|
5670
5675
|
word(noun) --> [robot] | [scientist].
|
|
5671
5676
|
```
|
|
5672
5677
|
|
|
5673
|
-
The
|
|
5678
|
+
The supported grammar constructs include terminal lists, `[]`, sequencing with
|
|
5674
5679
|
comma, alternatives with `;` or `|`, if-then-else, embedded goals `{Goal}`,
|
|
5675
5680
|
`call//1`, `phrase//1`, and `!//0`. Semicontexts provide look-ahead by restoring
|
|
5676
5681
|
terminals to the remaining sequence:
|
|
@@ -5973,7 +5978,7 @@ silently changing a static program.
|
|
|
5973
5978
|
| --- | --- |
|
|
5974
5979
|
| `op(+Priority,+Specifier,+NameOrNames)` | Defines or removes operators in the current program. Priority is `0..1200`; specifiers are `fx`, `fy`, `xf`, `yf`, `xfx`, `xfy`, or `yfx`; names may be one atom or a proper list. Priority zero removes the definition. `,` and `\|` cannot be modified. |
|
|
5975
5980
|
| `current_op(?Priority,?Specifier,?Name)` | Enumerates active operator definitions and filters supplied arguments. |
|
|
5976
|
-
| `char_conversion(+Input,+Output)` | Installs a one-character
|
|
5981
|
+
| `char_conversion(+Input,+Output)` | Installs a one-character conversion. In prepared Prolog text, later **unquoted** characters are converted when `char_conversion=on`; quoted characters are unchanged. The same mapping initializes execution-time term input. Mapping a character to itself removes its custom mapping. |
|
|
5977
5982
|
| `current_char_conversion(?Input,?Output)` | Enumerates installed nonidentity conversions. |
|
|
5978
5983
|
| `current_prolog_flag(?Flag,?Value)` | Enumerates flags or retrieves one named flag. An unknown bound flag raises *domain_error(prolog_flag)*. |
|
|
5979
5984
|
| `set_prolog_flag(+Flag,+Value)` | Changes a supported mutable flag after validating its allowed atom value. Read-only flags raise a permission error. |
|
|
@@ -5984,13 +5989,21 @@ silently changing a static program.
|
|
|
5984
5989
|
| `integer_rounding_function` | `toward_zero` | `toward_zero` | no |
|
|
5985
5990
|
| `char_conversion` | `on` | `on`, `off` | yes |
|
|
5986
5991
|
| `debug` | `off` | `on`, `off` | yes |
|
|
5987
|
-
| `max_integer` | `
|
|
5988
|
-
| `min_integer` | `
|
|
5992
|
+
| `max_integer` | no current value because `bounded=false` | not applicable | no |
|
|
5993
|
+
| `min_integer` | no current value because `bounded=false` | not applicable | no |
|
|
5989
5994
|
| `max_arity` | `unbounded` | `unbounded` | no |
|
|
5990
5995
|
| `unknown` | `error` | `error`, `fail`, `warning` | yes |
|
|
5991
5996
|
| `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
|
|
5992
5997
|
| `occurs_check` | `true` | `true`, `error` | yes |
|
|
5993
5998
|
|
|
5999
|
+
Because `bounded=false`, `current_prolog_flag(max_integer, _)` and
|
|
6000
|
+
`current_prolog_flag(min_integer, _)` fail as required by ISO 7.11.1.1;
|
|
6001
|
+
EyeProlog does not expose an `unbounded` sentinel as either flag value.
|
|
6002
|
+
Preparation-time `char_conversion/2` mappings affect later unquoted source text
|
|
6003
|
+
and also initialize the execution-time conversion mapping; setting the
|
|
6004
|
+
`char_conversion` flag to `off` disables conversion for following source text.
|
|
6005
|
+
Quoted source characters are not converted.
|
|
6006
|
+
|
|
5994
6007
|
Both normal EyeProlog and strict ISO core mode use the ISO `unknown=error`
|
|
5995
6008
|
default. Interactive `set_prolog_flag/2` changes are retained when the REPL
|
|
5996
6009
|
consults another file or imports a module instead of being reset by the host
|
|
@@ -7599,14 +7612,15 @@ npm run test:playground
|
|
|
7599
7612
|
|
|
7600
7613
|
### Supported ISO Prolog implementation
|
|
7601
7614
|
|
|
7602
|
-
EyeProlog executes a documented and tested ISO Prolog
|
|
7603
|
-
ISO/IEC 13211-1:1995
|
|
7604
|
-
|
|
7615
|
+
EyeProlog executes a documented and tested ISO-oriented Prolog profile. Its
|
|
7616
|
+
strict-core target is ISO/IEC 13211-1:1995 with Technical Corrigenda 1-3. Normal
|
|
7617
|
+
mode additionally provides the documented module compatibility surface and a
|
|
7618
|
+
Part 3-oriented definite-clause-grammar implementation. The exact supported predicate indicators are
|
|
7605
7619
|
listed in Chapter 39. The normal profile includes control and exceptions, term
|
|
7606
7620
|
operations, arithmetic, grouped solutions, dynamic clauses, operators,
|
|
7607
7621
|
atomic-term processing, flags, character conversion, streams, character/byte
|
|
7608
|
-
and term I/O, initialization, source inclusion,
|
|
7609
|
-
rules, and EyeProlog extensions.
|
|
7622
|
+
and term I/O, initialization, source inclusion, module compatibility forms,
|
|
7623
|
+
definite-clause grammar rules, and EyeProlog extensions.
|
|
7610
7624
|
|
|
7611
7625
|
For a Part 1 conformance boundary, `--iso-strict` (or API option
|
|
7612
7626
|
`isoStrict: true`) limits the processor to ISO/IEC 13211-1:1995 plus Technical
|
|
@@ -7627,7 +7641,7 @@ remaining qualifications are:
|
|
|
7627
7641
|
|
|
7628
7642
|
- zero-arity compound syntax such as `ready()` is represented by the atom
|
|
7629
7643
|
`ready`;
|
|
7630
|
-
-
|
|
7644
|
+
- module and ISO/IEC TS 13211-3-oriented DCG compatibility profiles are supported in normal mode, without a complete Part 2/Part 3 certification claim;
|
|
7631
7645
|
- variables cannot occupy functor or predicate position;
|
|
7632
7646
|
- double-quoted text follows `double_quotes` exactly; the default `chars` value
|
|
7633
7647
|
matches Trealla and Scryer and may be changed to `codes` or `atom`;
|
package/why-eyeprolog.md
CHANGED
|
@@ -6,8 +6,8 @@ answer.
|
|
|
6
6
|
|
|
7
7
|
Its design rule is simple:
|
|
8
8
|
|
|
9
|
-
> **Keep the ISO language core
|
|
10
|
-
> and implement portable conveniences as ordinary Prolog whenever possible.**
|
|
9
|
+
> **Keep the ISO language core explicit and auditable, keep extensions small and
|
|
10
|
+
> explicit, and implement portable conveniences as ordinary Prolog whenever possible.**
|
|
11
11
|
|
|
12
12
|
## Why ISO Prolog?
|
|
13
13
|
|
|
@@ -17,12 +17,13 @@ arithmetic, control, streams, errors, and processor behavior. Reusing that
|
|
|
17
17
|
language gives programs recognizable semantics without inventing another rule
|
|
18
18
|
syntax.
|
|
19
19
|
|
|
20
|
-
EyeProlog
|
|
21
|
-
and 3,
|
|
22
|
-
Its executable conformance matrix and tests
|
|
23
|
-
behavior, including an executable trace of the vendored
|
|
24
|
-
cases. This is extensive implementation evidence, not
|
|
25
|
-
|
|
20
|
+
EyeProlog targets the Part 1 core together with Technical Corrigenda 1, 2,
|
|
21
|
+
and 3, and provides documented module and definite-clause-grammar compatibility
|
|
22
|
+
profiles for normal-mode programs. Its executable conformance matrix and tests
|
|
23
|
+
document the supported behavior, including an executable trace of the vendored
|
|
24
|
+
active WG17 syntax cases. This is extensive implementation evidence, not a
|
|
25
|
+
claim that every Part 1, Part 2, or Part 3 normative requirement has already
|
|
26
|
+
been certified or closed.
|
|
26
27
|
|
|
27
28
|
## Why a small implementation?
|
|
28
29
|
|
|
@@ -34,8 +35,8 @@ keeps a narrow architecture:
|
|
|
34
35
|
- explicit `tnot/1` with well-founded semantics for finite, range-restricted,
|
|
35
36
|
function-free Datalog components;
|
|
36
37
|
- the ISO built-in registry;
|
|
37
|
-
- lean portable
|
|
38
|
-
- ISO Part 3 definite clause grammars and `phrase/2-3`;
|
|
38
|
+
- lean portable library modules using the documented module compatibility profile;
|
|
39
|
+
- ISO Part 3-oriented definite clause grammars and `phrase/2-3`;
|
|
39
40
|
- lifecycle-aware `call_cleanup/2` and `setup_call_cleanup/3` in normal mode;
|
|
40
41
|
- optional proof explanations; and
|
|
41
42
|
- the same implementation in Node.js and the browser.
|
|
@@ -72,8 +73,8 @@ positive Datalog closures with a shared relation-wide table, but the admission
|
|
|
72
73
|
heuristics are implementation details. Programs should rely on the documented
|
|
73
74
|
semantics and finiteness conditions, not on a particular internal threshold.
|
|
74
75
|
|
|
75
|
-
DCGs follow the same rule.
|
|
76
|
-
model, while finite sequence scans and proven zero-width hand-offs may use
|
|
76
|
+
DCGs follow the same rule. Their supported semantics follow the ISO Part 3
|
|
77
|
+
difference-list model, while finite sequence scans and proven zero-width hand-offs may use
|
|
77
78
|
lighter internal control paths so deep grammars do not pay one general solver
|
|
78
79
|
frame per token. Relational remainder-producing modes are preserved. The
|
|
79
80
|
checked `examples/dcg-expression-language.pl` program shows the declarative side
|
|
@@ -122,7 +123,7 @@ and integrations that can live outside the reasoning engine.
|
|
|
122
123
|
## The durable idea
|
|
123
124
|
|
|
124
125
|
EyeProlog demonstrates that a useful proof-producing reasoner can be built from
|
|
125
|
-
a
|
|
126
|
+
a carefully audited standard-oriented core, small portable modules, and an ordinary JavaScript
|
|
126
127
|
API. Its value is not feature count; it is that the language boundary stays
|
|
127
128
|
visible enough to understand.
|
|
128
129
|
|