eyeprolog 1.3.83 → 1.3.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/iso.js +14 -22
- package/src/repl.js +18 -44
- package/src/solver.js +12 -39
- package/test/conformance/ISO-COMPLIANCE.md +2 -2
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +2 -2
- package/test/conformance/expected/iso/exceptions_and_flags.pl +1 -1
- package/test/run-iso-strict.mjs +0 -9
- package/test/run-regression.mjs +13 -62
- package/the-art-of-eyeprolog.md +8 -27
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -804,7 +804,18 @@ function* currentPrologFlagBuiltin({ solver, goal, env }) {
|
|
|
804
804
|
}
|
|
805
805
|
|
|
806
806
|
function* setPrologFlagBuiltin({ solver, goal, env }) {
|
|
807
|
-
|
|
807
|
+
const flag = deref(goal.args[0], env);
|
|
808
|
+
const value = deref(goal.args[1], env);
|
|
809
|
+
if (flag.type === VAR || value.type === VAR) throw new PrologError('instantiation_error');
|
|
810
|
+
if (flag.type !== ATOM) throw new PrologError('type_error(atom)', flag);
|
|
811
|
+
const definition = solver.prologFlags.get(flag.name);
|
|
812
|
+
if (!definition) throw new PrologError('domain_error(prolog_flag)', flag);
|
|
813
|
+
const expectedType = definition.valueType ?? ATOM;
|
|
814
|
+
if (value.type !== expectedType || !definition.allowed.includes(value.name)) {
|
|
815
|
+
throw new PrologError('domain_error(flag_value)', compound('+', [flag, value]));
|
|
816
|
+
}
|
|
817
|
+
if (!definition.changeable) throw new PrologError('permission_error(modify, flag)', flag);
|
|
818
|
+
definition.value = atom(value.name);
|
|
808
819
|
yield env;
|
|
809
820
|
}
|
|
810
821
|
|
|
@@ -1709,15 +1720,6 @@ function writeOptionBoolean(value, env, option) {
|
|
|
1709
1720
|
return value.name === 'true';
|
|
1710
1721
|
}
|
|
1711
1722
|
|
|
1712
|
-
function writeOptionSpacing(value, env, option) {
|
|
1713
|
-
value = deref(value, env);
|
|
1714
|
-
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
1715
|
-
if (value.type !== ATOM || !['standard', 'minimal'].includes(value.name)) {
|
|
1716
|
-
throw new PrologError('domain_error(write_option)', copyResolved(option, env));
|
|
1717
|
-
}
|
|
1718
|
-
return value.name;
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
1723
|
function writeVariableNames(value, env, option) {
|
|
1722
1724
|
value = deref(value, env);
|
|
1723
1725
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
@@ -1746,10 +1748,8 @@ function writeVariableNames(value, env, option) {
|
|
|
1746
1748
|
return names;
|
|
1747
1749
|
}
|
|
1748
1750
|
|
|
1749
|
-
function termWriteOptionsFromItems(options, env, mode = 'write_term', solver = null
|
|
1750
|
-
const result =
|
|
1751
|
-
? defaultTermWriteOptions(mode)
|
|
1752
|
-
: { ...initial, variableNames: new Map(initial.variableNames ?? []) };
|
|
1751
|
+
function termWriteOptionsFromItems(options, env, mode = 'write_term', solver = null) {
|
|
1752
|
+
const result = defaultTermWriteOptions(mode);
|
|
1753
1753
|
for (const option of options) {
|
|
1754
1754
|
if (option.type !== COMPOUND || option.arity !== 1) {
|
|
1755
1755
|
throw new PrologError('domain_error(write_option)', copyResolved(option, env));
|
|
@@ -1760,8 +1760,6 @@ function termWriteOptionsFromItems(options, env, mode = 'write_term', solver = n
|
|
|
1760
1760
|
else if (option.name === 'variable_names') result.variableNames = writeVariableNames(option.args[0], env, option);
|
|
1761
1761
|
else if (option.name === 'double_quotes' && !solver?.isoStrict) {
|
|
1762
1762
|
result.doubleQuotes = writeOptionBoolean(option.args[0], env, option);
|
|
1763
|
-
} else if (option.name === 'spacing' && !solver?.isoStrict) {
|
|
1764
|
-
result.minimalOperatorSpacing = writeOptionSpacing(option.args[0], env, option) === 'minimal';
|
|
1765
1763
|
} else {
|
|
1766
1764
|
throw new PrologError('domain_error(write_option)', copyResolved(option, env));
|
|
1767
1765
|
}
|
|
@@ -1769,12 +1767,6 @@ function termWriteOptionsFromItems(options, env, mode = 'write_term', solver = n
|
|
|
1769
1767
|
return result;
|
|
1770
1768
|
}
|
|
1771
1769
|
|
|
1772
|
-
export function parseTermWriteOptions(optionTerm, env, mode = 'write_term', solver = null, initial = null) {
|
|
1773
|
-
const preflight = preflightOptionList(optionTerm, env);
|
|
1774
|
-
const items = requireProperOptionList(preflight);
|
|
1775
|
-
return termWriteOptionsFromItems(items, env, mode, solver, initial);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
1770
|
|
|
1779
1771
|
function writeBuiltin(mode) {
|
|
1780
1772
|
return function* ({ solver, goal, env }) {
|
package/src/repl.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
|
|
|
3
3
|
import { readSync } from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { createInterface } from 'node:readline';
|
|
6
|
-
import { formalErrorTerm
|
|
6
|
+
import { formalErrorTerm } from './iso.js';
|
|
7
7
|
import {
|
|
8
8
|
characterCodeConstantEnd, continuesGraphicToken, isTerminatingFullStop, quotedEscapeEnd,
|
|
9
9
|
} from './syntax-scan.js';
|
|
@@ -303,18 +303,20 @@ function makeState(engine, sources, output, options = {}, previousState = null,
|
|
|
303
303
|
const flagOverrides = new Map(previousState?.flagOverrides ?? []);
|
|
304
304
|
for (const [name, value] of flagOverrides) {
|
|
305
305
|
const definition = solver.prologFlags.get(name);
|
|
306
|
-
if (definition?.changeable
|
|
306
|
+
if (definition?.changeable && definition.allowed.includes(value)) {
|
|
307
|
+
definition.value = engine.atom(value);
|
|
308
|
+
}
|
|
307
309
|
}
|
|
308
310
|
return { program: solver.program, solver, strictIso, flagOverrides };
|
|
309
311
|
}
|
|
310
312
|
|
|
311
313
|
function snapshotFlagValues(solver) {
|
|
312
|
-
return new Map([...solver.prologFlags].map(([name, definition]) => [name, definition.value]));
|
|
314
|
+
return new Map([...solver.prologFlags].map(([name, definition]) => [name, definition.value?.name]));
|
|
313
315
|
}
|
|
314
316
|
|
|
315
317
|
function rememberFlagOverrides(state, before) {
|
|
316
318
|
for (const [name, definition] of state.solver.prologFlags) {
|
|
317
|
-
const value = definition.value;
|
|
319
|
+
const value = definition.value?.name;
|
|
318
320
|
if (before.get(name) !== value) state.flagOverrides.set(name, value);
|
|
319
321
|
}
|
|
320
322
|
}
|
|
@@ -718,7 +720,15 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
718
720
|
const valueMaxPriority = equality == null
|
|
719
721
|
? 699
|
|
720
722
|
: equality.specifier === 'xfy' ? equality.priority : equality.priority - 1;
|
|
721
|
-
const
|
|
723
|
+
const answerWriteOptions = {
|
|
724
|
+
quoted: true,
|
|
725
|
+
minimalOperatorSpacing: true,
|
|
726
|
+
operators,
|
|
727
|
+
variableNames: names,
|
|
728
|
+
operatorAtomsAsArgs: true,
|
|
729
|
+
dottedGraphicAtoms: true,
|
|
730
|
+
doubleQuotes: state.solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
731
|
+
};
|
|
722
732
|
let generated = 0;
|
|
723
733
|
|
|
724
734
|
// Meta-predicate wrappers can make a query variable alias an internal fresh
|
|
@@ -733,28 +743,18 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
733
743
|
if (value.type === 'var' &&
|
|
734
744
|
(value.name === variable.name || !queryVariableNames.has(value.name))) continue;
|
|
735
745
|
bindings.push(`${variable.name} = ${engine.formatTermForWrite(value, env, {
|
|
736
|
-
...
|
|
737
|
-
operators,
|
|
738
|
-
variableNames: names,
|
|
746
|
+
...answerWriteOptions,
|
|
739
747
|
maxPriority: valueMaxPriority,
|
|
740
748
|
// A binding value is the right-hand `arg` of =/2. ISO 6.3.3.1 permits
|
|
741
749
|
// current operator atoms in argument and list-element positions without
|
|
742
750
|
// quotes, just as writeq/1 already prints them.
|
|
743
|
-
operatorAtomsAsArgs: true,
|
|
744
|
-
dottedGraphicAtoms: true,
|
|
745
751
|
})}`);
|
|
746
752
|
}
|
|
747
753
|
for (const constraint of env.variableConstraints?.() ?? []) {
|
|
748
754
|
const residual = constraint.residualGoal?.(env);
|
|
749
755
|
if (residual == null) continue;
|
|
750
756
|
collectUnboundVariables(engine, residual, env, names, () => `_${letterName(generated++)}`);
|
|
751
|
-
bindings.push(engine.formatTermForWrite(residual, env,
|
|
752
|
-
...writeOptions,
|
|
753
|
-
operators,
|
|
754
|
-
variableNames: names,
|
|
755
|
-
operatorAtomsAsArgs: true,
|
|
756
|
-
dottedGraphicAtoms: true,
|
|
757
|
-
}));
|
|
757
|
+
bindings.push(engine.formatTermForWrite(residual, env, answerWriteOptions));
|
|
758
758
|
}
|
|
759
759
|
const projectedAttributeRoots = new Set();
|
|
760
760
|
for (const variable of variables) {
|
|
@@ -764,38 +764,12 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
764
764
|
names.set(root.name, variable.name);
|
|
765
765
|
for (const residual of state.solver.attributeResidualGoals(root, env)) {
|
|
766
766
|
collectUnboundVariables(engine, residual, env, names, () => `_${letterName(generated++)}`);
|
|
767
|
-
bindings.push(engine.formatTermForWrite(residual, env,
|
|
768
|
-
...writeOptions,
|
|
769
|
-
operators,
|
|
770
|
-
variableNames: names,
|
|
771
|
-
operatorAtomsAsArgs: true,
|
|
772
|
-
dottedGraphicAtoms: true,
|
|
773
|
-
}));
|
|
767
|
+
bindings.push(engine.formatTermForWrite(residual, env, answerWriteOptions));
|
|
774
768
|
}
|
|
775
769
|
}
|
|
776
770
|
return bindings.length === 0 ? 'true' : bindings.join(', ');
|
|
777
771
|
}
|
|
778
772
|
|
|
779
|
-
function answerTermWriteOptions(state, env) {
|
|
780
|
-
const doubleQuotes = state.solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars';
|
|
781
|
-
const initial = {
|
|
782
|
-
quoted: true,
|
|
783
|
-
ignoreOps: false,
|
|
784
|
-
numbervars: true,
|
|
785
|
-
variableNames: new Map(),
|
|
786
|
-
compact: false,
|
|
787
|
-
minimalOperatorSpacing: false,
|
|
788
|
-
operatorAtomsAsArgs: true,
|
|
789
|
-
doubleQuotes,
|
|
790
|
-
};
|
|
791
|
-
const optionTerm = state.solver.prologFlags.get('answer_write_options')?.value;
|
|
792
|
-
if (optionTerm == null) return initial;
|
|
793
|
-
const options = parseTermWriteOptions(optionTerm, env, 'write_term', state.solver, initial);
|
|
794
|
-
if (options.doubleQuotes === true) options.doubleQuotes = doubleQuotes;
|
|
795
|
-
else if (options.doubleQuotes === false) options.doubleQuotes = null;
|
|
796
|
-
return options;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
773
|
function collectUnboundVariables(engine, term, env, names, nextName) {
|
|
800
774
|
const stack = [term];
|
|
801
775
|
const seen = new Set();
|
package/src/solver.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Depth-first EyeProlog solver with builtin dispatch, memoization, and guarded recursion handling.
|
|
2
2
|
// Most semantic decisions still flow through unification; optimizations only select candidates earlier.
|
|
3
3
|
import {
|
|
4
|
-
ATOM, COMPOUND, NUMBER, STRING, VAR, Env,
|
|
4
|
+
ATOM, COMPOUND, NUMBER, STRING, VAR, Env, compactListLength, compactVariableList, compound, cons, copyResolved, deref, emptyList,
|
|
5
5
|
flattenConjunction, freshTerm, isCons, isDecimalInteger, isEmptyList,
|
|
6
|
-
|
|
6
|
+
numberTerm, numberTextFromDouble, properListItems, termIsGround, termToString, unify, variable, variantTerms,
|
|
7
7
|
} from './term.js';
|
|
8
8
|
import { numberValueKey, sameNumberValue } from './number-value.js';
|
|
9
|
-
import { PrologError, getStrictIsoRegistry
|
|
9
|
+
import { PrologError, getStrictIsoRegistry } from './iso.js';
|
|
10
10
|
import { getEyePrologRegistry } from './standard-library.js';
|
|
11
11
|
import { selectClauseCandidates, selectClauseCandidatesForValues, selectGroundClauseCandidates } from './program-indexing.js';
|
|
12
12
|
import { StreamManager } from './io.js';
|
|
@@ -144,7 +144,15 @@ export class Solver {
|
|
|
144
144
|
this.prologFlags.get('double_quotes').value = compound(program.doubleQuotes, []);
|
|
145
145
|
}
|
|
146
146
|
for (const [flag, value] of program.prologFlagDirectives ?? []) {
|
|
147
|
-
|
|
147
|
+
if (flag.type === 'var' || value.type === 'var') throw new PrologError('instantiation_error');
|
|
148
|
+
if (flag.type !== 'atom') throw new PrologError('type_error(atom)', flag);
|
|
149
|
+
const definition = this.prologFlags.get(flag.name);
|
|
150
|
+
if (!definition) throw new PrologError('domain_error(prolog_flag)', flag);
|
|
151
|
+
if (value.type !== 'atom' || !definition.allowed.includes(value.name)) {
|
|
152
|
+
throw new PrologError('domain_error(flag_value)', compound('+', [flag, value]));
|
|
153
|
+
}
|
|
154
|
+
if (!definition.changeable) throw new PrologError('permission_error(modify, flag)', flag);
|
|
155
|
+
definition.value = value;
|
|
148
156
|
}
|
|
149
157
|
}
|
|
150
158
|
if (!options.charConversions) {
|
|
@@ -198,35 +206,6 @@ export class Solver {
|
|
|
198
206
|
};
|
|
199
207
|
}
|
|
200
208
|
|
|
201
|
-
setPrologFlag(flagTerm, valueTerm, env = new Env()) {
|
|
202
|
-
const flag = deref(flagTerm, env);
|
|
203
|
-
const value = deref(valueTerm, env);
|
|
204
|
-
if (flag.type === VAR || value.type === VAR) throw new PrologError('instantiation_error');
|
|
205
|
-
if (flag.type !== ATOM) throw new PrologError('type_error(atom)', flag);
|
|
206
|
-
const definition = this.prologFlags.get(flag.name);
|
|
207
|
-
if (!definition) throw new PrologError('domain_error(prolog_flag)', flag);
|
|
208
|
-
|
|
209
|
-
if (definition.writeOptions === true) {
|
|
210
|
-
if (!termIsGround(value, env)) throw new PrologError('instantiation_error');
|
|
211
|
-
try {
|
|
212
|
-
parseTermWriteOptions(value, env, 'write_term', this);
|
|
213
|
-
} catch (error) {
|
|
214
|
-
if (error instanceof PrologError && error.formal === 'instantiation_error') throw error;
|
|
215
|
-
throw new PrologError('domain_error(flag_value)', compound('+', [flag, copyResolved(value, env)]));
|
|
216
|
-
}
|
|
217
|
-
if (!definition.changeable) throw new PrologError('permission_error(modify, flag)', flag);
|
|
218
|
-
definition.value = copyResolved(value, env);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const expectedType = definition.valueType ?? ATOM;
|
|
223
|
-
if (value.type !== expectedType || !definition.allowed.includes(value.name)) {
|
|
224
|
-
throw new PrologError('domain_error(flag_value)', compound('+', [flag, value]));
|
|
225
|
-
}
|
|
226
|
-
if (!definition.changeable) throw new PrologError('permission_error(modify, flag)', flag);
|
|
227
|
-
definition.value = atom(value.name);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
209
|
cloneForInnerGoal(solutionLimit = this.solutionLimit, options = {}) {
|
|
231
210
|
const solver = new Solver(this.program, {
|
|
232
211
|
registry: this.registry,
|
|
@@ -1093,12 +1072,6 @@ function defaultPrologFlags(unknown = 'error', strictIso = false) {
|
|
|
1093
1072
|
['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
|
|
1094
1073
|
['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
|
|
1095
1074
|
['occurs_check', { value: compound('true', []), allowed: ['true', 'error'], changeable: true }],
|
|
1096
|
-
['answer_write_options', {
|
|
1097
|
-
value: listFromItems([compound('spacing', [compound('standard', [])])]),
|
|
1098
|
-
allowed: [],
|
|
1099
|
-
changeable: true,
|
|
1100
|
-
writeOptions: true,
|
|
1101
|
-
}],
|
|
1102
1075
|
]);
|
|
1103
1076
|
if (strictIso) flags.delete('occurs_check');
|
|
1104
1077
|
return flags;
|
|
@@ -97,8 +97,8 @@ required permission error. `read_term/3`, `write_term/3`, `op/3`, and
|
|
|
97
97
|
EyeProlog's chosen simultaneous-error behavior stable. Section 7.12 makes that
|
|
98
98
|
choice implementation dependent when several error conditions hold at once. Strict `write_term/2-3` accepts only
|
|
99
99
|
the Part 1 plus Corrigendum 3 option surface; the normal-profile
|
|
100
|
-
`double_quotes/1`
|
|
101
|
-
|
|
100
|
+
`double_quotes/1` write option remains an explicitly documented EyeProlog
|
|
101
|
+
extension.
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
The preparation, stream, conversion, sorting, and arithmetic audit expands the coverage into Prolog-text,
|
|
@@ -120,11 +120,11 @@ families; `--iso-strict` is intended to remove their Part 1 interpretation.
|
|
|
120
120
|
| 5.5.5 Directives | `module/2`, `use_module/1-2`, `meta_predicate/1` and normal-profile library behavior | Rejected as implementation-specific Part 1 directives. |
|
|
121
121
|
| 5.5.6 Side effects | Normal mode adds `statistics/0-2`, cleanup/library state, and optional proof/statistics host instrumentation. | The strict registry excludes Prolog-visible statistics/cleanup/library adapters; ordinary Part 1 I/O/database/flag/operator side effects remain. Host proof/statistics collection observes execution through the embedding API rather than adding a strict Prolog goal effect. **covered** — strict registry/execution regression. |
|
|
122
122
|
| 5.5.7 Control constructs | `tnot/1` and normal-profile execution optimizations | `tnot/1` is absent; automatic tabling/recursion guards are disabled. |
|
|
123
|
-
| 5.5.8 Flags | `occurs_check
|
|
123
|
+
| 5.5.8 Flags | `occurs_check` | Absent in strict mode. |
|
|
124
124
|
| 5.5.9 Built-in predicates | EyeProlog libraries, CLP(Z), statistics, Part 3 `phrase/2-3`, and interop autoloaded predicates | Strict registry contains only the Part 1 + Corrigenda core registry. |
|
|
125
125
|
| 5.5.10 Evaluable functors | Normal mode additionally accepts the EyeProlog evaluable atom `e`; the remaining arithmetic functors accepted by strict mode are the Part 1 + Corrigenda set. | Strict mode rejects `e/0` as non-evaluable and retains the Corrigendum arithmetic additions. **covered** — strict extension-boundary regression plus `src/iso-arithmetic.js`. |
|
|
126
126
|
| 5.5.11 Reserved atoms | None | None. |
|
|
127
|
-
| Cor.3 5.5.12 Options | Extra library/host options may exist outside core option lists | Normal mode additionally accepts the EyeProlog `write_term/2-3`
|
|
127
|
+
| Cor.3 5.5.12 Options | Extra library/host options may exist outside core option lists | Normal mode additionally accepts the EyeProlog `write_term/2-3` option `double_quotes(true|false)`. Strict core excludes that extension and accepts only the Part 1 plus Corrigendum 3 write-option surface; unknown extension options raise `domain_error(write_option,...)`. |
|
|
128
128
|
|
|
129
129
|
Normal mode provides documented module and DCG compatibility profiles whose
|
|
130
130
|
features overlap standardized Part 2 and Part 3 facilities. They are extensions
|
|
@@ -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(max_arity, unbounded), pair(unknown, fail), pair(double_quotes, chars), pair(occurs_check, true)
|
|
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/run-iso-strict.mjs
CHANGED
|
@@ -41,7 +41,6 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
41
41
|
reporter.test('exposes only ISO core Prolog flags', () => {
|
|
42
42
|
const solver = new Solver(Program.parse('', { isoStrict: true }), { isoStrict: true });
|
|
43
43
|
equal(solver.prologFlags.has('occurs_check'), false, 'occurs_check');
|
|
44
|
-
equal(solver.prologFlags.has('answer_write_options'), false, 'answer_write_options');
|
|
45
44
|
equal(solver.prologFlags.get('unknown')?.value?.name, 'error', 'unknown default');
|
|
46
45
|
});
|
|
47
46
|
|
|
@@ -284,16 +283,8 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
284
283
|
}));
|
|
285
284
|
equal(strictError.formal, 'domain_error(write_option)', 'strict extension rejection');
|
|
286
285
|
|
|
287
|
-
const strictSpacingError = capture(() => run('', {
|
|
288
|
-
isoStrict: true,
|
|
289
|
-
goal: 'write_term(1*1,[spacing(minimal)])',
|
|
290
|
-
}));
|
|
291
|
-
equal(strictSpacingError.formal, 'domain_error(write_option)', 'strict spacing extension rejection');
|
|
292
|
-
|
|
293
286
|
const normal = run('', { goal: 'write_term("ab",[double_quotes(true)])' });
|
|
294
287
|
includes(normal.stdout, '"ab"', 'normal-profile extension remains available');
|
|
295
|
-
includes(run('', { goal: 'write_term(1*1,[spacing(standard)])' }).stdout,
|
|
296
|
-
'1 * 1', 'normal-profile spacing extension remains available');
|
|
297
288
|
});
|
|
298
289
|
|
|
299
290
|
reporter.test('follows Corrigendum 3 variable metadata traversal and write naming', () => {
|
package/test/run-regression.mjs
CHANGED
|
@@ -186,7 +186,7 @@ function regressionCases() {
|
|
|
186
186
|
});
|
|
187
187
|
assertEqual(residuals.status, 0, 'dif/2 residual display status');
|
|
188
188
|
assertEqual(residuals.stdout,
|
|
189
|
-
'?- C = z, D = z, dif(A
|
|
189
|
+
'?- C = z, D = z, dif(A-z, B-z).\n' +
|
|
190
190
|
'?- A = [[]], B = [], dif([[]], [C]).\n' +
|
|
191
191
|
'?- X = Y.\n' +
|
|
192
192
|
'?- ',
|
|
@@ -1449,7 +1449,7 @@ c4 ?- call((!;1)).
|
|
|
1449
1449
|
input: 'read(T).\n!,*.\n.\nread(T).\na\n.\nhalt.\n',
|
|
1450
1450
|
});
|
|
1451
1451
|
assertEqual(continuedGraphic.status, 0, 'continued graphic operator REPL status');
|
|
1452
|
-
assertIncludes(continuedGraphic.stdout, 'T = (
|
|
1452
|
+
assertIncludes(continuedGraphic.stdout, 'T = (!,*.).', 'continued graphic operator answer');
|
|
1453
1453
|
assertIncludes(continuedGraphic.stdout, 'T = a.', 'read after continued graphic operator');
|
|
1454
1454
|
assertNotIncludes(continuedGraphic.stdout, 'syntax_error', 'continued graphic operator syntax');
|
|
1455
1455
|
assertEqual(continuedGraphic.stderr, '', 'continued graphic operator REPL stderr');
|
|
@@ -2456,10 +2456,10 @@ c4 ?- call((!;1)).
|
|
|
2456
2456
|
});
|
|
2457
2457
|
assertEqual(result.status, 0, 'exit status');
|
|
2458
2458
|
assertEqual(result.stdout,
|
|
2459
|
-
'?- T = (a
|
|
2460
|
-
'?- U = (a,
|
|
2461
|
-
'?- V = (a
|
|
2462
|
-
'?- W = a
|
|
2459
|
+
'?- T = (a=b).\n' +
|
|
2460
|
+
'?- U = (a,b).\n' +
|
|
2461
|
+
'?- V = (a;b).\n' +
|
|
2462
|
+
'?- W = a+b.\n' +
|
|
2463
2463
|
'?- ',
|
|
2464
2464
|
'stdout');
|
|
2465
2465
|
assertEqual(result.stderr, '', 'stderr');
|
|
@@ -2665,9 +2665,9 @@ c4 ?- call((!;1)).
|
|
|
2665
2665
|
});
|
|
2666
2666
|
assertEqual(result.status, 0, 'exit status');
|
|
2667
2667
|
assertEqual(result.stdout,
|
|
2668
|
-
'?- |: X = (_A
|
|
2669
|
-
'?- |: X = (_A
|
|
2670
|
-
"?- |: X = (_A
|
|
2668
|
+
'?- |: X = (_A=a).\n' +
|
|
2669
|
+
'?- |: X = (_A=a).\n' +
|
|
2670
|
+
"?- |: X = (_A=pair(_A, _B)), Vs = [_A, _B], Names = ['X'=_A, 'Y'=_B], Singletons = ['Y'=_B].\n" +
|
|
2671
2671
|
'?- ',
|
|
2672
2672
|
'stdout');
|
|
2673
2673
|
assertEqual(result.stderr, '', 'stderr');
|
|
@@ -3882,67 +3882,18 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
3882
3882
|
},
|
|
3883
3883
|
},
|
|
3884
3884
|
{
|
|
3885
|
-
name: '
|
|
3885
|
+
name: 'REPL answers use only lexically required operator spacing (issue #76)',
|
|
3886
3886
|
run: () => {
|
|
3887
|
-
const source = [
|
|
3888
|
-
'emit :-',
|
|
3889
|
-
' write_term(1*1, [spacing(standard)]), put_char(\'|\'),',
|
|
3890
|
-
' write_term(1*1, [spacing(minimal)]), put_char(\'|\'),',
|
|
3891
|
-
' write_term(1*1, [spacing(standard),spacing(minimal)]).',
|
|
3892
|
-
'',
|
|
3893
|
-
].join('\n');
|
|
3894
|
-
assertEqual(run(source, { goal: 'emit' }).stdout, '1 * 1|1*1|1*1emit.\n',
|
|
3895
|
-
'explicit and rightmost spacing options');
|
|
3896
|
-
assertEqual(run([
|
|
3897
|
-
':- set_prolog_flag(answer_write_options, [spacing(minimal)]).',
|
|
3898
|
-
'emit :- current_prolog_flag(answer_write_options, [spacing(minimal)]).',
|
|
3899
|
-
], { goal: 'emit' }).stdout, 'emit.\n', 'source flag directive');
|
|
3900
|
-
|
|
3901
3887
|
const result = runCli([], {
|
|
3902
|
-
input:
|
|
3903
|
-
'current_prolog_flag(answer_write_options, Os).\n' +
|
|
3904
|
-
'X=1*1.\n' +
|
|
3905
|
-
'set_prolog_flag(answer_write_options,[spacing(minimal)]).\n' +
|
|
3906
|
-
'use_module(library(lists)).\n' +
|
|
3907
|
-
'X=1*1.\n' +
|
|
3908
|
-
'current_prolog_flag(answer_write_options, Os).\n' +
|
|
3909
|
-
'halt.\n',
|
|
3888
|
+
input: 'X=1*1.\nX=a + -b.\nhalt.\n',
|
|
3910
3889
|
});
|
|
3911
3890
|
assertEqual(result.status, 0, 'exit status');
|
|
3912
3891
|
assertEqual(result.stdout,
|
|
3913
|
-
'?- Os = [spacing(standard)].\n' +
|
|
3914
|
-
'?- X = 1 * 1.\n' +
|
|
3915
|
-
'?- true.\n' +
|
|
3916
|
-
'?- true.\n' +
|
|
3917
3892
|
'?- X = 1*1.\n' +
|
|
3918
|
-
'?-
|
|
3893
|
+
'?- X = a+ -b.\n' +
|
|
3919
3894
|
'?- ',
|
|
3920
|
-
'
|
|
3895
|
+
'minimal operator layout preserves required token separation');
|
|
3921
3896
|
assertEqual(result.stderr, '', 'stderr');
|
|
3922
|
-
|
|
3923
|
-
let writeOptionError = null;
|
|
3924
|
-
try {
|
|
3925
|
-
run('', { goal: 'write_term(a,[spacing(other)])' });
|
|
3926
|
-
} catch (error) {
|
|
3927
|
-
writeOptionError = error;
|
|
3928
|
-
}
|
|
3929
|
-
assertEqual(writeOptionError?.formal, 'domain_error(write_option)', 'spacing value domain');
|
|
3930
|
-
|
|
3931
|
-
let flagValueError = null;
|
|
3932
|
-
try {
|
|
3933
|
-
run('', { goal: 'set_prolog_flag(answer_write_options,[spacing(other)])' });
|
|
3934
|
-
} catch (error) {
|
|
3935
|
-
flagValueError = error;
|
|
3936
|
-
}
|
|
3937
|
-
assertEqual(flagValueError?.formal, 'domain_error(flag_value)', 'answer option flag domain');
|
|
3938
|
-
|
|
3939
|
-
let nongroundFlagError = null;
|
|
3940
|
-
try {
|
|
3941
|
-
run('', { goal: 'set_prolog_flag(answer_write_options,[spacing(X)])' });
|
|
3942
|
-
} catch (error) {
|
|
3943
|
-
nongroundFlagError = error;
|
|
3944
|
-
}
|
|
3945
|
-
assertEqual(nongroundFlagError?.formal, 'instantiation_error', 'answer options must be ground');
|
|
3946
3897
|
},
|
|
3947
3898
|
},
|
|
3948
3899
|
{
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5426,10 +5426,10 @@ still retained where adjacent graphic tokens would otherwise merge, as in
|
|
|
5426
5426
|
`write_term/2-3` supports `quoted/1`, `ignore_ops/1`, `numbervars/1`, and
|
|
5427
5427
|
`variable_names/1`. Normal mode additionally accepts the EyeProlog extension
|
|
5428
5428
|
`double_quotes(true|false)`: `true` lets eligible character/code lists use the
|
|
5429
|
-
current `double_quotes` representation
|
|
5430
|
-
|
|
5431
|
-
`
|
|
5432
|
-
|
|
5429
|
+
current `double_quotes` representation, while strict ISO mode rejects this
|
|
5430
|
+
non-standard write option. The REPL follows the same minimal-separator rule as
|
|
5431
|
+
term output, so `X=1*1` is displayed as `X = 1*1`, while a separator is retained
|
|
5432
|
+
in `X = a+ -b` because the adjacent graphic tokens would otherwise merge.
|
|
5433
5433
|
|
|
5434
5434
|
Character operations are `get_char`, `peek_char`, `put_char`, `get_code`,
|
|
5435
5435
|
`peek_code`, and `put_code`; byte streams use the corresponding byte
|
|
@@ -6002,7 +6002,7 @@ silently changing a static program.
|
|
|
6002
6002
|
| `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. |
|
|
6003
6003
|
| `current_char_conversion(?Input,?Output)` | Enumerates installed nonidentity conversions. |
|
|
6004
6004
|
| `current_prolog_flag(?Flag,?Value)` | Enumerates flags or retrieves one named flag. An unknown bound flag raises *domain_error(prolog_flag)*. |
|
|
6005
|
-
| `set_prolog_flag(+Flag,+Value)` | Changes a supported mutable flag after validating its value. Read-only flags raise a permission error. |
|
|
6005
|
+
| `set_prolog_flag(+Flag,+Value)` | Changes a supported mutable flag after validating its allowed atom value. Read-only flags raise a permission error. |
|
|
6006
6006
|
|
|
6007
6007
|
| Flag | Default in normal EyeProlog | Allowed values | Mutable |
|
|
6008
6008
|
| --- | --- | --- | --- |
|
|
@@ -6016,7 +6016,6 @@ silently changing a static program.
|
|
|
6016
6016
|
| `unknown` | `error` | `error`, `fail`, `warning` | yes |
|
|
6017
6017
|
| `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
|
|
6018
6018
|
| `occurs_check` | `true` | `true`, `error` | yes |
|
|
6019
|
-
| `answer_write_options` | `[spacing(standard)]` | A ground proper list of supported normal-mode `write_term/2-3` options | yes |
|
|
6020
6019
|
|
|
6021
6020
|
Because `bounded=false`, `current_prolog_flag(max_integer, _)` and
|
|
6022
6021
|
`current_prolog_flag(min_integer, _)` fail as required by ISO 7.11.1.1;
|
|
@@ -6051,24 +6050,6 @@ codes("ab"). % codes([97,98])
|
|
|
6051
6050
|
quoted_atom("ab"). % quoted_atom(ab)
|
|
6052
6051
|
```
|
|
6053
6052
|
|
|
6054
|
-
Normal mode also exposes the REPL's term-writing options through the mutable
|
|
6055
|
-
`answer_write_options` flag. Its explicit default explains the spaces in an
|
|
6056
|
-
answer such as `X = 1 * 1`; selecting minimal spacing is discoverable and
|
|
6057
|
-
reusable from Prolog:
|
|
6058
|
-
|
|
6059
|
-
```text
|
|
6060
|
-
?- current_prolog_flag(answer_write_options, Options).
|
|
6061
|
-
Options = [spacing(standard)].
|
|
6062
|
-
?- set_prolog_flag(answer_write_options, [spacing(minimal)]).
|
|
6063
|
-
true.
|
|
6064
|
-
?- X = 1*1.
|
|
6065
|
-
X = 1*1.
|
|
6066
|
-
```
|
|
6067
|
-
|
|
6068
|
-
The setting survives interactive `consult/1` and `use_module/1-2` program
|
|
6069
|
-
rebuilds. `answer_write_options` is a normal-mode host flag and is absent in
|
|
6070
|
-
strict ISO mode.
|
|
6071
|
-
|
|
6072
6053
|
### Atomic-term operations and conversions
|
|
6073
6054
|
|
|
6074
6055
|
| Predicate and principal call | Behavior |
|
|
@@ -6141,7 +6122,7 @@ the beginning. A peek does not mark the stream as past-end.
|
|
|
6141
6122
|
| `write(+Term)`, `write(+Stream,+Term)` | Writes readable operator notation without quoting atoms merely because quoting would be required for reparsing. Number variables are enabled. |
|
|
6142
6123
|
| `writeq(+Term)`, `writeq(+Stream,+Term)` | Like `write`, but quotes atoms when required for unambiguous input syntax. |
|
|
6143
6124
|
| `write_canonical(+Term)`, `write_canonical(+Stream,+Term)` | Writes quoted canonical functor notation while ignoring operators and without interpreting *$VAR/1*. |
|
|
6144
|
-
| `write_term(+Term,+Options)`, `write_term(+Stream,+Term,+Options)` | Writes with *quoted(true or false)*, *ignore_ops(true or false)*, *numbervars(true or false)*, and *variable_names([Name=Variable,...])*. Normal mode also supports *double_quotes(true or false)
|
|
6125
|
+
| `write_term(+Term,+Options)`, `write_term(+Stream,+Term,+Options)` | Writes with *quoted(true or false)*, *ignore_ops(true or false)*, *numbervars(true or false)*, and *variable_names([Name=Variable,...])*. Normal mode also supports *double_quotes(true or false)*. |
|
|
6145
6126
|
|
|
6146
6127
|
Term input uses the program's current operator table and the same ISO quoted-character
|
|
6147
6128
|
syntax as source text, including backslash-terminated octal and hexadecimal
|
|
@@ -7786,8 +7767,8 @@ Notable implementation boundaries are:
|
|
|
7786
7767
|
- `write_term/2-3` implements the Part 1 plus Corrigendum 3 `quoted/1`,
|
|
7787
7768
|
`ignore_ops/1`, `numbervars/1`, and `variable_names/1` option surface,
|
|
7788
7769
|
including option validation and traversal rules; normal mode also offers
|
|
7789
|
-
`double_quotes(true|false)`
|
|
7790
|
-
|
|
7770
|
+
`double_quotes(true|false)` as an explicitly non-standard extension, which
|
|
7771
|
+
strict mode rejects;
|
|
7791
7772
|
- unification consistently performs an occurs check, rejecting rational-tree
|
|
7792
7773
|
bindings accepted as extensions by some systems.
|
|
7793
7774
|
|