eyeprolog 1.2.3 → 1.2.5
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/examples/access-control-policy.pl +2 -0
- package/examples/d3-group.pl +2 -0
- package/examples/fast-fourier-transform.pl +1 -0
- package/examples/map-four-color-search.pl +2 -0
- package/examples/partial-evaluator.pl +2 -0
- package/examples/weighted-interval-scheduling.pl +2 -0
- package/package.json +1 -1
- package/src/iso.js +5 -2
- package/src/parser.js +4 -0
- package/src/repl.js +37 -11
- package/src/solver.js +1 -1
- package/src/write.js +2 -0
- package/test/conformance/ISO-COMPLIANCE.md +1 -1
- package/test/conformance/ISO-MATRIX.md +4 -4
- package/test/conformance/README.md +1 -1
- package/test/conformance/cases/aggregation/057_countall_empty_and_nonempty.pl +2 -0
- package/test/conformance/cases/aggregation/075_aggregation_edges.pl +2 -0
- package/test/conformance/cases/aggregation/aggregate_max_no_answers_fails.pl +2 -0
- package/test/conformance/cases/aggregation/countall_zero.pl +2 -0
- package/test/conformance/cases/aggregation/findall_empty_bag.pl +2 -0
- package/test/conformance/cases/aggregation/sumall_empty_zero.pl +2 -0
- package/test/conformance/cases/control/074_forall_edges.pl +2 -0
- package/test/conformance/cases/control/extra_not_unbound_unknown_goal_succeeds.pl +2 -0
- package/test/conformance/cases/control/extra_once_empty_fails.pl +2 -0
- package/test/conformance/cases/control/forall_empty_generator_true.pl +2 -0
- package/test/conformance/cases/iso/wg17_syntax_high_risk.pl +5 -1
- package/test/conformance/cases/negation/not_unknown_goal_succeeds.pl +2 -0
- package/test/conformance/errors/iso/wg17_invalid_octal_escape.pl +1 -0
- package/test/conformance/expected/iso/wg17_syntax_high_risk.pl +2 -0
- package/test/conformance/expected-errors/iso/wg17_invalid_octal_escape.txt +1 -0
- package/test/conformance/warnings/negation/extra_stratified_multiple_negations_quiet.pl +2 -0
- package/test/conformance/warnings/negation/extra_warning_preserves_empty_stdout.pl +2 -0
- package/test/conformance/warnings/negation/negative_unknown_group_quiet.pl +2 -0
- package/test/conformance/warnings/negation/stratified_negative_unknown_after_positive_quiet.pl +2 -0
- package/test/conformance/warnings/negation/stratified_quiet.pl +2 -0
- package/test/run-regression.mjs +83 -1
- package/the-art-of-eyeprolog.md +19 -11
package/conformance-report.md
CHANGED
|
@@ -11,7 +11,7 @@ This report summarizes the file-based conformance corpus under `test/conformance
|
|
|
11
11
|
| builtins | 11 | 0 | 0 | 0 | 11 |
|
|
12
12
|
| context | 11 | 0 | 0 | 0 | 11 |
|
|
13
13
|
| control | 15 | 0 | 0 | 0 | 15 |
|
|
14
|
-
| iso | 167 |
|
|
14
|
+
| iso | 167 | 210 | 0 | 0 | 377 |
|
|
15
15
|
| lists | 52 | 3 | 0 | 0 | 55 |
|
|
16
16
|
| modules | 2 | 0 | 0 | 0 | 2 |
|
|
17
17
|
| negation | 8 | 0 | 19 | 0 | 27 |
|
|
@@ -23,4 +23,4 @@ This report summarizes the file-based conformance corpus under `test/conformance
|
|
|
23
23
|
| terms | 26 | 3 | 0 | 0 | 29 |
|
|
24
24
|
| unification | 18 | 0 | 0 | 0 | 18 |
|
|
25
25
|
| variables | 16 | 9 | 0 | 0 | 25 |
|
|
26
|
-
| **Total** | **482** | **
|
|
26
|
+
| **Total** | **482** | **261** | **19** | **21** | **783** |
|
package/examples/d3-group.pl
CHANGED
|
@@ -72,3 +72,5 @@ residual_program(Name, Residual) :- program(Name, Expr, Env), pe(Env, Expr, Resi
|
|
|
72
72
|
|
|
73
73
|
partialEvalAnswer(residual(Name), Residual) :- residual_program(Name, Residual).
|
|
74
74
|
partialEvalAnswer(note, "static inputs are folded while dynamic variables remain as residual code") :- residual_program(poly_y, _).
|
|
75
|
+
|
|
76
|
+
:- set_prolog_flag(unknown, fail).
|
|
@@ -82,3 +82,5 @@ weighted_interval_answer(chosen_interval, interval(I, Start, Finish, Value)) :-
|
|
|
82
82
|
chosen_from(1, I),
|
|
83
83
|
interval(I, Start, Finish, Value).
|
|
84
84
|
weighted_interval_answer(candidate_count, Count) :- countall(interval(_i, _start, _finish, _value), Count).
|
|
85
|
+
|
|
86
|
+
:- set_prolog_flag(unknown, fail).
|
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -1075,9 +1075,12 @@ function quotedEscapeEnd(source, index) {
|
|
|
1075
1075
|
while (/^[0-9A-Fa-f]$/.test(source[cursor] ?? '')) cursor++;
|
|
1076
1076
|
return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
|
|
1077
1077
|
}
|
|
1078
|
-
if (/^[0-
|
|
1078
|
+
if (/^[0-9]$/.test(escaped)) {
|
|
1079
1079
|
let cursor = index + 1;
|
|
1080
|
-
|
|
1080
|
+
// Scan all decimal digits here, including 8 and 9. This scanner only
|
|
1081
|
+
// locates the end of a candidate quoted escape; the parser remains
|
|
1082
|
+
// authoritative and rejects non-octal digits.
|
|
1083
|
+
while (/^[0-9]$/.test(source[cursor] ?? '')) cursor++;
|
|
1081
1084
|
return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
|
|
1082
1085
|
}
|
|
1083
1086
|
|
package/src/parser.js
CHANGED
|
@@ -303,6 +303,10 @@ class Parser {
|
|
|
303
303
|
if (this.take() !== '\\') throw new Error(`parse line ${line}: bad octal escape`);
|
|
304
304
|
return String.fromCodePoint(Number.parseInt(digits, 8));
|
|
305
305
|
}
|
|
306
|
+
// A backslash followed by a decimal digit is numeric-escape syntax, but
|
|
307
|
+
// ISO octal digits are limited to 0..7. Do not reinterpret \8 or \9 as
|
|
308
|
+
// implementation-specific one-character escapes.
|
|
309
|
+
if (/^[0-9]$/.test(escaped)) throw new Error(`parse line ${line}: bad octal escape`);
|
|
306
310
|
return escaped;
|
|
307
311
|
}
|
|
308
312
|
nextToken() {
|
package/src/repl.js
CHANGED
|
@@ -34,7 +34,7 @@ export async function runRepl(engine, options = {}) {
|
|
|
34
34
|
const goal = parseGoal(engine, state, text);
|
|
35
35
|
if (!options.isoStrict && isUseModuleGoal(goal)) {
|
|
36
36
|
sources.push({ text: `:- ${text}.\n`, filename: '<repl>' });
|
|
37
|
-
state = makeState(engine, sources, output, options);
|
|
37
|
+
state = makeState(engine, sources, output, options, state);
|
|
38
38
|
state.solver.runInitializations();
|
|
39
39
|
output.write(' true.\n');
|
|
40
40
|
continue;
|
|
@@ -42,17 +42,22 @@ export async function runRepl(engine, options = {}) {
|
|
|
42
42
|
const consultFiles = options.isoStrict ? null : consultDesignations(engine, goal);
|
|
43
43
|
if (consultFiles != null) {
|
|
44
44
|
for (const filename of consultFiles) sources.push(await readSource(filename));
|
|
45
|
-
state = makeState(engine, sources, output, options);
|
|
45
|
+
state = makeState(engine, sources, output, options, state);
|
|
46
46
|
state.solver.runInitializations();
|
|
47
47
|
output.write(' true.\n');
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
const flagsBefore = snapshotFlagValues(state.solver);
|
|
52
|
+
try {
|
|
53
|
+
await prepareInteractiveTermInput(state, goal, reader);
|
|
54
|
+
const result = await solveQuery(engine, state, goal, reader, output);
|
|
55
|
+
if (result?.halted) {
|
|
56
|
+
exitCode = result.code;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
rememberFlagOverrides(state, flagsBefore);
|
|
56
61
|
}
|
|
57
62
|
} catch (error) {
|
|
58
63
|
if (error?.name === 'HaltSignal') {
|
|
@@ -143,7 +148,7 @@ class LineReader {
|
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
150
|
|
|
146
|
-
function makeState(engine, sources, output, options = {}) {
|
|
151
|
+
function makeState(engine, sources, output, options = {}, previousState = null) {
|
|
147
152
|
const strictIso = options.isoStrict === true;
|
|
148
153
|
const program = engine.Program.parseSources(sources, { strictIso, sourceMetadata: strictIso });
|
|
149
154
|
const solver = new engine.Solver(program, {
|
|
@@ -151,7 +156,25 @@ function makeState(engine, sources, output, options = {}) {
|
|
|
151
156
|
isoStrict: strictIso,
|
|
152
157
|
ioOptions: { write: (text) => output.write(String(text)) },
|
|
153
158
|
});
|
|
154
|
-
|
|
159
|
+
const flagOverrides = new Map(previousState?.flagOverrides ?? []);
|
|
160
|
+
for (const [name, value] of flagOverrides) {
|
|
161
|
+
const definition = solver.prologFlags.get(name);
|
|
162
|
+
if (definition?.changeable && definition.allowed.includes(value)) {
|
|
163
|
+
definition.value = engine.atom(value);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { program: solver.program, solver, strictIso, flagOverrides };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function snapshotFlagValues(solver) {
|
|
170
|
+
return new Map([...solver.prologFlags].map(([name, definition]) => [name, definition.value?.name]));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function rememberFlagOverrides(state, before) {
|
|
174
|
+
for (const [name, definition] of state.solver.prologFlags) {
|
|
175
|
+
const value = definition.value?.name;
|
|
176
|
+
if (before.get(name) !== value) state.flagOverrides.set(name, value);
|
|
177
|
+
}
|
|
155
178
|
}
|
|
156
179
|
|
|
157
180
|
async function readQuery(reader) {
|
|
@@ -230,9 +253,12 @@ function quotedEscapeEnd(source, index) {
|
|
|
230
253
|
while (/^[0-9A-Fa-f]$/.test(source[cursor] ?? '')) cursor++;
|
|
231
254
|
return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
|
|
232
255
|
}
|
|
233
|
-
if (/^[0-
|
|
256
|
+
if (/^[0-9]$/.test(escaped)) {
|
|
234
257
|
let cursor = index + 1;
|
|
235
|
-
|
|
258
|
+
// Scan all decimal digits here, including 8 and 9. This scanner only
|
|
259
|
+
// locates the end of a candidate quoted escape; the parser remains
|
|
260
|
+
// authoritative and rejects non-octal digits.
|
|
261
|
+
while (/^[0-9]$/.test(source[cursor] ?? '')) cursor++;
|
|
236
262
|
return source[cursor] === '\\' ? cursor : Math.max(index + 1, cursor - 1);
|
|
237
263
|
}
|
|
238
264
|
|
package/src/solver.js
CHANGED
|
@@ -49,7 +49,7 @@ export class Solver {
|
|
|
49
49
|
this.inferenceLimitExceeded = false;
|
|
50
50
|
this.solutionLimit = options.solutionLimit ?? 10000000;
|
|
51
51
|
this.solutionsSeen = 0;
|
|
52
|
-
this.prologFlags = options.prologFlags ?? defaultPrologFlags(
|
|
52
|
+
this.prologFlags = options.prologFlags ?? defaultPrologFlags('error', this.isoStrict);
|
|
53
53
|
if (this.isoStrict) {
|
|
54
54
|
for (const name of [...this.prologFlags.keys()]) {
|
|
55
55
|
if (!ISO_CORE_FLAG_NAMES.has(name)) this.prologFlags.delete(name);
|
package/src/write.js
CHANGED
|
@@ -22,6 +22,7 @@ function quoteAtom(name) {
|
|
|
22
22
|
for (const ch of name) {
|
|
23
23
|
if (ch === "'") out += "''";
|
|
24
24
|
else if (ch === '\\') out += '\\\\';
|
|
25
|
+
else if (ch === '\x00') out += '\\0\\';
|
|
25
26
|
else if (ch === '\x07') out += '\\a';
|
|
26
27
|
else if (ch === '\b') out += '\\b';
|
|
27
28
|
else if (ch === '\r') out += '\\r';
|
|
@@ -59,6 +60,7 @@ function writeString(value) {
|
|
|
59
60
|
let out = '"';
|
|
60
61
|
for (const ch of value) {
|
|
61
62
|
if (ch === '"' || ch === '\\') out += `\\${ch}`;
|
|
63
|
+
else if (ch === '\x00') out += '\\0\\';
|
|
62
64
|
else if (ch === '\x07') out += '\\a';
|
|
63
65
|
else if (ch === '\b') out += '\\b';
|
|
64
66
|
else if (ch === '\r') out += '\\r';
|
|
@@ -38,7 +38,7 @@ error-ordering alternative to an individual executable assertion.
|
|
|
38
38
|
| 7.8 — control constructs and exceptions | audit | call, cut, conjunction, disjunction, 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.
|
|
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. |
|
|
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. |
|
|
@@ -8,7 +8,7 @@ compliance audit and the remaining work before a full conformance claim.
|
|
|
8
8
|
|
|
9
9
|
| Standard area | Implementation | Representative executable coverage |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| Clause 6 lexical and term syntax | tokenizer, operator parser, lists, curly terms, quotes, numeric syntax, comments | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, syntax error cases |
|
|
11
|
+
| Clause 6 lexical and term syntax | tokenizer, operator parser, lists, curly terms, quotes, numeric syntax, comments | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, `wg17_invalid_octal_escape`, syntax error cases |
|
|
12
12
|
| Clause 7 term order and unification | finite-tree unification, identity, standard order, errors | `unification_control_information`, `swipl_occurs_check`, `term_modes_and_ordering`, `logtalk_compare_standard_order` |
|
|
13
13
|
| Clause 7 control and exceptions | call, cut, conjunction, disjunction, if-then-else, catch and throw | `cut_control`, `control_and_terms`, `exceptions_and_flags`, `corrigenda_catch_callability`, `throw_copies_ball` |
|
|
14
14
|
| 8.2-8.5 term predicates | unification, Corrigendum 2 tests, comparison, sorting, creation and decomposition | `corrigenda_term_predicates`, `corrigenda_sort_keysort`, `logtalk_arg_unification`, `logtalk_univ`, associated error cases |
|
|
@@ -31,9 +31,9 @@ integer powers (Cor.3).
|
|
|
31
31
|
Implementation-defined choices are documented in *The Art of EyeProlog*:
|
|
32
32
|
integers and arity are unbounded by the Prolog model (subject to host memory),
|
|
33
33
|
ordinary unification performs an occurs check, `double_quotes` defaults to
|
|
34
|
-
`chars`, strict core mode
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
`chars`, both strict core mode and normal EyeProlog use the ISO
|
|
35
|
+
`unknown=error` default, `//` rounds toward zero, floating-point operations use
|
|
36
|
+
finite ECMAScript numbers, and character codes use Unicode scalar values.
|
|
37
37
|
|
|
38
38
|
This is an executable conformance matrix, not a certification issued by an
|
|
39
39
|
independent standards body. Release gating runs the ISO cases and the dedicated
|
|
@@ -102,7 +102,7 @@ Selected cases are adapted from the ISO and standard-core suites of Logtalk,
|
|
|
102
102
|
Scryer Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
|
|
103
103
|
are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
|
|
104
104
|
|
|
105
|
-
The corpus has
|
|
105
|
+
The corpus has 377 cases in `iso/` and 783 file-based conformance cases in
|
|
106
106
|
total. The generated `conformance-report.md` is the authoritative source for
|
|
107
107
|
current category totals. Together with regression, documentation-sync, API,
|
|
108
108
|
example, and book-example checks, `npm test` is the release gate.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
% High-risk ISO syntax/write regressions, independently derived from
|
|
2
2
|
% ISO/IEC 13211-1 clauses 6.3, 6.4 and 7.10 and cross-checked against the
|
|
3
|
-
% public WG17 conformity-testing syntax cases (#1, #14-15, #28-31, #33-34).
|
|
3
|
+
% public WG17 conformity-testing syntax cases (#1, #14-15, #28-31, #33-34, #301).
|
|
4
4
|
|
|
5
5
|
%% goal: wg17_numeric_escape
|
|
6
6
|
wg17_numeric_escape :-
|
|
@@ -24,3 +24,7 @@ wg17_operator_precedence :-
|
|
|
24
24
|
%% goal: wg17_canonical_list
|
|
25
25
|
wg17_canonical_list :-
|
|
26
26
|
write_canonical([a]), nl.
|
|
27
|
+
|
|
28
|
+
%% goal: wg17_zero_character_escape
|
|
29
|
+
wg17_zero_character_escape :-
|
|
30
|
+
writeq('\0\'), nl.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'\8\'.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
parse line 1: bad octal escape
|
package/test/run-regression.mjs
CHANGED
|
@@ -671,6 +671,31 @@ c4 ?- call((!;1)).
|
|
|
671
671
|
assertEqual(result.stderr, '', 'stderr');
|
|
672
672
|
},
|
|
673
673
|
},
|
|
674
|
+
{
|
|
675
|
+
name: 'writeq preserves the NUL character with an ISO octal escape',
|
|
676
|
+
run: () => {
|
|
677
|
+
const result = runCli([], {
|
|
678
|
+
input: "writeq('\\0\\').\nhalt.\n",
|
|
679
|
+
});
|
|
680
|
+
assertEqual(result.status, 0, 'exit status');
|
|
681
|
+
assertEqual(result.stdout, "?- '\\0\\' true.\n?- ", 'stdout');
|
|
682
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
name: 'REPL rejects non-octal numeric escapes without waiting for continuation',
|
|
687
|
+
run: () => {
|
|
688
|
+
const result = runCli([], {
|
|
689
|
+
input: "'\\8\\'.\nhalt.\n",
|
|
690
|
+
});
|
|
691
|
+
assertEqual(result.status, 0, 'exit status');
|
|
692
|
+
assertEqual(result.stdout,
|
|
693
|
+
'?- parse line 1: bad octal escape.\n' +
|
|
694
|
+
'?- ',
|
|
695
|
+
'stdout');
|
|
696
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
697
|
+
},
|
|
698
|
+
},
|
|
674
699
|
{
|
|
675
700
|
name: 'REPL read predicates consume following interactive term input',
|
|
676
701
|
run: () => {
|
|
@@ -716,6 +741,36 @@ c4 ?- call((!;1)).
|
|
|
716
741
|
assertEqual(result.stderr, '', 'stderr');
|
|
717
742
|
},
|
|
718
743
|
},
|
|
744
|
+
{
|
|
745
|
+
name: 'REPL preserves runtime unknown flag across consultation',
|
|
746
|
+
run: () => {
|
|
747
|
+
const filename = path.join(tmp, `repl-empty-${++tmpCounter}.pl`);
|
|
748
|
+
fs.writeFileSync(filename, '');
|
|
749
|
+
const result = runCli([], {
|
|
750
|
+
input:
|
|
751
|
+
'current_prolog_flag(unknown, V).\n' +
|
|
752
|
+
'set_prolog_flag(unknown, fail).\n' +
|
|
753
|
+
`[${sourceAtom(filename)}].\n` +
|
|
754
|
+
'current_prolog_flag(unknown, V).\n' +
|
|
755
|
+
'set_prolog_flag(unknown, error).\n' +
|
|
756
|
+
`[${sourceAtom(filename)}].\n` +
|
|
757
|
+
'missing_after_consult.\n' +
|
|
758
|
+
'halt.\n',
|
|
759
|
+
});
|
|
760
|
+
assertEqual(result.status, 0, 'exit status');
|
|
761
|
+
assertEqual(result.stdout,
|
|
762
|
+
'?- V = error.\n' +
|
|
763
|
+
'?- true.\n' +
|
|
764
|
+
'?- true.\n' +
|
|
765
|
+
'?- V = fail.\n' +
|
|
766
|
+
'?- true.\n' +
|
|
767
|
+
'?- true.\n' +
|
|
768
|
+
'?- error(existence_error(procedure, missing_after_consult / 0), eyeprolog).\n' +
|
|
769
|
+
'?- ',
|
|
770
|
+
'stdout');
|
|
771
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
772
|
+
},
|
|
773
|
+
},
|
|
719
774
|
{
|
|
720
775
|
name: 'REPL use_module imports Part 2 library predicates',
|
|
721
776
|
run: () => {
|
|
@@ -723,7 +778,11 @@ c4 ?- call((!;1)).
|
|
|
723
778
|
input: 'append(X, Y, [1, 2, 3, 4]).\nuse_module(library(lists)).\nappend(X, Y, [1, 2, 3, 4]).\n\nhalt.\n',
|
|
724
779
|
});
|
|
725
780
|
assertEqual(result.status, 0, 'exit status');
|
|
726
|
-
assertEqual(result.stdout,
|
|
781
|
+
assertEqual(result.stdout,
|
|
782
|
+
'?- error(existence_error(procedure, append / 3), eyeprolog).\n' +
|
|
783
|
+
'?- true.\n' +
|
|
784
|
+
'?- X = [], Y = [1, 2, 3, 4]\n; ... .\n?- ',
|
|
785
|
+
'stdout');
|
|
727
786
|
assertEqual(result.stderr, '', 'stderr');
|
|
728
787
|
},
|
|
729
788
|
},
|
|
@@ -1120,6 +1179,19 @@ c4 ?- call((!;1)).
|
|
|
1120
1179
|
assertEqual(infix.arity, 2, 'quad query arity');
|
|
1121
1180
|
},
|
|
1122
1181
|
},
|
|
1182
|
+
{
|
|
1183
|
+
name: 'normal EyeProlog uses the ISO unknown=error default',
|
|
1184
|
+
run: () => {
|
|
1185
|
+
const program = Program.parse('');
|
|
1186
|
+
const solver = new Solver(program, { registry: getEyePrologRegistry() });
|
|
1187
|
+
assertEqual(solver.prologFlags.get('unknown')?.value?.name, 'error', 'normal unknown default');
|
|
1188
|
+
assertEqual(
|
|
1189
|
+
run('', { goal: 'current_prolog_flag(unknown, V)' }).stdout,
|
|
1190
|
+
'current_prolog_flag(unknown, error).\n',
|
|
1191
|
+
'public runner unknown default',
|
|
1192
|
+
);
|
|
1193
|
+
},
|
|
1194
|
+
},
|
|
1123
1195
|
{
|
|
1124
1196
|
name: 'strict ISO core mode exposes only the Part 1 registry and flag surface',
|
|
1125
1197
|
run: () => {
|
|
@@ -1265,6 +1337,16 @@ c4 ?- call((!;1)).
|
|
|
1265
1337
|
"answer('\\a').\n",
|
|
1266
1338
|
'read/1 user_input numeric escape',
|
|
1267
1339
|
);
|
|
1340
|
+
|
|
1341
|
+
const invalidOctal = String.fromCharCode(39, 92, 56, 92, 39, 46);
|
|
1342
|
+
assertEqual(
|
|
1343
|
+
run('answer(T) :- catch(read(T), E, T=E).\n', {
|
|
1344
|
+
goal: 'answer(T)',
|
|
1345
|
+
ioOptions: { input: invalidOctal },
|
|
1346
|
+
}).stdout,
|
|
1347
|
+
'answer(error(syntax_error(read_term), eyeprolog)).\n',
|
|
1348
|
+
'read/1 rejects non-octal numeric escape',
|
|
1349
|
+
);
|
|
1268
1350
|
},
|
|
1269
1351
|
},
|
|
1270
1352
|
{
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5279,7 +5279,10 @@ message("café").
|
|
|
5279
5279
|
Inside a quoted atom, a single quote is doubled: `'don''t'`. Quoted characters
|
|
5280
5280
|
support the ISO symbolic control escapes such as `\a`, `\n`, and `\t`, and numeric
|
|
5281
5281
|
octal or hexadecimal escapes are terminated by a backslash; for example, `'\7\'`
|
|
5282
|
-
and `'\x7\'` both denote the alert character.
|
|
5282
|
+
and `'\x7\'` both denote the alert character. The NUL character, when present in
|
|
5283
|
+
the processor character set, is written readably as `'\0\'`; digits `8` and `9`
|
|
5284
|
+
are not octal digits, so forms such as `'\8\'` are syntax errors rather than
|
|
5285
|
+
continuation input. Double-quoted lists use the same
|
|
5283
5286
|
quoted-character escapes. Whitespace is insignificant
|
|
5284
5287
|
between tokens, and a `%` comment continues to the end of its line. Doubling
|
|
5285
5288
|
the active delimiter is also accepted inside either quoted form, so `""`
|
|
@@ -5667,18 +5670,23 @@ silently changing a static program.
|
|
|
5667
5670
|
| `max_integer` | `unbounded` | `unbounded` | no |
|
|
5668
5671
|
| `min_integer` | `unbounded` | `unbounded` | no |
|
|
5669
5672
|
| `max_arity` | `unbounded` | `unbounded` | no |
|
|
5670
|
-
| `unknown` | `
|
|
5673
|
+
| `unknown` | `error` | `error`, `fail`, `warning` | yes |
|
|
5671
5674
|
| `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
|
|
5672
5675
|
| `occurs_check` | `true` | `true`, `error` | yes |
|
|
5673
5676
|
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5677
|
+
Both normal EyeProlog and strict ISO core mode use the ISO `unknown=error`
|
|
5678
|
+
default. Interactive `set_prolog_flag/2` changes are retained when the REPL
|
|
5679
|
+
consults another file or imports a module instead of being reset by the host
|
|
5680
|
+
rebuild of the program. Programs that intentionally treat an undefined
|
|
5681
|
+
predicate as failure must opt in with `set_prolog_flag(unknown, fail)`; bundled
|
|
5682
|
+
examples and non-ISO corpus cases that depend on that policy do so explicitly.
|
|
5683
|
+
The `occurs_check` flag is an EyeProlog diagnostic
|
|
5684
|
+
extension rather than an ISO-defined core flag: it is absent in strict mode,
|
|
5685
|
+
while normal mode keeps its `true` default and optional `error` diagnostic for
|
|
5686
|
+
STO attempts. Operator and flag directives are processed per program rather
|
|
5687
|
+
than changing global JavaScript state. The `double_quotes` setting affects
|
|
5688
|
+
subsequent source text, included files, command-line and API goal text, and
|
|
5689
|
+
terms read by `read_term/*`:
|
|
5682
5690
|
|
|
5683
5691
|
```text
|
|
5684
5692
|
% Default: a list of one-character atoms.
|
|
@@ -6941,7 +6949,7 @@ precedence still need one-by-one closure. `test/conformance/ISO-MATRIX.md`
|
|
|
6941
6949
|
maps language families to representative executable cases.
|
|
6942
6950
|
|
|
6943
6951
|
The complete suite must pass before release. The file-based conformance corpus
|
|
6944
|
-
contains
|
|
6952
|
+
contains 783 cases, including 377 focused ISO
|
|
6945
6953
|
cases derived from the success, failure, mode, and error behavior in
|
|
6946
6954
|
ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
|
|
6947
6955
|
Separate exact-output suites check 189 normal
|