eyeprolog 1.4.1 → 1.4.2
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/write.js +29 -6
- package/test/conformance/ISO-COMPLIANCE.md +4 -4
- package/test/conformance/README.md +1 -1
- package/test/conformance/WG17-SYNTAX-STATUS.md +12 -12
- package/test/conformance/wg17-syntax-cases.json +101 -10
- package/test/conformance/wg17-syntax-coverage.json +3 -3
- package/test/run-conformance-report.mjs +3 -1
- package/test/run-regression.mjs +51 -3
- package/test/run-wg17.mjs +14 -0
- package/tools/report-wg17-syntax-coverage.mjs +4 -4
package/conformance-report.md
CHANGED
|
@@ -8,9 +8,9 @@ when this report is generated; it is not inferred from fixture counts.
|
|
|
8
8
|
|
|
9
9
|
| Gate | Passed | Total | Status |
|
|
10
10
|
|---|---:|---:|---|
|
|
11
|
-
| WG17 syntax |
|
|
11
|
+
| WG17 syntax | 372 | 372 | pass |
|
|
12
12
|
|
|
13
|
-
The WG17 syntax row executes the vendored
|
|
13
|
+
The WG17 syntax row executes the vendored 372-case conformity-testing matrix
|
|
14
14
|
against EyeProlog's strict ISO reader/writer. A behavior fix such as operator-token
|
|
15
15
|
spelling therefore changes this report even when no corpus file is added or removed.
|
|
16
16
|
|
package/package.json
CHANGED
package/src/write.js
CHANGED
|
@@ -99,17 +99,30 @@ function compactPrefixOperator(token, argument) {
|
|
|
99
99
|
return `${token}${needsLayout ? ' ' : ''}${argument}`;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function quotedOperatorAfterNumericNeedsSpace(left, token) {
|
|
103
|
+
if (!token.startsWith("'") || !/^[0-9]+$/.test(left)) return false;
|
|
104
|
+
const value = Number(left);
|
|
105
|
+
// `0'X` starts character-code notation and bases 2..36 start based-number
|
|
106
|
+
// notation. Base 1 and values above 36 do not, so they need no layout
|
|
107
|
+
// before a quoted operator (WG17 #208, #355).
|
|
108
|
+
return value === 0 || (value >= 2 && value <= 36);
|
|
109
|
+
}
|
|
110
|
+
|
|
102
111
|
function compactPostfixOperator(argument, token) {
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
const needsLayout = compactBoundaryNeedsSpace(argument, token) ||
|
|
113
|
+
quotedOperatorAfterNumericNeedsSpace(argument, token);
|
|
114
|
+
return `${argument}${needsLayout ? ' ' : ''}${token}`;
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
function compactInfixOperator(left, token, right) {
|
|
108
118
|
// Solo punctuation operators already delimit themselves.
|
|
109
119
|
if (token === ',' || token === ';') return `${left}${token}${right}`;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
const before = (compactBoundaryNeedsSpace(left, token) ||
|
|
121
|
+
quotedOperatorAfterNumericNeedsSpace(left, token)) ? ' ' : '';
|
|
122
|
+
let after = compactBoundaryNeedsSpace(token, right) ? ' ' : '';
|
|
123
|
+
// A word operator immediately followed by `(` would look like functional
|
|
124
|
+
// notation for that atom rather than operator notation.
|
|
125
|
+
if (isWordOperatorToken(token) && right.startsWith('(')) after = ' ';
|
|
113
126
|
return `${left}${before}${token}${after}${right}`;
|
|
114
127
|
}
|
|
115
128
|
|
|
@@ -384,7 +397,17 @@ function format(term, env, options, table, maxPriority = 1200, context = 'term')
|
|
|
384
397
|
['fx', 'fy', 'xfx', 'xfy', 'yfx'].includes(childDefinition.specifier)) {
|
|
385
398
|
argumentPriority = priority - 1;
|
|
386
399
|
}
|
|
387
|
-
const
|
|
400
|
+
const child = deref(resolved.args[0], env);
|
|
401
|
+
let argument = format(resolved.args[0], env, options, table, argumentPriority);
|
|
402
|
+
// If both the operand atom and postfix operator require quoted names,
|
|
403
|
+
// plain adjacency would form quoted-atom escape syntax and layout would
|
|
404
|
+
// not match the ISO writer form. Parenthesize the operator atom instead
|
|
405
|
+
// (WG17 #169). Do not apply this to ordinary word postfix operators.
|
|
406
|
+
if (options.minimalOperatorSpacing && child.type === ATOM && table.has(child.name) &&
|
|
407
|
+
writeAtom(child.name).startsWith("'") && token.startsWith("'") &&
|
|
408
|
+
compactBoundaryNeedsSpace(argument, token)) {
|
|
409
|
+
argument = `(${argument})`;
|
|
410
|
+
}
|
|
388
411
|
text = options.minimalOperatorSpacing ? compactPostfixOperator(argument, token) : `${argument} ${token}`;
|
|
389
412
|
} else {
|
|
390
413
|
let leftPriority = specifier === 'yfx' ? priority : priority - 1;
|
|
@@ -19,7 +19,7 @@ relevant implementation-dependent overlap choice to an explicit audit outcome.
|
|
|
19
19
|
|
|
20
20
|
| Requirement | Status | EyeProlog evidence / remaining work |
|
|
21
21
|
| --- | --- | --- |
|
|
22
|
-
| 5.1(a) prepare conforming Prolog text | covered | Clause 6 token/term production and rejection families are mapped by the strict production gate and the complete
|
|
22
|
+
| 5.1(a) prepare conforming Prolog text | covered | Clause 6 token/term production and rejection families are mapped by the strict production gate and the complete 372-case vendored WG17 syntax matrix; 7.4 preparation/directive behavior is separately closed. |
|
|
23
23
|
| 5.1(b) execute conforming Prolog goals | covered | Clause 7 term/control/execution semantics, the higher-level 7.10 stream model, 7.11 flags, the 7.12 error envelope, all 8.2-8.17 built-in rows, and Clause 9 arithmetic have explicit executable dispositions. |
|
|
24
24
|
| 5.1(c) reject nonconforming text/read-terms | covered | WG17 negative syntax cases, focused malformed-production/escape/comment/operator cases, read-term syntax errors, and strict rejection of implementation-specific syntax provide explicit rejection evidence across the Clause 6 families. |
|
|
25
25
|
| 5.1(d) document permitted variations | covered | The clause-by-clause [ISO 5.4 decision index](ISO-IMPLEMENTATION-DEFINED.md) records every explicit implementation-defined decision found in the Part 1 + Corrigenda baseline and separately inventories implementation-specific extension families. The index retains an `audit gap` category for future discoveries; no current release-facing row depends on one. |
|
|
@@ -31,7 +31,7 @@ relevant implementation-dependent overlap choice to an explicit audit outcome.
|
|
|
31
31
|
|
|
32
32
|
| Standard area | Status | Current evidence |
|
|
33
33
|
| --- | --- | --- |
|
|
34
|
-
| Clause 6 — tokens, terms, lists, operators, quoted text | covered | The strict Clause 6 gate maps atomic/variable/compound/operator/list/curly/double-quoted term forms, layout/comments, quoted escapes, integer bases/character codes, floating tokens, and solo/meta token boundaries, with malformed counterparts rejected. The complete
|
|
34
|
+
| Clause 6 — tokens, terms, lists, operators, quoted text | covered | The strict Clause 6 gate maps atomic/variable/compound/operator/list/curly/double-quoted term forms, layout/comments, quoted escapes, integer bases/character codes, floating tokens, and solo/meta token boundaries, with malformed counterparts rejected. The complete 372-case WG17 syntax matrix provides independent production-level coverage, and a cross-profile gate verifies that normal syntax extensions preserve every strict-accepted standard observation. The implementation-defined 6.5/6.6 PCS/collation choices are documented separately. |
|
|
35
35
|
| 7.1-7.3 — term types, term order, unification | covered | [ISO-TERM-SEMANTICS-MATRIX.md](ISO-TERM-SEMANTICS-MATRIX.md) records the five mutually exclusive strict term types, derived variable/compound/list notions, the complete standard-order classes and implementation-dependent variable-order choice, NSTO MGU/failure behavior, and EyeProlog's permitted consistent occurs-check failure for Part 1's undefined STO ordinary-unification cases. The normal JavaScript API string term is documented as a 5.5.4 extension and rejected at strict program/goal entry. |
|
|
36
36
|
| 7.4 — Prolog text and directives | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes the preparation/directive rows: declaration semantics, source clauses, cross-text operators/character conversion/flags, initialization order/lifetime, `include/1`, and one-time `ensure_loaded/1`, with implementation-defined cross-text choices indexed under 5.4. |
|
|
37
37
|
| 7.5-7.6 — database and term/clause conversion | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes static/dynamic and private/public procedure semantics, clause order, logical-update visibility, empty/unknown lifetime, and term/body/clause conversion. Source and runtime assertion conversion recurse through `,/2`, `;/2`, and `->/2`, preserve variable identity, and protect standardized static/control procedures. |
|
|
@@ -286,7 +286,7 @@ post-N289 STC drafts remain review input until standardized.
|
|
|
286
286
|
| Criterion | Status | Evidence |
|
|
287
287
|
| --- | --- | --- |
|
|
288
288
|
| Clause 5 processor obligations have explicit dispositions | covered | `ISO-PROCESSOR-REQUIREMENTS.md` gives explicit outcomes for 5.1-5.5, including the strict/normal extension boundary. |
|
|
289
|
-
| Clause 6 lexical/syntactic requirements have explicit dispositions | covered | the strict Clause 6 production/rejection gate and the
|
|
289
|
+
| Clause 6 lexical/syntactic requirements have explicit dispositions | covered | the strict Clause 6 production/rejection gate and the 372-case vendored WG17 matrix cover the standard families; cross-profile preservation verifies that normal extensions do not reinterpret strict-accepted text. |
|
|
290
290
|
| Clause 7 semantic requirements have explicit dispositions | covered | 7.1-7.12 now have explicit semantic, implementation-defined, or error-envelope dispositions backed by strict tests and the specialized matrices. |
|
|
291
291
|
| Clause 8 built-in modes/errors have explicit dispositions | covered | `ISO-BUILTIN-MODE-ERROR-MATRIX.md` closes 8.2-8.17 row by row |
|
|
292
292
|
| Clause 9 evaluable-functor requirements have explicit dispositions | covered | `ISO-EVALUABLE-FUNCTOR-MATRIX.md` plus the strict arithmetic regression closes the published Part 1 + Corrigenda arithmetic rows |
|
|
@@ -294,7 +294,7 @@ post-N289 STC drafts remain review input until standardized.
|
|
|
294
294
|
| Implementation-specific strict/normal boundary is documented and tested | covered | all 5.5 hooks have explicit dispositions; the WG17 cross-profile gate verifies syntax-preservation for standard text accepted by the strict reader. |
|
|
295
295
|
| Published Corrigenda 1-3 are incorporated | covered | dedicated conformance/strict tests and this ledger track all three published Corrigenda |
|
|
296
296
|
| Current post-N289 draft is tracked without silently changing the published baseline | covered | `STC-DRAFT-STATUS.md` tracks reviewed draft items separately from normative requirements |
|
|
297
|
-
| Independent external syntax corpus is an offline release gate | covered | the vendored WG17 syntax matrix has
|
|
297
|
+
| Independent external syntax corpus is an offline release gate | covered | the vendored WG17 syntax matrix has 372 executable dispositions checked against its upstream expectations |
|
|
298
298
|
| Third-party standard-core regression provenance is retained | covered | adapted Logtalk, Scryer, Trealla, and SWI-Prolog cases retain source identifiers and licenses in `THIRD_PARTY.md` |
|
|
299
299
|
| No unexplained deviation remains in the release-facing ledger | covered | the release-facing ledger contains no remaining `audit` rows; documented variation points are implementation-defined/specific or draft-only rather than unexplained deviations. |
|
|
300
300
|
|
|
@@ -145,7 +145,7 @@ are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
|
|
|
145
145
|
|
|
146
146
|
The corpus has 386 cases in `iso/` and 802 file-based conformance cases in
|
|
147
147
|
total. Of those, 11 cases in `stc/` are explicitly labelled working-draft
|
|
148
|
-
review evidence rather than normative ISO claims. The separate strict-reader WG17 matrix has
|
|
148
|
+
review evidence rather than normative ISO claims. The separate strict-reader WG17 matrix has 372 executable dispositions.
|
|
149
149
|
The generated `conformance-report.md` is the authoritative source for the current
|
|
150
150
|
executable WG17 syntax result and file-based category totals. Together with regression, documentation-sync, API, example,
|
|
151
151
|
and book-example checks, `npm test` is the release gate.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# WG17 syntax traceability status
|
|
2
2
|
|
|
3
3
|
Source: [Conformity Testing I: Syntax](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)
|
|
4
|
-
Upstream inventory checked: 2026-08-
|
|
4
|
+
Upstream inventory checked: 2026-08-28
|
|
5
5
|
|
|
6
6
|
This ledger counts an upstream case when its WG17 identifier, query, and
|
|
7
7
|
expected ISO disposition are stored in the offline executable matrix. Every
|
|
8
8
|
case is executed against the upstream Codex expectation. Reviewed exact
|
|
9
9
|
EyeProlog outcomes are additional regression locks and can never override the
|
|
10
|
-
upstream assertion.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
upstream assertion. Concrete writer expectations are matched lexically: layout,
|
|
11
|
+
parentheses, and quoting are significant. Only Codex expectations explicitly
|
|
12
|
+
marked e.g. use controlled flexibility for representation choices such as
|
|
13
|
+
generated variable names or equivalent numeric spellings.
|
|
14
14
|
|
|
15
15
|
## Current standing
|
|
16
16
|
|
|
@@ -20,25 +20,25 @@ A represented case still has to pass the executable upstream assertion in
|
|
|
20
20
|
|
|
21
21
|
| Measure | Count |
|
|
22
22
|
| --- | ---: |
|
|
23
|
-
| Active upstream cases |
|
|
24
|
-
| Upstream cases represented in the executable matrix |
|
|
23
|
+
| Active upstream cases | 372 |
|
|
24
|
+
| Upstream cases represented in the executable matrix | 372 (100.0%) |
|
|
25
25
|
| Not yet traced one-by-one | 0 |
|
|
26
26
|
| Deleted upstream identifiers | #20, #273 |
|
|
27
27
|
|
|
28
28
|
The matrix runs in strict ISO stream-reader mode as part of `npm test`. The
|
|
29
29
|
3 upstream `waits` cases are checked through EyeProlog's interactive input
|
|
30
|
-
hook. All
|
|
31
|
-
upstream Codex expectation.
|
|
30
|
+
hook. All 372 executable cases are independently checked against the
|
|
31
|
+
upstream Codex expectation. 372 cases additionally retain exact reviewed
|
|
32
32
|
outcomes for stronger regression checking; 0 cases currently rely on the upstream assertion alone.
|
|
33
33
|
|
|
34
34
|
## Traceable evidence
|
|
35
35
|
|
|
36
36
|
| Executable evidence | Referenced IDs | WG17 cases |
|
|
37
37
|
| --- | ---: | --- |
|
|
38
|
-
| [complete offline executable matrix](../run-wg17.mjs) |
|
|
38
|
+
| [complete offline executable matrix](../run-wg17.mjs) | 372 | #1–#19, #21–#272, #274–#374 |
|
|
39
39
|
|
|
40
|
-
The evidence groups overlap. Their union is **
|
|
41
|
-
#1–#19, #21–#272, #274–#
|
|
40
|
+
The evidence groups overlap. Their union is **372** active cases:
|
|
41
|
+
#1–#19, #21–#272, #274–#374.
|
|
42
42
|
|
|
43
43
|
## Untraced upstream identifiers
|
|
44
44
|
|
|
@@ -2486,7 +2486,7 @@
|
|
|
2486
2486
|
"variables": "[]"
|
|
2487
2487
|
},
|
|
2488
2488
|
{
|
|
2489
|
-
"output": "0''",
|
|
2489
|
+
"output": "0 ''",
|
|
2490
2490
|
"variables": "[]"
|
|
2491
2491
|
}
|
|
2492
2492
|
]
|
|
@@ -2506,7 +2506,7 @@
|
|
|
2506
2506
|
"variables": "[]"
|
|
2507
2507
|
},
|
|
2508
2508
|
{
|
|
2509
|
-
"output": "0''",
|
|
2509
|
+
"output": "0 ''",
|
|
2510
2510
|
"variables": "[]"
|
|
2511
2511
|
}
|
|
2512
2512
|
]
|
|
@@ -2653,7 +2653,7 @@
|
|
|
2653
2653
|
"variables": "[]"
|
|
2654
2654
|
},
|
|
2655
2655
|
{
|
|
2656
|
-
"output": "0'f '",
|
|
2656
|
+
"output": "0 'f '",
|
|
2657
2657
|
"variables": "[]"
|
|
2658
2658
|
}
|
|
2659
2659
|
]
|
|
@@ -3313,7 +3313,7 @@
|
|
|
3313
3313
|
"variables": "[]"
|
|
3314
3314
|
},
|
|
3315
3315
|
{
|
|
3316
|
-
"output": "(fy 1)
|
|
3316
|
+
"output": "(fy 1)yf",
|
|
3317
3317
|
"variables": "[]"
|
|
3318
3318
|
}
|
|
3319
3319
|
]
|
|
@@ -3333,7 +3333,7 @@
|
|
|
3333
3333
|
"variables": "[]"
|
|
3334
3334
|
},
|
|
3335
3335
|
{
|
|
3336
|
-
"output": "(fy (fy 1)
|
|
3336
|
+
"output": "(fy (fy 1)yf)yf",
|
|
3337
3337
|
"variables": "[]"
|
|
3338
3338
|
}
|
|
3339
3339
|
]
|
|
@@ -3393,7 +3393,7 @@
|
|
|
3393
3393
|
"variables": "[]"
|
|
3394
3394
|
},
|
|
3395
3395
|
{
|
|
3396
|
-
"output": "(fy 1)
|
|
3396
|
+
"output": "(fy 1)yfx 2",
|
|
3397
3397
|
"variables": "[]"
|
|
3398
3398
|
}
|
|
3399
3399
|
]
|
|
@@ -3453,7 +3453,7 @@
|
|
|
3453
3453
|
"variables": "[]"
|
|
3454
3454
|
},
|
|
3455
3455
|
{
|
|
3456
|
-
"output": "(1 xfy 2)
|
|
3456
|
+
"output": "(1 xfy 2)yf",
|
|
3457
3457
|
"variables": "[]"
|
|
3458
3458
|
}
|
|
3459
3459
|
]
|
|
@@ -3587,7 +3587,7 @@
|
|
|
3587
3587
|
"query": "op(9,fy,p),op(9,xfy,p).\nwrite_canonical(1 p p p 2).",
|
|
3588
3588
|
"input": "op(9,fy,p),op(9,xfy,p).\nwrite_canonical(1 p p p 2).",
|
|
3589
3589
|
"readCount": 2,
|
|
3590
|
-
"expected": "p(1,
|
|
3590
|
+
"expected": "p(1,p(p(2)))",
|
|
3591
3591
|
"outcome": {
|
|
3592
3592
|
"type": "success",
|
|
3593
3593
|
"stages": [
|
|
@@ -3607,7 +3607,7 @@
|
|
|
3607
3607
|
"query": "op(7,fy,p),op(9,yfx,p).\nwrite_canonical(1 p p p 2).",
|
|
3608
3608
|
"input": "op(7,fy,p),op(9,yfx,p).\nwrite_canonical(1 p p p 2).",
|
|
3609
3609
|
"readCount": 2,
|
|
3610
|
-
"expected": "p(1,
|
|
3610
|
+
"expected": "p(1,p(p(2)))",
|
|
3611
3611
|
"outcome": {
|
|
3612
3612
|
"type": "success",
|
|
3613
3613
|
"stages": [
|
|
@@ -3695,7 +3695,7 @@
|
|
|
3695
3695
|
"variables": "[]"
|
|
3696
3696
|
},
|
|
3697
3697
|
{
|
|
3698
|
-
"output": "'.'
|
|
3698
|
+
"output": "('.')'.'",
|
|
3699
3699
|
"variables": "[]"
|
|
3700
3700
|
}
|
|
3701
3701
|
]
|
|
@@ -5565,6 +5565,97 @@
|
|
|
5565
5565
|
}
|
|
5566
5566
|
]
|
|
5567
5567
|
}
|
|
5568
|
+
},
|
|
5569
|
+
{
|
|
5570
|
+
"id": 369,
|
|
5571
|
+
"query": "writeq(?-).",
|
|
5572
|
+
"input": "writeq(?-).",
|
|
5573
|
+
"readCount": 1,
|
|
5574
|
+
"expected": "?-",
|
|
5575
|
+
"outcome": {
|
|
5576
|
+
"type": "success",
|
|
5577
|
+
"stages": [
|
|
5578
|
+
{
|
|
5579
|
+
"output": "?-",
|
|
5580
|
+
"variables": "[]"
|
|
5581
|
+
}
|
|
5582
|
+
]
|
|
5583
|
+
}
|
|
5584
|
+
},
|
|
5585
|
+
{
|
|
5586
|
+
"id": 370,
|
|
5587
|
+
"query": "writeq(./*).",
|
|
5588
|
+
"input": "writeq(./*).",
|
|
5589
|
+
"readCount": 1,
|
|
5590
|
+
"expected": "./*",
|
|
5591
|
+
"outcome": {
|
|
5592
|
+
"type": "success",
|
|
5593
|
+
"stages": [
|
|
5594
|
+
{
|
|
5595
|
+
"output": "./*",
|
|
5596
|
+
"variables": "[]"
|
|
5597
|
+
}
|
|
5598
|
+
]
|
|
5599
|
+
}
|
|
5600
|
+
},
|
|
5601
|
+
{
|
|
5602
|
+
"id": 371,
|
|
5603
|
+
"query": "writeq({*}).",
|
|
5604
|
+
"input": "writeq({*}).",
|
|
5605
|
+
"readCount": 1,
|
|
5606
|
+
"expected": "{*}",
|
|
5607
|
+
"outcome": {
|
|
5608
|
+
"type": "success",
|
|
5609
|
+
"stages": [
|
|
5610
|
+
{
|
|
5611
|
+
"output": "{*}",
|
|
5612
|
+
"variables": "[]"
|
|
5613
|
+
}
|
|
5614
|
+
]
|
|
5615
|
+
}
|
|
5616
|
+
},
|
|
5617
|
+
{
|
|
5618
|
+
"id": 372,
|
|
5619
|
+
"query": "writeq(.*).",
|
|
5620
|
+
"input": "writeq(.*).",
|
|
5621
|
+
"readCount": 1,
|
|
5622
|
+
"expected": ".*",
|
|
5623
|
+
"outcome": {
|
|
5624
|
+
"type": "success",
|
|
5625
|
+
"stages": [
|
|
5626
|
+
{
|
|
5627
|
+
"output": ".*",
|
|
5628
|
+
"variables": "[]"
|
|
5629
|
+
}
|
|
5630
|
+
]
|
|
5631
|
+
}
|
|
5632
|
+
},
|
|
5633
|
+
{
|
|
5634
|
+
"id": 373,
|
|
5635
|
+
"query": "writeq(...*).",
|
|
5636
|
+
"input": "writeq(...*).",
|
|
5637
|
+
"readCount": 1,
|
|
5638
|
+
"expected": "...*",
|
|
5639
|
+
"outcome": {
|
|
5640
|
+
"type": "success",
|
|
5641
|
+
"stages": [
|
|
5642
|
+
{
|
|
5643
|
+
"output": "...*",
|
|
5644
|
+
"variables": "[]"
|
|
5645
|
+
}
|
|
5646
|
+
]
|
|
5647
|
+
}
|
|
5648
|
+
},
|
|
5649
|
+
{
|
|
5650
|
+
"id": 374,
|
|
5651
|
+
"query": "Finis ().",
|
|
5652
|
+
"input": "Finis ().",
|
|
5653
|
+
"readCount": 1,
|
|
5654
|
+
"expected": "syntax err.",
|
|
5655
|
+
"outcome": {
|
|
5656
|
+
"type": "error",
|
|
5657
|
+
"formal": "syntax_error(read_term)"
|
|
5658
|
+
}
|
|
5568
5659
|
}
|
|
5569
5660
|
],
|
|
5570
5661
|
"sourceRevision": "1.300",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing",
|
|
3
|
-
"checkedOn": "2026-08-
|
|
3
|
+
"checkedOn": "2026-08-28",
|
|
4
4
|
"upstream": {
|
|
5
5
|
"firstId": 1,
|
|
6
|
-
"lastId":
|
|
6
|
+
"lastId": 374,
|
|
7
7
|
"deletedIds": [
|
|
8
8
|
20,
|
|
9
9
|
273
|
|
10
10
|
],
|
|
11
|
-
"activeCases":
|
|
11
|
+
"activeCases": 372
|
|
12
12
|
},
|
|
13
13
|
"evidence": [
|
|
14
14
|
{
|
|
@@ -69,6 +69,8 @@ export function buildConformanceReport({ wg17Suite = runWg17 } = {}) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export function formatConformanceReport(report = buildConformanceReport()) {
|
|
72
|
+
const wg17 = report.executable.find((gate) => gate.name === 'WG17 syntax');
|
|
73
|
+
const wg17Total = wg17?.total ?? 0;
|
|
72
74
|
const lines = [
|
|
73
75
|
'# EyeProlog conformance report',
|
|
74
76
|
'',
|
|
@@ -89,7 +91,7 @@ export function formatConformanceReport(report = buildConformanceReport()) {
|
|
|
89
91
|
|
|
90
92
|
lines.push(
|
|
91
93
|
'',
|
|
92
|
-
|
|
94
|
+
`The WG17 syntax row executes the vendored ${wg17Total}-case conformity-testing matrix`,
|
|
93
95
|
'against EyeProlog\'s strict ISO reader/writer. A behavior fix such as operator-token',
|
|
94
96
|
'spelling therefore changes this report even when no corpus file is added or removed.',
|
|
95
97
|
'',
|
package/test/run-regression.mjs
CHANGED
|
@@ -4726,6 +4726,54 @@ function documentationSyncCases() {
|
|
|
4726
4726
|
false,
|
|
4727
4727
|
'quoted semicolon is not accepted when the ISO name token is bare',
|
|
4728
4728
|
);
|
|
4729
|
+
const quotedPostfixWriter = {
|
|
4730
|
+
id: 208,
|
|
4731
|
+
query: "op(100,xf,'f ').\nwriteq(0 'f ').",
|
|
4732
|
+
input: "op(100,xf,'f ').\nwriteq(0 'f ').",
|
|
4733
|
+
};
|
|
4734
|
+
assertEqual(
|
|
4735
|
+
matchesUpstreamExpectation(
|
|
4736
|
+
"0 'f '",
|
|
4737
|
+
{ type: 'success', stages: [{ output: "0'f '", variables: '[]' }] },
|
|
4738
|
+
quotedPostfixWriter,
|
|
4739
|
+
),
|
|
4740
|
+
false,
|
|
4741
|
+
'mandatory layout before quoted postfix operator is not erased',
|
|
4742
|
+
);
|
|
4743
|
+
const dottedPostfixWriter = {
|
|
4744
|
+
id: 169,
|
|
4745
|
+
query: 'writeq(.(.)).',
|
|
4746
|
+
input: "op(0,xfy,.),op(9,yf,.).\nwriteq(.(.)).",
|
|
4747
|
+
};
|
|
4748
|
+
assertEqual(
|
|
4749
|
+
matchesUpstreamExpectation(
|
|
4750
|
+
"('.')'.'",
|
|
4751
|
+
{ type: 'success', stages: [{ output: "'.' '.'", variables: '[]' }] },
|
|
4752
|
+
dottedPostfixWriter,
|
|
4753
|
+
),
|
|
4754
|
+
false,
|
|
4755
|
+
'mandatory writer parentheses are not replaced by layout',
|
|
4756
|
+
);
|
|
4757
|
+
const wordPostfixWriter = { id: 150, query: 'writeq(yf(fy(1))).', input: 'writeq(yf(fy(1))).' };
|
|
4758
|
+
assertEqual(
|
|
4759
|
+
matchesUpstreamExpectation(
|
|
4760
|
+
'(fy 1)yf',
|
|
4761
|
+
{ type: 'success', stages: [{ output: '(fy 1) yf', variables: '[]' }] },
|
|
4762
|
+
wordPostfixWriter,
|
|
4763
|
+
),
|
|
4764
|
+
false,
|
|
4765
|
+
'concrete writer layout is matched exactly',
|
|
4766
|
+
);
|
|
4767
|
+
const canonicalWriter = { id: 163, query: 'write_canonical(1 p p p 2).', input: 'write_canonical(1 p p p 2).' };
|
|
4768
|
+
assertEqual(
|
|
4769
|
+
matchesUpstreamExpectation(
|
|
4770
|
+
'p(1,p(p(2)))',
|
|
4771
|
+
{ type: 'success', stages: [{ output: 'p(1, p(p(2)))', variables: '[]' }] },
|
|
4772
|
+
canonicalWriter,
|
|
4773
|
+
),
|
|
4774
|
+
false,
|
|
4775
|
+
'concrete canonical writer layout is matched exactly',
|
|
4776
|
+
);
|
|
4729
4777
|
const repeated = {
|
|
4730
4778
|
id: 227, input: 'write_canonical(B+B).',
|
|
4731
4779
|
outcome: { type: 'success', stages: [{ output: '+(_A,_A)', variables: "['B' = B]" }] },
|
|
@@ -5030,11 +5078,11 @@ function documentationSyncCases() {
|
|
|
5030
5078
|
assertEqual(report.total.positive + report.total.errors + report.total.warnings + report.total.proofs, report.total.total, 'conformance total');
|
|
5031
5079
|
assertEqual(report.rows.some((row) => row.category === 'legacy-numbered'), false, 'legacy-numbered category');
|
|
5032
5080
|
const wg17 = report.executable.find((gate) => gate.name === 'WG17 syntax');
|
|
5033
|
-
assertEqual(wg17?.total,
|
|
5034
|
-
assertEqual(wg17?.passed,
|
|
5081
|
+
assertEqual(wg17?.total, 372, 'WG17 executable total');
|
|
5082
|
+
assertEqual(wg17?.passed, 372, 'WG17 executable passed');
|
|
5035
5083
|
assertArrayEqual(wg17?.failures ?? [], [], 'WG17 executable failures');
|
|
5036
5084
|
const text = formatConformanceReport(report);
|
|
5037
|
-
assertIncludes(text, '| WG17 syntax |
|
|
5085
|
+
assertIncludes(text, '| WG17 syntax | 372 | 372 | pass |', 'report');
|
|
5038
5086
|
assertIncludes(text, '| variables |', 'report');
|
|
5039
5087
|
assertIncludes(text, '| Proofs |', 'report');
|
|
5040
5088
|
assertIncludes(text, '| **Total** |', 'report');
|
package/test/run-wg17.mjs
CHANGED
|
@@ -306,12 +306,26 @@ function abbreviatedErrorMatches(expected, actual) {
|
|
|
306
306
|
return patterns.some(([pattern, prefix]) => pattern.test(expected) && formal.startsWith(prefix));
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
+
function isWriterCase(item) {
|
|
310
|
+
return /\bwrite(?:q|_canonical|_term)?\s*\(/.test(item?.query ?? '');
|
|
311
|
+
}
|
|
312
|
+
|
|
309
313
|
function textExpectationMatches(expectedText, actual, item, example = false) {
|
|
310
314
|
const candidates = observableCandidates(actual);
|
|
311
315
|
if (candidates.length === 0) return false;
|
|
312
316
|
let expected = presentationText(expectedText);
|
|
313
317
|
if (example) expected = expected.replace(/^e\.g\.\s*/i, '');
|
|
314
318
|
|
|
319
|
+
// A concrete WG17 writer expectation describes output syntax, not merely
|
|
320
|
+
// the term denoted by that output. Layout, quoting and parentheses are
|
|
321
|
+
// therefore significant. Only expectations explicitly marked `e.g.` use
|
|
322
|
+
// the controlled semantic/layout fallbacks below.
|
|
323
|
+
const hasBindings = actual.type === 'success' &&
|
|
324
|
+
actual.stages.some(({ variables }) => bindingAnswer(variables).length > 0);
|
|
325
|
+
if (!example && isWriterCase(item) && !hasBindings) {
|
|
326
|
+
return candidates.some((candidate) => presentationText(candidate) === expected);
|
|
327
|
+
}
|
|
328
|
+
|
|
315
329
|
for (const candidate of candidates) {
|
|
316
330
|
let left = expected;
|
|
317
331
|
let right = candidate;
|
|
@@ -95,10 +95,10 @@ This ledger counts an upstream case when its WG17 identifier, query, and
|
|
|
95
95
|
expected ISO disposition are stored in the offline executable matrix. Every
|
|
96
96
|
case is executed against the upstream Codex expectation. Reviewed exact
|
|
97
97
|
EyeProlog outcomes are additional regression locks and can never override the
|
|
98
|
-
upstream assertion.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
upstream assertion. Concrete writer expectations are matched lexically: layout,
|
|
99
|
+
parentheses, and quoting are significant. Only Codex expectations explicitly
|
|
100
|
+
marked e.g. use controlled flexibility for representation choices such as
|
|
101
|
+
generated variable names or equivalent numeric spellings.
|
|
102
102
|
|
|
103
103
|
## Current standing
|
|
104
104
|
|