eyeprolog 1.3.44 → 1.3.45
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 +6 -1
- package/package.json +1 -1
- package/src/io.js +33 -10
- package/src/iso-arithmetic.js +12 -10
- package/src/iso.js +224 -60
- package/src/program.js +130 -19
- package/src/solver.js +4 -0
- package/test/conformance/ISO-COMPLIANCE.md +19 -5
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +1 -1
- package/test/conformance/ISO-MATRIX.md +3 -3
- package/test/conformance/expected-errors/iso/get_byte_text_stream.txt +1 -1
- package/the-art-of-eyeprolog.md +7 -1
package/README.md
CHANGED
|
@@ -223,7 +223,12 @@ and `write_term/2-3` accept the Part 1 plus Corrigendum 3 options, while the
|
|
|
223
223
|
normal-profile `write_term(...,[double_quotes(...)])` option remains an
|
|
224
224
|
EyeProlog extension. The strict gate also checks the prescribed ISO error
|
|
225
225
|
precedence for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`, and
|
|
226
|
-
the complete Part 1 flag defaults/value domains/changeability rules.
|
|
226
|
+
the complete Part 1 flag defaults/value domains/changeability rules. The
|
|
227
|
+
ongoing issue #65 audit additionally tightens Prolog-text declaration ordering,
|
|
228
|
+
initialization lifetime, stream/open/close behavior, character/byte stream
|
|
229
|
+
errors, `keysort/2` error cases, and strict arithmetic-extension boundaries.
|
|
230
|
+
Those additions improve the Part 1 surface without turning the remaining audit
|
|
231
|
+
rows into a blanket conformance claim.
|
|
227
232
|
|
|
228
233
|
The auditable processor-requirement checklist lives in
|
|
229
234
|
[`test/conformance/ISO-COMPLIANCE.md`](test/conformance/ISO-COMPLIANCE.md).
|
package/package.json
CHANGED
package/src/io.js
CHANGED
|
@@ -88,8 +88,13 @@ export class StreamManager {
|
|
|
88
88
|
const raw = fs.readFileSync(path);
|
|
89
89
|
if (type === 'binary') content = raw;
|
|
90
90
|
else { content = decodeUtf8ForTextStream(raw); strictUtf8 = true; }
|
|
91
|
-
} else if (mode === '
|
|
92
|
-
|
|
91
|
+
} else if (mode === 'write') {
|
|
92
|
+
// ISO 7.10.1.1: opening an existing sink in write mode empties it,
|
|
93
|
+
// while a missing sink is created at open time (not deferred to close).
|
|
94
|
+
fs.writeFileSync(path, type === 'binary' ? BufferCtor.from([]) : '');
|
|
95
|
+
} else if (mode === 'append') {
|
|
96
|
+
if (fs.existsSync(path)) content = fs.readFileSync(path, type === 'binary' ? null : 'utf8');
|
|
97
|
+
else fs.writeFileSync(path, type === 'binary' ? BufferCtor.from([]) : '');
|
|
93
98
|
}
|
|
94
99
|
if (BufferCtor?.isBuffer(content)) content = [...content];
|
|
95
100
|
return this.add({
|
|
@@ -100,13 +105,18 @@ export class StreamManager {
|
|
|
100
105
|
pastEnd: false,
|
|
101
106
|
});
|
|
102
107
|
}
|
|
103
|
-
|
|
104
|
-
if (!stream.standard
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
flush(stream) {
|
|
109
|
+
if (!stream || stream.standard || stream.mode === 'read') return;
|
|
110
|
+
fs.writeFileSync(stream.path, stream.type === 'binary' ? BufferCtor.from(stream.content) : stream.content);
|
|
111
|
+
}
|
|
112
|
+
discard(stream) {
|
|
107
113
|
if (stream.alias) this.aliases.delete(stream.alias);
|
|
108
114
|
this.streams.delete(stream.id);
|
|
109
115
|
}
|
|
116
|
+
close(stream) {
|
|
117
|
+
this.flush(stream);
|
|
118
|
+
this.discard(stream);
|
|
119
|
+
}
|
|
110
120
|
readUnit(stream, peek = false) {
|
|
111
121
|
if (stream.position >= stream.content.length) return null;
|
|
112
122
|
if (stream.type === 'binary') {
|
|
@@ -126,9 +136,22 @@ export class StreamManager {
|
|
|
126
136
|
return value;
|
|
127
137
|
}
|
|
128
138
|
writeUnit(stream, value) {
|
|
129
|
-
if (stream.standard && stream.write)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
139
|
+
if (stream.standard && stream.write) {
|
|
140
|
+
stream.write(String(value));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// 7.10.2.8: output after repositioning overwrites the existing sink
|
|
144
|
+
// contents at the selected stream position rather than always appending.
|
|
145
|
+
if (stream.type === 'binary') {
|
|
146
|
+
if (stream.position < stream.content.length) stream.content[stream.position] = value;
|
|
147
|
+
else stream.content.push(value);
|
|
148
|
+
stream.position++;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const text = String(value);
|
|
152
|
+
const content = String(stream.content);
|
|
153
|
+
const end = Math.min(content.length, stream.position + text.length);
|
|
154
|
+
stream.content = `${content.slice(0, stream.position)}${text}${content.slice(end)}`;
|
|
155
|
+
stream.position += text.length;
|
|
133
156
|
}
|
|
134
157
|
}
|
package/src/iso-arithmetic.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from './term.js';
|
|
6
6
|
import { PrologError } from './errors.js';
|
|
7
7
|
|
|
8
|
-
function evaluate(term, env) {
|
|
8
|
+
function evaluate(term, env, options = {}) {
|
|
9
9
|
term = deref(term, env);
|
|
10
10
|
if (term.type === VAR) throw new PrologError('instantiation_error');
|
|
11
11
|
if (term.type === NUMBER) {
|
|
@@ -16,10 +16,11 @@ function evaluate(term, env) {
|
|
|
16
16
|
}
|
|
17
17
|
if (term.type === ATOM) {
|
|
18
18
|
if (term.name === 'pi') return { integer: false, value: Math.PI };
|
|
19
|
-
if (term.name === 'e') return { integer: false, value: Math.E };
|
|
19
|
+
if (term.name === 'e' && options.isoStrict !== true) return { integer: false, value: Math.E };
|
|
20
|
+
throw new PrologError('type_error(evaluable)', compound('/', [atom(term.name), numberTerm(0)]));
|
|
20
21
|
}
|
|
21
22
|
if (term.type !== COMPOUND) throw new PrologError('type_error(evaluable)', term);
|
|
22
|
-
const args = term.args.map((arg) => evaluate(arg, env));
|
|
23
|
+
const args = term.args.map((arg) => evaluate(arg, env, options));
|
|
23
24
|
return evaluateOperation(term, args);
|
|
24
25
|
}
|
|
25
26
|
function evaluateOperation(term, args) {
|
|
@@ -120,8 +121,8 @@ export function arithmeticValueTerm(value) {
|
|
|
120
121
|
function numericTerm(value) {
|
|
121
122
|
return arithmeticValueTerm(value);
|
|
122
123
|
}
|
|
123
|
-
export function evaluateArithmetic(term, env) {
|
|
124
|
-
return evaluate(term, env);
|
|
124
|
+
export function evaluateArithmetic(term, env, options = {}) {
|
|
125
|
+
return evaluate(term, env, options);
|
|
125
126
|
}
|
|
126
127
|
function compareIntegerToFloat(integerValue, floatValue) {
|
|
127
128
|
if (!Number.isFinite(floatValue)) throw new PrologError('evaluation_error(float_overflow)');
|
|
@@ -151,15 +152,16 @@ export function compareArithmeticValues(left, right) {
|
|
|
151
152
|
if (right.integer) return -compareIntegerToFloat(b, a);
|
|
152
153
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
153
154
|
}
|
|
154
|
-
export function* isBuiltin({ goal, env }) {
|
|
155
|
-
const result = arithmeticValueTerm(evaluateArithmetic(goal.args[1], env));
|
|
155
|
+
export function* isBuiltin({ solver, goal, env }) {
|
|
156
|
+
const result = arithmeticValueTerm(evaluateArithmetic(goal.args[1], env, { isoStrict: solver?.isoStrict === true }));
|
|
156
157
|
const next = env.clone();
|
|
157
158
|
if (unify(goal.args[0], result, next)) yield next;
|
|
158
159
|
}
|
|
159
160
|
export function arithmeticComparison(test) {
|
|
160
|
-
return function* ({ goal, env }) {
|
|
161
|
-
const
|
|
162
|
-
const
|
|
161
|
+
return function* ({ solver, goal, env }) {
|
|
162
|
+
const options = { isoStrict: solver?.isoStrict === true };
|
|
163
|
+
const left = evaluateArithmetic(goal.args[0], env, options);
|
|
164
|
+
const right = evaluateArithmetic(goal.args[1], env, options);
|
|
163
165
|
const cmp = compareArithmeticValues(left, right);
|
|
164
166
|
if (test(cmp)) yield env;
|
|
165
167
|
};
|
package/src/iso.js
CHANGED
|
@@ -96,7 +96,7 @@ export const isoBuiltins = {
|
|
|
96
96
|
registry.add('current_output', 1, currentOutputBuiltin, { deterministic: true });
|
|
97
97
|
registry.add('set_input', 1, setCurrentStreamBuiltin('read'), { deterministic: true });
|
|
98
98
|
registry.add('set_output', 1, setCurrentStreamBuiltin('write'), { deterministic: true });
|
|
99
|
-
registry.add('flush_output', 0,
|
|
99
|
+
registry.add('flush_output', 0, flushOutputBuiltin, { deterministic: true });
|
|
100
100
|
registry.add('flush_output', 1, flushOutputBuiltin, { deterministic: true });
|
|
101
101
|
registry.add('stream_property', 2, streamPropertyBuiltin);
|
|
102
102
|
registry.add('set_stream_position', 2, setStreamPositionBuiltin, { deterministic: true });
|
|
@@ -346,10 +346,18 @@ function* keysortBuiltin({ goal, env }) {
|
|
|
346
346
|
validateListOutput(goal.args[1], env);
|
|
347
347
|
for (const item of items) {
|
|
348
348
|
const resolved = deref(item, env);
|
|
349
|
+
if (resolved.type === VAR) throw new PrologError('instantiation_error');
|
|
349
350
|
if (resolved.type !== COMPOUND || resolved.name !== '-' || resolved.arity !== 2) {
|
|
350
351
|
throw new PrologError('type_error(pair)', resolved);
|
|
351
352
|
}
|
|
352
353
|
}
|
|
354
|
+
const { items: outputPrefix } = listElements(goal.args[1], env);
|
|
355
|
+
for (const item of outputPrefix) {
|
|
356
|
+
if (item.type === VAR) continue;
|
|
357
|
+
if (item.type !== COMPOUND || item.name !== '-' || item.arity !== 2) {
|
|
358
|
+
throw new PrologError('type_error(pair)', item);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
353
361
|
// Modern ECMAScript specifies a stable Array#sort, as required by keysort/2.
|
|
354
362
|
// Keep one implementation-dependent variable order for this whole sorting
|
|
355
363
|
// operation, as required by ISO 7.2.1.
|
|
@@ -830,6 +838,16 @@ function streamReference(term, env) {
|
|
|
830
838
|
throw new PrologError('domain_error(stream_or_alias)', value);
|
|
831
839
|
}
|
|
832
840
|
|
|
841
|
+
function streamTermReference(term, env) {
|
|
842
|
+
const value = deref(term, env);
|
|
843
|
+
if (value.type === VAR) return null;
|
|
844
|
+
if (value.type === COMPOUND && value.name === '$stream' && value.arity === 1 &&
|
|
845
|
+
value.args[0].type === NUMBER && isDecimalInteger(value.args[0].name)) {
|
|
846
|
+
return Number(value.args[0].name);
|
|
847
|
+
}
|
|
848
|
+
throw new PrologError('domain_error(stream)', value);
|
|
849
|
+
}
|
|
850
|
+
|
|
833
851
|
function requireStream(solver, term, env, mode = null) {
|
|
834
852
|
const culprit = deref(term, env);
|
|
835
853
|
const stream = solver.io.resolve(streamReference(term, env));
|
|
@@ -886,13 +904,14 @@ function openOptions(term, env) {
|
|
|
886
904
|
if ((value = optionAtom(option, 'type'))) {
|
|
887
905
|
value = deref(value, env);
|
|
888
906
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
889
|
-
if (value.type !== ATOM
|
|
890
|
-
|
|
907
|
+
if (value.type !== ATOM || !['text', 'binary'].includes(value.name)) {
|
|
908
|
+
throw new PrologError('domain_error(stream_option)', option);
|
|
909
|
+
}
|
|
891
910
|
result.type = value.name;
|
|
892
911
|
} else if ((value = optionAtom(option, 'alias'))) {
|
|
893
912
|
value = deref(value, env);
|
|
894
913
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
895
|
-
if (value.type !== ATOM) throw new PrologError('
|
|
914
|
+
if (value.type !== ATOM) throw new PrologError('domain_error(stream_option)', option);
|
|
896
915
|
result.alias = value.name;
|
|
897
916
|
} else if ((value = optionAtom(option, 'reposition'))) {
|
|
898
917
|
value = deref(value, env);
|
|
@@ -911,19 +930,45 @@ function openOptions(term, env) {
|
|
|
911
930
|
return result;
|
|
912
931
|
}
|
|
913
932
|
|
|
933
|
+
function streamOptionHasRequiredVariable(option, env) {
|
|
934
|
+
option = deref(option, env);
|
|
935
|
+
if (option.type !== COMPOUND || option.arity !== 1 ||
|
|
936
|
+
!['type', 'alias', 'reposition', 'eof_action'].includes(option.name)) return false;
|
|
937
|
+
return deref(option.args[0], env).type === VAR;
|
|
938
|
+
}
|
|
939
|
+
|
|
914
940
|
function* openBuiltin({ solver, goal, env }) {
|
|
915
|
-
const
|
|
916
|
-
const mode =
|
|
917
|
-
if (!['read', 'write', 'append'].includes(mode.name)) throw new PrologError('domain_error(io_mode)', mode);
|
|
941
|
+
const source = deref(goal.args[0], env);
|
|
942
|
+
const mode = deref(goal.args[1], env);
|
|
918
943
|
const streamTarget = deref(goal.args[2], env);
|
|
944
|
+
const optionTerm = goal.arity === 3 ? emptyList() : goal.args[3];
|
|
945
|
+
|
|
946
|
+
// ISO 8.11.5.3 + Corrigenda 2/3. Keep each prescribed condition
|
|
947
|
+
// observable on its own and apply the published ordering for deterministic
|
|
948
|
+
// diagnostics when several arguments are erroneous.
|
|
949
|
+
if (source.type === VAR) throw new PrologError('instantiation_error');
|
|
950
|
+
if (mode.type === VAR) throw new PrologError('instantiation_error');
|
|
951
|
+
const preflight = preflightOptionList(optionTerm, env);
|
|
952
|
+
if (preflight.items.some((option) => streamOptionHasRequiredVariable(option, env))) {
|
|
953
|
+
throw new PrologError('instantiation_error');
|
|
954
|
+
}
|
|
955
|
+
if (mode.type !== ATOM) throw new PrologError('type_error(atom)', mode);
|
|
956
|
+
const optionItems = requireProperOptionList(preflight);
|
|
919
957
|
if (streamTarget.type !== VAR) throw new PrologError('uninstantiation_error', streamTarget);
|
|
920
|
-
|
|
921
|
-
if (
|
|
958
|
+
if (source.type !== ATOM) throw new PrologError('domain_error(source_sink)', source);
|
|
959
|
+
if (!['read', 'write', 'append'].includes(mode.name)) throw new PrologError('domain_error(io_mode)', mode);
|
|
960
|
+
const options = goal.arity === 3 ? {} : openOptions(optionTerm, env);
|
|
961
|
+
if (options.alias && solver.io.resolve(options.alias)) {
|
|
962
|
+
throw new PrologError('permission_error(open, source_sink)', atom(options.alias));
|
|
963
|
+
}
|
|
922
964
|
let stream;
|
|
923
965
|
try {
|
|
924
|
-
stream = solver.io.open(
|
|
925
|
-
} catch (
|
|
926
|
-
|
|
966
|
+
stream = solver.io.open(source.name, mode.name, options);
|
|
967
|
+
} catch (error) {
|
|
968
|
+
if (error?.code === 'ENOENT' && mode.name === 'read') {
|
|
969
|
+
throw new PrologError('existence_error(source_sink)', source);
|
|
970
|
+
}
|
|
971
|
+
throw new PrologError('permission_error(open, source_sink)', source);
|
|
927
972
|
}
|
|
928
973
|
const next = env.clone();
|
|
929
974
|
if (unify(goal.args[2], streamHandle(stream.id), next)) yield next;
|
|
@@ -931,22 +976,35 @@ function* openBuiltin({ solver, goal, env }) {
|
|
|
931
976
|
}
|
|
932
977
|
|
|
933
978
|
function* closeBuiltin({ solver, goal, env }) {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
979
|
+
const reference = deref(goal.args[0], env);
|
|
980
|
+
if (reference.type === VAR) throw new PrologError('instantiation_error');
|
|
981
|
+
const optionTerm = goal.arity === 2 ? goal.args[1] : emptyList();
|
|
982
|
+
const preflight = preflightOptionList(optionTerm, env);
|
|
983
|
+
if (preflight.items.some((option) => {
|
|
984
|
+
const force = optionAtom(option, 'force');
|
|
985
|
+
return force != null && deref(force, env).type === VAR;
|
|
986
|
+
})) throw new PrologError('instantiation_error');
|
|
987
|
+
const options = requireProperOptionList(preflight);
|
|
988
|
+
streamReference(goal.args[0], env);
|
|
989
|
+
let forceClose = false;
|
|
990
|
+
for (const option of options) {
|
|
991
|
+
const force = optionAtom(option, 'force');
|
|
992
|
+
const value = force && deref(force, env);
|
|
993
|
+
if (!value || value.type !== ATOM || !['true', 'false'].includes(value.name)) {
|
|
994
|
+
throw new PrologError('domain_error(close_option)', option);
|
|
943
995
|
}
|
|
996
|
+
forceClose = value.name === 'true'; // rightmost contradictory option applies
|
|
944
997
|
}
|
|
945
998
|
const stream = requireStream(solver, goal.args[0], env);
|
|
946
999
|
if (!stream.standard) {
|
|
1000
|
+
try {
|
|
1001
|
+
solver.io.close(stream);
|
|
1002
|
+
} catch (_) {
|
|
1003
|
+
if (!forceClose) throw new PrologError('system_error');
|
|
1004
|
+
solver.io.discard(stream);
|
|
1005
|
+
}
|
|
947
1006
|
if (solver.io.currentInput === stream.id) solver.io.currentInput = 0;
|
|
948
1007
|
if (solver.io.currentOutput === stream.id) solver.io.currentOutput = 1;
|
|
949
|
-
solver.io.close(stream);
|
|
950
1008
|
}
|
|
951
1009
|
yield env;
|
|
952
1010
|
}
|
|
@@ -954,7 +1012,8 @@ function* closeBuiltin({ solver, goal, env }) {
|
|
|
954
1012
|
function* currentInputBuiltin({ solver, goal, env }) {
|
|
955
1013
|
const value = deref(goal.args[0], env);
|
|
956
1014
|
if (value.type !== VAR) {
|
|
957
|
-
const
|
|
1015
|
+
const id = streamTermReference(goal.args[0], env);
|
|
1016
|
+
const stream = solver.io.resolve(id);
|
|
958
1017
|
if (!stream) throw new PrologError('domain_error(stream)', value);
|
|
959
1018
|
if (stream.id === solver.io.currentInput) yield env;
|
|
960
1019
|
return;
|
|
@@ -965,7 +1024,8 @@ function* currentInputBuiltin({ solver, goal, env }) {
|
|
|
965
1024
|
function* currentOutputBuiltin({ solver, goal, env }) {
|
|
966
1025
|
const value = deref(goal.args[0], env);
|
|
967
1026
|
if (value.type !== VAR) {
|
|
968
|
-
const
|
|
1027
|
+
const id = streamTermReference(goal.args[0], env);
|
|
1028
|
+
const stream = solver.io.resolve(id);
|
|
969
1029
|
if (!stream) throw new PrologError('domain_error(stream)', value);
|
|
970
1030
|
if (stream.id === solver.io.currentOutput) yield env;
|
|
971
1031
|
return;
|
|
@@ -973,6 +1033,7 @@ function* currentOutputBuiltin({ solver, goal, env }) {
|
|
|
973
1033
|
const next = env.clone();
|
|
974
1034
|
if (unify(goal.args[0], streamHandle(solver.io.currentOutput), next)) yield next;
|
|
975
1035
|
}
|
|
1036
|
+
|
|
976
1037
|
function setCurrentStreamBuiltin(mode) {
|
|
977
1038
|
return function* ({ solver, goal, env }) {
|
|
978
1039
|
const stream = requireStream(solver, goal.args[0], env, mode);
|
|
@@ -982,7 +1043,14 @@ function setCurrentStreamBuiltin(mode) {
|
|
|
982
1043
|
};
|
|
983
1044
|
}
|
|
984
1045
|
function* flushOutputBuiltin({ solver, goal, env }) {
|
|
985
|
-
|
|
1046
|
+
const stream = goal.arity === 0
|
|
1047
|
+
? solver.io.resolve(solver.io.currentOutput)
|
|
1048
|
+
: requireStream(solver, goal.args[0], env, 'write');
|
|
1049
|
+
try {
|
|
1050
|
+
solver.io.flush(stream);
|
|
1051
|
+
} catch (_) {
|
|
1052
|
+
throw new PrologError('system_error');
|
|
1053
|
+
}
|
|
986
1054
|
yield env;
|
|
987
1055
|
}
|
|
988
1056
|
|
|
@@ -1003,23 +1071,51 @@ function streamProperties(stream) {
|
|
|
1003
1071
|
return properties;
|
|
1004
1072
|
}
|
|
1005
1073
|
function* setStreamPositionBuiltin({ solver, goal, env }) {
|
|
1006
|
-
const
|
|
1007
|
-
if (
|
|
1074
|
+
const reference = deref(goal.args[0], env);
|
|
1075
|
+
if (reference.type === VAR) throw new PrologError('instantiation_error');
|
|
1008
1076
|
let position = deref(goal.args[1], env);
|
|
1009
|
-
if (position.type ===
|
|
1077
|
+
if (position.type === VAR) throw new PrologError('instantiation_error');
|
|
1078
|
+
streamReference(goal.args[0], env);
|
|
1079
|
+
if (!solver.isoStrict && position.type === COMPOUND && position.name === 'position' && position.arity === 1) {
|
|
1080
|
+
// Historical EyeProlog convenience form. Strict Part 1 mode uses only
|
|
1081
|
+
// the implementation-defined integer stream-position terms returned by
|
|
1082
|
+
// stream_property/2.
|
|
1010
1083
|
position = deref(position.args[0], env);
|
|
1011
1084
|
}
|
|
1012
|
-
if (position.type === VAR
|
|
1013
|
-
|
|
1085
|
+
if (position.type === VAR || position.type !== NUMBER || !isDecimalInteger(position.name)) {
|
|
1086
|
+
throw new PrologError('domain_error(stream_position)', position);
|
|
1087
|
+
}
|
|
1014
1088
|
const offset = BigInt(position.name);
|
|
1015
|
-
if (offset < 0n
|
|
1089
|
+
if (offset < 0n) throw new PrologError('domain_error(stream_position)', position);
|
|
1090
|
+
const stream = requireStream(solver, goal.args[0], env);
|
|
1091
|
+
if (!stream.reposition) throw new PrologError('permission_error(reposition, stream)', reference);
|
|
1092
|
+
if (offset > BigInt(stream.content.length)) throw new PrologError('domain_error(stream_position)', position);
|
|
1016
1093
|
stream.position = Number(offset);
|
|
1017
1094
|
stream.pastEnd = false;
|
|
1018
1095
|
yield env;
|
|
1019
1096
|
}
|
|
1097
|
+
|
|
1098
|
+
function isStreamPropertyPattern(value) {
|
|
1099
|
+
if (value.type === VAR) return true;
|
|
1100
|
+
if (value.type === ATOM) return value.name === 'input' || value.name === 'output';
|
|
1101
|
+
return value.type === COMPOUND && value.arity === 1 && [
|
|
1102
|
+
'file_name', 'mode', 'alias', 'position', 'end_of_stream', 'eof_action', 'reposition', 'type',
|
|
1103
|
+
].includes(value.name);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1020
1106
|
function* streamPropertyBuiltin({ solver, goal, env }) {
|
|
1021
1107
|
const reference = deref(goal.args[0], env);
|
|
1022
|
-
const
|
|
1108
|
+
const propertyPattern = deref(goal.args[1], env);
|
|
1109
|
+
if (reference.type !== VAR) {
|
|
1110
|
+
const id = streamTermReference(goal.args[0], env);
|
|
1111
|
+
if (!solver.io.resolve(id)) throw new PrologError('domain_error(stream)', reference);
|
|
1112
|
+
}
|
|
1113
|
+
if (!isStreamPropertyPattern(propertyPattern)) {
|
|
1114
|
+
throw new PrologError('domain_error(stream_property)', propertyPattern);
|
|
1115
|
+
}
|
|
1116
|
+
const streams = reference.type === VAR
|
|
1117
|
+
? [...solver.io.streams.values()]
|
|
1118
|
+
: [solver.io.resolve(streamTermReference(goal.args[0], env))];
|
|
1023
1119
|
for (const stream of streams) {
|
|
1024
1120
|
for (const property of streamProperties(stream)) {
|
|
1025
1121
|
const next = env.clone();
|
|
@@ -1035,27 +1131,69 @@ function outputStreamFor(solver, goal, env) {
|
|
|
1035
1131
|
return goal.arity === 1 ? solver.io.resolve(solver.io.currentOutput) : requireStream(solver, goal.args[0], env, 'write');
|
|
1036
1132
|
}
|
|
1037
1133
|
function* atEndBuiltin({ solver, goal, env }) {
|
|
1038
|
-
|
|
1039
|
-
|
|
1134
|
+
// at_end_of_stream/1 is bootstrapped in Part 1 from the end_of_stream/1
|
|
1135
|
+
// stream property and therefore accepts any open stream-or-alias; it is not
|
|
1136
|
+
// an input-only operation.
|
|
1137
|
+
const stream = goal.arity === 0 ? solver.io.resolve(solver.io.currentInput) : requireStream(solver, goal.args[0], env);
|
|
1138
|
+
if (stream.pastEnd || stream.position >= stream.content.length) yield env;
|
|
1139
|
+
}
|
|
1140
|
+
function validStrictInputCharacterCode(value, solver) {
|
|
1141
|
+
if (value.type !== NUMBER || !isDecimalInteger(value.name)) return false;
|
|
1142
|
+
const code = BigInt(value.name);
|
|
1143
|
+
if (code === -1n) return true;
|
|
1144
|
+
if (code < 0n || code > 0x10ffffn || (code >= 0xd800n && code <= 0xdfffn)) return false;
|
|
1145
|
+
return !solver.isoStrict || isStrictIsoPcsCodePoint(Number(code));
|
|
1040
1146
|
}
|
|
1147
|
+
|
|
1148
|
+
function preflightInputUnitTarget(name, target, env, solver) {
|
|
1149
|
+
const value = deref(target, env);
|
|
1150
|
+
if (name.endsWith('char')) {
|
|
1151
|
+
if (value.type !== VAR && !(oneChar(value) || (value.type === ATOM && value.name === 'end_of_file'))) {
|
|
1152
|
+
throw new PrologError('type_error(in_character)', value);
|
|
1153
|
+
}
|
|
1154
|
+
if (value.type === ATOM && oneChar(value) && solver.isoStrict && !isStrictIsoPcsCharacter(value.name)) {
|
|
1155
|
+
throw new PrologError('type_error(in_character)', value);
|
|
1156
|
+
}
|
|
1157
|
+
} else if (name.endsWith('code')) {
|
|
1158
|
+
if (value.type !== VAR && (value.type !== NUMBER || !isDecimalInteger(value.name))) {
|
|
1159
|
+
throw new PrologError('type_error(integer)', value);
|
|
1160
|
+
}
|
|
1161
|
+
if (value.type !== VAR && !validStrictInputCharacterCode(value, solver)) {
|
|
1162
|
+
throw new PrologError('representation_error(in_character_code)');
|
|
1163
|
+
}
|
|
1164
|
+
} else {
|
|
1165
|
+
if (value.type !== VAR) {
|
|
1166
|
+
if (value.type !== NUMBER || !isDecimalInteger(value.name)) throw new PrologError('type_error(in_byte)', value);
|
|
1167
|
+
const byte = BigInt(value.name);
|
|
1168
|
+
if (byte !== -1n && (byte < 0n || byte > 255n)) throw new PrologError('type_error(in_byte)', value);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
return value;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1041
1174
|
function inputUnitBuiltin(name) {
|
|
1042
1175
|
return function* ({ solver, goal, env }) {
|
|
1176
|
+
const explicit = goal.arity === 2;
|
|
1177
|
+
if (explicit && deref(goal.args[0], env).type === VAR) throw new PrologError('instantiation_error');
|
|
1178
|
+
const target = goal.args[goal.arity - 1];
|
|
1179
|
+
preflightInputUnitTarget(name, target, env, solver);
|
|
1043
1180
|
const stream = inputStreamFor(solver, goal, env);
|
|
1044
1181
|
const binary = name.endsWith('byte');
|
|
1045
|
-
if (binary
|
|
1182
|
+
if (binary && stream.type !== 'binary') {
|
|
1183
|
+
throw new PrologError('permission_error(input, text_stream)', streamHandle(stream.id));
|
|
1184
|
+
}
|
|
1185
|
+
if (!binary && stream.type !== 'text') {
|
|
1186
|
+
throw new PrologError('permission_error(input, binary_stream)', streamHandle(stream.id));
|
|
1187
|
+
}
|
|
1046
1188
|
if (stream.pastEnd && stream.eofAction === 'error') {
|
|
1047
1189
|
throw new PrologError('permission_error(input, past_end_of_stream)', streamHandle(stream.id));
|
|
1048
1190
|
}
|
|
1049
1191
|
if (stream.pastEnd && stream.eofAction === 'reset') {
|
|
1050
|
-
// A terminal EOF (Ctrl-D) is local to one input operation. The next
|
|
1051
|
-
// operation should wait for fresh terminal input rather than rewinding
|
|
1052
|
-
// and replaying the already-consumed interactive buffer.
|
|
1053
1192
|
if (typeof stream.interactiveReadUnit !== 'function') stream.position = 0;
|
|
1054
1193
|
stream.pastEnd = false;
|
|
1055
1194
|
}
|
|
1056
1195
|
const peek = name.startsWith('peek');
|
|
1057
|
-
if (stream.position >= stream.content.length &&
|
|
1058
|
-
typeof stream.interactiveReadUnit === 'function') {
|
|
1196
|
+
if (stream.position >= stream.content.length && typeof stream.interactiveReadUnit === 'function') {
|
|
1059
1197
|
const text = stream.interactiveReadUnit();
|
|
1060
1198
|
if (text != null) {
|
|
1061
1199
|
stream.content += String(text);
|
|
@@ -1066,50 +1204,68 @@ function inputUnitBuiltin(name) {
|
|
|
1066
1204
|
try {
|
|
1067
1205
|
unit = solver.io.readUnit(stream, peek);
|
|
1068
1206
|
} catch (error) {
|
|
1069
|
-
if (error?.name === 'InvalidCharacterEncodingError')
|
|
1070
|
-
throw new PrologError('representation_error(character)');
|
|
1071
|
-
}
|
|
1207
|
+
if (error?.name === 'InvalidCharacterEncodingError') throw new PrologError('representation_error(character)');
|
|
1072
1208
|
throw error;
|
|
1073
1209
|
}
|
|
1074
1210
|
if (unit == null && !peek) stream.pastEnd = true;
|
|
1075
1211
|
if (!binary && unit != null && solver.isoStrict && !isStrictIsoPcsCharacter(unit)) {
|
|
1076
1212
|
throw new PrologError('representation_error(character)');
|
|
1077
1213
|
}
|
|
1078
|
-
const result = unit == null
|
|
1214
|
+
const result = unit == null
|
|
1215
|
+
? (binary ? numberTerm(-1) : name.endsWith('code') ? numberTerm(-1) : atom('end_of_file'))
|
|
1079
1216
|
: binary ? numberTerm(unit) : name.endsWith('code') ? numberTerm(unit.codePointAt(0)) : atom(unit);
|
|
1080
|
-
const target = goal.args[goal.arity - 1];
|
|
1081
1217
|
const next = env.clone();
|
|
1082
1218
|
if (unify(target, result, next)) yield next;
|
|
1083
1219
|
};
|
|
1084
1220
|
}
|
|
1221
|
+
|
|
1222
|
+
function preflightOutputUnitValue(name, target, env) {
|
|
1223
|
+
const value = deref(target, env);
|
|
1224
|
+
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
1225
|
+
if (name === 'put_char') {
|
|
1226
|
+
if (!oneChar(value)) throw new PrologError('type_error(character)', value);
|
|
1227
|
+
return value;
|
|
1228
|
+
}
|
|
1229
|
+
if (name === 'put_byte') {
|
|
1230
|
+
if (value.type !== NUMBER || !isDecimalInteger(value.name)) throw new PrologError('type_error(byte)', value);
|
|
1231
|
+
const byte = BigInt(value.name);
|
|
1232
|
+
if (byte < 0n || byte > 255n) throw new PrologError('type_error(byte)', value);
|
|
1233
|
+
return value;
|
|
1234
|
+
}
|
|
1235
|
+
if (value.type !== NUMBER || !isDecimalInteger(value.name)) throw new PrologError('type_error(integer)', value);
|
|
1236
|
+
return value;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1085
1239
|
function outputUnitBuiltin(name) {
|
|
1086
1240
|
return function* ({ solver, goal, env }) {
|
|
1241
|
+
const explicit = goal.arity === 2;
|
|
1242
|
+
if (explicit && deref(goal.args[0], env).type === VAR) throw new PrologError('instantiation_error');
|
|
1243
|
+
const value = preflightOutputUnitValue(name, goal.args[goal.arity - 1], env);
|
|
1087
1244
|
const stream = outputStreamFor(solver, goal, env);
|
|
1088
|
-
const value = deref(goal.args[goal.arity - 1], env);
|
|
1089
|
-
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
1090
1245
|
if (name === 'put_char') {
|
|
1091
1246
|
if (stream.type !== 'text') throw new PrologError('permission_error(output, binary_stream)', streamHandle(stream.id));
|
|
1092
|
-
if (!oneChar(value)) throw new PrologError('type_error(character)', value);
|
|
1093
1247
|
if (solver.isoStrict && !isStrictIsoPcsCharacter(value.name)) {
|
|
1094
1248
|
throw new PrologError('representation_error(character)', value);
|
|
1095
1249
|
}
|
|
1096
1250
|
solver.io.writeUnit(stream, value.name);
|
|
1251
|
+
} else if (name === 'put_byte') {
|
|
1252
|
+
if (stream.type !== 'binary') throw new PrologError('permission_error(output, text_stream)', streamHandle(stream.id));
|
|
1253
|
+
solver.io.writeUnit(stream, Number(value.name));
|
|
1097
1254
|
} else {
|
|
1098
|
-
if (
|
|
1099
|
-
throw new PrologError('permission_error(output, stream)', streamHandle(stream.id));
|
|
1100
|
-
}
|
|
1101
|
-
if (value.type !== NUMBER || !isDecimalInteger(value.name)) throw new PrologError('type_error(integer)', value);
|
|
1255
|
+
if (stream.type !== 'text') throw new PrologError('permission_error(output, binary_stream)', streamHandle(stream.id));
|
|
1102
1256
|
const code = BigInt(value.name);
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1257
|
+
if (code < 0n || code > 0x10ffffn || (code >= 0xd800n && code <= 0xdfffn)) {
|
|
1258
|
+
throw new PrologError('representation_error(character_code)');
|
|
1259
|
+
}
|
|
1260
|
+
if (solver.isoStrict && !isStrictIsoPcsCodePoint(Number(code))) {
|
|
1106
1261
|
throw new PrologError('representation_error(character_code)');
|
|
1107
1262
|
}
|
|
1108
|
-
solver.io.writeUnit(stream,
|
|
1263
|
+
solver.io.writeUnit(stream, String.fromCodePoint(Number(code)));
|
|
1109
1264
|
}
|
|
1110
1265
|
yield env;
|
|
1111
1266
|
};
|
|
1112
1267
|
}
|
|
1268
|
+
|
|
1113
1269
|
function* nlBuiltin({ solver, goal, env }) {
|
|
1114
1270
|
const stream = goal.arity === 0
|
|
1115
1271
|
? solver.io.resolve(solver.io.currentOutput)
|
|
@@ -1274,6 +1430,13 @@ export function isCompleteReadTermText(text, solver) {
|
|
|
1274
1430
|
}
|
|
1275
1431
|
|
|
1276
1432
|
function readTermFromStream(stream, solver) {
|
|
1433
|
+
if (stream.pastEnd && stream.eofAction === 'error') {
|
|
1434
|
+
throw new PrologError('permission_error(input, past_end_of_stream)', streamHandle(stream.id));
|
|
1435
|
+
}
|
|
1436
|
+
if (stream.pastEnd && stream.eofAction === 'reset') {
|
|
1437
|
+
if (typeof stream.interactiveReadTerm !== 'function') stream.position = 0;
|
|
1438
|
+
stream.pastEnd = false;
|
|
1439
|
+
}
|
|
1277
1440
|
let requestedInteractiveTerm = false;
|
|
1278
1441
|
while (true) {
|
|
1279
1442
|
let sawCandidate = false;
|
|
@@ -1317,6 +1480,7 @@ function readTermFromStream(stream, solver) {
|
|
|
1317
1480
|
stream.position = source.length;
|
|
1318
1481
|
if (!sawCandidate) {
|
|
1319
1482
|
if (hasNonLayoutRemainder(source, remainderStart)) throw new PrologError('syntax_error(read_term)');
|
|
1483
|
+
stream.pastEnd = true;
|
|
1320
1484
|
return { term: atom('end_of_file'), variables: [] };
|
|
1321
1485
|
}
|
|
1322
1486
|
throw new PrologError('syntax_error(read_term)');
|
|
@@ -1789,14 +1953,14 @@ function canonicalNumberText(value) {
|
|
|
1789
1953
|
return text;
|
|
1790
1954
|
}
|
|
1791
1955
|
|
|
1792
|
-
function numberListText(list, env, kind, valueIsBound) {
|
|
1956
|
+
function numberListText(list, env, kind, valueIsBound, solver = null) {
|
|
1793
1957
|
const whole = deref(list, env);
|
|
1794
1958
|
const { items, tail } = listElements(list, env);
|
|
1795
1959
|
const proper = tail.type === ATOM && tail.name === '[]';
|
|
1796
1960
|
if (tail.type !== VAR && !proper) throw new PrologError('type_error(list)', whole);
|
|
1797
1961
|
|
|
1798
1962
|
const invalid = items.find((item) => item.type !== VAR &&
|
|
1799
|
-
(kind === 'chars' ? !oneChar(item) : !validCharacterCode(item)));
|
|
1963
|
+
(kind === 'chars' ? !oneChar(item) : !validCharacterCode(item, solver)));
|
|
1800
1964
|
if (invalid) {
|
|
1801
1965
|
if (kind === 'chars') throw new PrologError('type_error(character)', invalid);
|
|
1802
1966
|
if (invalid.type !== NUMBER || !isDecimalInteger(invalid.name)) {
|
|
@@ -1816,12 +1980,12 @@ function numberListText(list, env, kind, valueIsBound) {
|
|
|
1816
1980
|
const numberSyntaxError = new PrologError('syntax_error(number)');
|
|
1817
1981
|
|
|
1818
1982
|
function numberListBuiltin(kind) {
|
|
1819
|
-
return function* ({ goal, env }) {
|
|
1983
|
+
return function* ({ solver, goal, env }) {
|
|
1820
1984
|
const value = deref(goal.args[0], env);
|
|
1821
1985
|
if (value.type !== VAR && value.type !== NUMBER) throw new PrologError('type_error(number)', value);
|
|
1822
1986
|
const list = deref(goal.args[1], env);
|
|
1823
1987
|
if (value.type === VAR && list.type === VAR) throw new PrologError('instantiation_error');
|
|
1824
|
-
const text = numberListText(list, env, kind, value.type === NUMBER);
|
|
1988
|
+
const text = numberListText(list, env, kind, value.type === NUMBER, solver);
|
|
1825
1989
|
if (value.type === NUMBER) {
|
|
1826
1990
|
if (text != null) {
|
|
1827
1991
|
const parsed = parseIsoNumber(text);
|
package/src/program.js
CHANGED
|
@@ -44,6 +44,8 @@ export class Program {
|
|
|
44
44
|
this.libraryImports = [];
|
|
45
45
|
this.interopPortabilityWarnings = [];
|
|
46
46
|
this.dynamicPredicates = new Set();
|
|
47
|
+
this.multifilePredicates = new Set();
|
|
48
|
+
this.discontiguousPredicates = new Set();
|
|
47
49
|
this.strictIso = options.isoStrict === true;
|
|
48
50
|
this.operators = new Map();
|
|
49
51
|
const predefinedOperatorSets = this.strictIso
|
|
@@ -55,6 +57,7 @@ export class Program {
|
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
this.initializations = [];
|
|
60
|
+
this._initializationsExecuted = false;
|
|
58
61
|
this.quads = [];
|
|
59
62
|
this.prologFlagDirectives = [];
|
|
60
63
|
this.charConversionDirectives = [];
|
|
@@ -476,11 +479,94 @@ class ProgramBuilder {
|
|
|
476
479
|
this.options = options;
|
|
477
480
|
this.program = program ?? new Program([], { ...options, [DEFER_PROGRAM_BUILD]: true });
|
|
478
481
|
this.declaredDynamicIndicators = new Map();
|
|
482
|
+
this.declaredMultifileIndicators = new Map();
|
|
483
|
+
this.declaredDiscontiguousIndicators = new Map();
|
|
484
|
+
this.directiveDeclarationsByText = new Map();
|
|
485
|
+
this.clauseTextUnitsByKey = new Map();
|
|
486
|
+
this.lastPredicateByText = new Map();
|
|
479
487
|
this.lastGroupKey = null;
|
|
480
488
|
this.lastGroup = null;
|
|
481
489
|
this.finished = false;
|
|
482
490
|
}
|
|
483
491
|
|
|
492
|
+
declarationSet(textUnit, kind) {
|
|
493
|
+
const unit = textUnit ?? '<input>';
|
|
494
|
+
let declarations = this.directiveDeclarationsByText.get(unit);
|
|
495
|
+
if (!declarations) {
|
|
496
|
+
declarations = { dynamic: new Set(), multifile: new Set(), discontiguous: new Set() };
|
|
497
|
+
this.directiveDeclarationsByText.set(unit, declarations);
|
|
498
|
+
}
|
|
499
|
+
return declarations[kind];
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
noteProcedureClause(key, textUnit) {
|
|
503
|
+
if (!this.program.strictIso) return;
|
|
504
|
+
const unit = textUnit ?? '<input>';
|
|
505
|
+
const declarations = this.directiveDeclarationsByText.get(unit);
|
|
506
|
+
const units = this.clauseTextUnitsByKey.get(key) ?? new Set();
|
|
507
|
+
|
|
508
|
+
if (this.program.dynamicPredicates.has(key) && !declarations?.dynamic.has(key)) {
|
|
509
|
+
throw new Error(`ISO preparation error: dynamic(${key}) must precede clauses for ${key} in every Prolog text`);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (units.size > 0 && !units.has(unit)) {
|
|
513
|
+
if (!declarations?.multifile.has(key)) {
|
|
514
|
+
throw new Error(`ISO preparation error: multifile(${key}) is required before clauses for ${key} in this Prolog text`);
|
|
515
|
+
}
|
|
516
|
+
for (const prior of units) {
|
|
517
|
+
if (!this.directiveDeclarationsByText.get(prior)?.multifile.has(key)) {
|
|
518
|
+
throw new Error(`ISO preparation error: multifile(${key}) is required in every Prolog text containing clauses for ${key}`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const last = this.lastPredicateByText.get(unit);
|
|
524
|
+
if (last != null && last !== key && units.has(unit) && !declarations?.discontiguous.has(key)) {
|
|
525
|
+
throw new Error(`ISO preparation error: discontiguous(${key}) must precede non-consecutive clauses for ${key}`);
|
|
526
|
+
}
|
|
527
|
+
units.add(unit);
|
|
528
|
+
this.clauseTextUnitsByKey.set(key, units);
|
|
529
|
+
this.lastPredicateByText.set(unit, key);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
addProcedureDirective(clause, kind, targetSet, declaredMap) {
|
|
533
|
+
const program = this.program;
|
|
534
|
+
const indicators = procedureDirectiveIndicators(clause, kind, program.strictIso);
|
|
535
|
+
if (indicators == null) return false;
|
|
536
|
+
const textUnit = clause.textUnit ?? '<input>';
|
|
537
|
+
const local = this.declarationSet(textUnit, kind);
|
|
538
|
+
for (const indicator of indicators) {
|
|
539
|
+
assertDynamicIndicatorIsDefinable(indicator, program.strictIso);
|
|
540
|
+
const module = clause.module ?? 'user';
|
|
541
|
+
const key = modulePredicateKey(module, indicator.name, indicator.arity);
|
|
542
|
+
if (program.strictIso && this.clauseTextUnitsByKey.get(key)?.has(textUnit)) {
|
|
543
|
+
throw new Error(`ISO preparation error: ${kind}(${indicator.key}) must precede all clauses for ${indicator.key}`);
|
|
544
|
+
}
|
|
545
|
+
if (program.strictIso && kind === 'dynamic') {
|
|
546
|
+
for (const prior of this.clauseTextUnitsByKey.get(key) ?? []) {
|
|
547
|
+
if (prior !== textUnit && !this.directiveDeclarationsByText.get(prior)?.dynamic.has(key)) {
|
|
548
|
+
throw new Error(`ISO preparation error: dynamic(${indicator.key}) is required in every Prolog text containing clauses for ${indicator.key}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (program.strictIso && kind === 'multifile') {
|
|
553
|
+
for (const prior of this.clauseTextUnitsByKey.get(key) ?? []) {
|
|
554
|
+
if (prior !== textUnit && !this.directiveDeclarationsByText.get(prior)?.multifile.has(key)) {
|
|
555
|
+
throw new Error(`ISO preparation error: multifile(${indicator.key}) is required in every Prolog text containing clauses for ${indicator.key}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
local.add(key);
|
|
560
|
+
targetSet.add(key);
|
|
561
|
+
declaredMap.set(`${textUnit}\u0000${key}`, { ...indicator, key, module, textUnit });
|
|
562
|
+
if (kind === 'dynamic') {
|
|
563
|
+
const existing = program.groups.get(key);
|
|
564
|
+
if (existing) existing.dynamic = true;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
|
|
484
570
|
addClauses(clauses) {
|
|
485
571
|
if (this.finished) throw new Error('program builder is already finalized');
|
|
486
572
|
const program = this.program;
|
|
@@ -501,6 +587,7 @@ class ProgramBuilder {
|
|
|
501
587
|
assertPredicateIsDefinable(clause.headName, 2, program.strictIso);
|
|
502
588
|
const module = clause.module ?? 'user';
|
|
503
589
|
const key = modulePredicateKey(module, clause.headName, 2);
|
|
590
|
+
this.noteProcedureClause(key, clause.textUnit);
|
|
504
591
|
let group = key === lastGroupKey ? lastGroup : program.groups.get(key);
|
|
505
592
|
if (!group) {
|
|
506
593
|
group = program.makeGroup(clause.headName, 2, module);
|
|
@@ -524,6 +611,7 @@ class ProgramBuilder {
|
|
|
524
611
|
if (head.type !== ATOM && head.type !== COMPOUND) continue;
|
|
525
612
|
const module = clause.module ?? 'user';
|
|
526
613
|
const key = modulePredicateKey(module, head.name, head.arity);
|
|
614
|
+
this.noteProcedureClause(key, clause.textUnit);
|
|
527
615
|
let group = key === lastGroupKey ? lastGroup : program.groups.get(key);
|
|
528
616
|
if (!group) {
|
|
529
617
|
group = program.makeGroup(head.name, head.arity, module);
|
|
@@ -553,14 +641,10 @@ class ProgramBuilder {
|
|
|
553
641
|
addDirectiveClause(clause) {
|
|
554
642
|
const program = this.program;
|
|
555
643
|
const module = clause.module ?? 'user';
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
this.declaredDynamicIndicators.set(key, { ...indicator, key, module });
|
|
561
|
-
const existing = program.groups.get(key);
|
|
562
|
-
if (existing) existing.dynamic = true;
|
|
563
|
-
}
|
|
644
|
+
const textUnit = clause.textUnit ?? '<input>';
|
|
645
|
+
this.addProcedureDirective(clause, 'dynamic', program.dynamicPredicates, this.declaredDynamicIndicators);
|
|
646
|
+
this.addProcedureDirective(clause, 'multifile', program.multifilePredicates, this.declaredMultifileIndicators);
|
|
647
|
+
this.addProcedureDirective(clause, 'discontiguous', program.discontiguousPredicates, this.declaredDiscontiguousIndicators);
|
|
564
648
|
|
|
565
649
|
const operator = operatorDirective(clause);
|
|
566
650
|
if (operator) {
|
|
@@ -586,6 +670,7 @@ class ProgramBuilder {
|
|
|
586
670
|
} else if (directive?.type === COMPOUND && directive.name === 'char_conversion' && directive.arity === 2) {
|
|
587
671
|
program.charConversionDirectives.push(directive.args);
|
|
588
672
|
}
|
|
673
|
+
if (program.strictIso) this.lastPredicateByText.set(textUnit, '@directive');
|
|
589
674
|
}
|
|
590
675
|
|
|
591
676
|
finish() {
|
|
@@ -596,9 +681,13 @@ class ProgramBuilder {
|
|
|
596
681
|
// A dynamic declaration creates a procedure even when it has no clauses.
|
|
597
682
|
// Calls to that procedure fail normally instead of being treated as calls
|
|
598
683
|
// to an unknown predicate.
|
|
599
|
-
for (const
|
|
600
|
-
|
|
601
|
-
|
|
684
|
+
for (const declarations of [
|
|
685
|
+
this.declaredDynamicIndicators, this.declaredMultifileIndicators, this.declaredDiscontiguousIndicators,
|
|
686
|
+
]) {
|
|
687
|
+
for (const indicator of declarations.values()) {
|
|
688
|
+
if (!program.groups.has(indicator.key)) {
|
|
689
|
+
program.groups.set(indicator.key, program.makeGroup(indicator.name, indicator.arity, indicator.module));
|
|
690
|
+
}
|
|
602
691
|
}
|
|
603
692
|
}
|
|
604
693
|
program.mutable = program.dynamicPredicates.size > 0;
|
|
@@ -694,11 +783,14 @@ function loadSourcesIntoBuilder(builder, sources, options, fast) {
|
|
|
694
783
|
if (filename) ensured.add(filename);
|
|
695
784
|
}
|
|
696
785
|
try {
|
|
697
|
-
for (const item of prepared) {
|
|
786
|
+
for (const [sourceIndex, item] of prepared.entries()) {
|
|
698
787
|
const text = typeof item.source === 'string'
|
|
699
788
|
? item.source
|
|
700
789
|
: item.source?.text ?? item.source?.source ?? '';
|
|
701
|
-
const context = {
|
|
790
|
+
const context = {
|
|
791
|
+
module: 'user',
|
|
792
|
+
textUnit: sourcePath(item.options) ?? `<input:${sourceIndex}>`,
|
|
793
|
+
};
|
|
702
794
|
if (!loadSourceIntoBuilder(builder, text, item.options, ensured, loadedModules, fast, context)) return false;
|
|
703
795
|
}
|
|
704
796
|
const autoloadGoals = parseInteropGoalInputs(options.autoloadGoals, {
|
|
@@ -964,6 +1056,7 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
964
1056
|
if (moduleDeclaration) {
|
|
965
1057
|
flush();
|
|
966
1058
|
clause.module = moduleDeclaration.name;
|
|
1059
|
+
clause.textUnit = context.textUnit;
|
|
967
1060
|
clause.moduleFilename = options.filename ?? '<input>';
|
|
968
1061
|
builder.addClauses([clause]);
|
|
969
1062
|
context.module = moduleDeclaration.name;
|
|
@@ -974,6 +1067,7 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
974
1067
|
if (use) {
|
|
975
1068
|
flush();
|
|
976
1069
|
clause.module = context.module;
|
|
1070
|
+
clause.textUnit = context.textUnit;
|
|
977
1071
|
builder.addClauses([clause]);
|
|
978
1072
|
const libraryName = libraryDesignationName(use.designation);
|
|
979
1073
|
if (libraryName != null) {
|
|
@@ -996,6 +1090,7 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
996
1090
|
const include = includeDirective(clause);
|
|
997
1091
|
if (!include) {
|
|
998
1092
|
clause.module ??= context.module;
|
|
1093
|
+
clause.textUnit ??= context.textUnit;
|
|
999
1094
|
if (!isDirectiveClause(clause)) {
|
|
1000
1095
|
for (const goal of clause.body) annotateGoalModule(goal, clause.module);
|
|
1001
1096
|
}
|
|
@@ -1004,9 +1099,15 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
1004
1099
|
return;
|
|
1005
1100
|
}
|
|
1006
1101
|
flush();
|
|
1102
|
+
if (builder.program.strictIso && include.name === 'ensure_loaded') {
|
|
1103
|
+
builder.lastPredicateByText.set(context.textUnit ?? '<input>', '@directive');
|
|
1104
|
+
}
|
|
1007
1105
|
const child = readIncludedSource(include, options, ensured);
|
|
1008
1106
|
if (!child) return;
|
|
1009
|
-
|
|
1107
|
+
const childContext = include.name === 'include'
|
|
1108
|
+
? context
|
|
1109
|
+
: { module: context.module, textUnit: sourcePath(child.options) ?? context.textUnit };
|
|
1110
|
+
if (!loadSourceIntoBuilder(builder, child.text, child.options, ensured, loadedModules, fast, childContext)) {
|
|
1010
1111
|
throw FAST_PARSE_ABORT;
|
|
1011
1112
|
}
|
|
1012
1113
|
};
|
|
@@ -1019,6 +1120,7 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
1019
1120
|
bodyName, body0Type, body0Name, body1Type, body1Name,
|
|
1020
1121
|
));
|
|
1021
1122
|
batch[batch.length - 1].module = context.module;
|
|
1123
|
+
batch[batch.length - 1].textUnit = context.textUnit;
|
|
1022
1124
|
if (batch.length >= PROGRAM_BUILD_BATCH_SIZE) flush();
|
|
1023
1125
|
};
|
|
1024
1126
|
const parsed = tryParseClausesFastInto(source, accept, acceptBinary, options);
|
|
@@ -1148,16 +1250,25 @@ function isDirectiveClause(clause) {
|
|
|
1148
1250
|
clause.head.name === ':-' && clause.head.arity === 1;
|
|
1149
1251
|
}
|
|
1150
1252
|
|
|
1151
|
-
function
|
|
1152
|
-
if (!isDirectiveClause(clause)) return
|
|
1253
|
+
function procedureDirectiveIndicators(clause, name, strictIso = false) {
|
|
1254
|
+
if (!isDirectiveClause(clause)) return null;
|
|
1153
1255
|
const directive = clause.head.args[0];
|
|
1154
|
-
if (directive.type !== COMPOUND || directive.name !==
|
|
1256
|
+
if (directive.type !== COMPOUND || directive.name !== name || directive.arity !== 1) return null;
|
|
1155
1257
|
const terms = properListItems(directive.args[0], new Env()) ?? flattenDirectiveSequence(directive.args[0]);
|
|
1156
|
-
|
|
1258
|
+
const indicators = terms.map((indicator) =>
|
|
1157
1259
|
indicator.type === COMPOUND && ['/', '//'].includes(indicator.name) && indicator.arity === 2
|
|
1158
1260
|
? nonterminalOrPredicateIndicator(indicator)
|
|
1159
1261
|
: null
|
|
1160
|
-
)
|
|
1262
|
+
);
|
|
1263
|
+
if (indicators.some((indicator) => indicator == null)) {
|
|
1264
|
+
if (strictIso) throw new Error(`ISO preparation error: invalid ${name}/1 predicate indicator`);
|
|
1265
|
+
return [];
|
|
1266
|
+
}
|
|
1267
|
+
return indicators;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function dynamicDirectiveIndicators(clause) {
|
|
1271
|
+
return procedureDirectiveIndicators(clause, 'dynamic', false) ?? [];
|
|
1161
1272
|
}
|
|
1162
1273
|
|
|
1163
1274
|
function nonterminalOrPredicateIndicator(term) {
|
package/src/solver.js
CHANGED
|
@@ -283,6 +283,7 @@ export class Solver {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
runInitializations() {
|
|
286
|
+
if (this.program._initializationsExecuted) return;
|
|
286
287
|
for (const goal of this.program.initializations ?? []) {
|
|
287
288
|
let succeeded = false;
|
|
288
289
|
for (const _ of this.solve([goal], new Env(), 0)) {
|
|
@@ -291,6 +292,9 @@ export class Solver {
|
|
|
291
292
|
}
|
|
292
293
|
if (!succeeded) throw new PrologError('initialization_error');
|
|
293
294
|
}
|
|
295
|
+
// 7.4.2.6 attaches initialization to preparation of the Prolog text, not
|
|
296
|
+
// to each later Solver instance created for the same prepared Program.
|
|
297
|
+
this.program._initializationsExecuted = true;
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
*solve(goals, env = new Env(), depth = 0) {
|
|
@@ -32,16 +32,16 @@ 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, writer/read-back regressions, and strict ASCII PCS/collation boundary tests. The implementation-defined 6.5/6.6 character-model decisions are now closed; wider shall-by-shall lexical mapping remains open. |
|
|
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, plus strict checks for the required `variable < float < integer < atom < compound` type order and PCS-based atom collation. |
|
|
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`
|
|
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` affects later unquoted source text and respects `char_conversion=off`. Strict preparation now enforces declaration-before-clause ordering for `dynamic/1`, `multifile/1`, and `discontiguous/1`, cross-text `multifile/1`, discontiguous clause grouping, empty declared procedures, include textual-replacement behavior, and one-time initialization per prepared program. The remaining shall-by-shall mapping is still open. |
|
|
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
|
-
| 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The
|
|
40
|
+
| 7.10 — input/output concepts | audit | Stream, character/byte I/O, read/write options, operator-sensitive write-back, and Corrigendum 3 writer cases. The audit now also covers write-mode creation/truncation, append creation, repositioned overwrite, flushing, EOF actions, close/current-stream handling, strict stream-position terms, stream-property validation, stream-term requirements, and text-vs-binary permission errors. The full option/mode cross-product is still being mapped. |
|
|
41
41
|
| 7.11 — flags | covered | The complete Part 1 flag set, selected defaults, standard value domains, changeability, `current_prolog_flag/2`, and `set_prolog_flag/2` error behavior have dedicated strict tests. EyeProlog selects `bounded=false` and `integer_rounding_function=toward_zero`; valid alternative values of those fixed flags reach `permission_error(modify,flag,...)`, while `max_integer` and `min_integer` have no current value. Strict mode excludes the EyeProlog `occurs_check` extension. |
|
|
42
|
-
| 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is
|
|
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. The
|
|
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. |
|
|
42
|
+
| 7.12 — errors | audit | ISO `error(Error, Context)` envelope, type/domain/permission/representation/evaluation/syntax families and focused error cases. Exact ISO 8.14.1-8.14.4 error precedence is covered for `read_term/3`, `write_term/3`, `op/3`, and `current_op/3`; the continuing audit has corrected additional 8.11-8.13 stream/character/byte errors, Corrigendum 2 `keysort/2` errors, and arithmetic culprit reporting. A complete one-row-per-prescribed-error ordering map remains open. |
|
|
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. The audit now includes corrected `keysort/2` variable/non-pair errors, stricter 8.11-8.13 stream and character/byte modes/errors, the 8.14 option/error-order subfamily, and closed 8.17 flags. The remaining mode/error matrix is not yet one-row-per-standard-row. |
|
|
44
|
+
| Clause 9 — evaluable functors | audit | Integer/float/rounding/transcendental/bitwise suites and corrigendum cases. Strict mode excludes the EyeProlog-only evaluable atom `e`, retains Corrigendum 2 arithmetic additions, and reports unknown zero-arity evaluables with the required `F/0` culprit shape. Host floating-point representation choices remain documented implementation-defined behavior; exhaustive exceptional-value precedence remains open. |
|
|
45
45
|
| Corrigendum 1 | covered | Double-quoted atom/operator-priority corrections have dedicated cases. |
|
|
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. |
|
|
@@ -81,6 +81,20 @@ the Part 1 plus Corrigendum 3 option surface; the normal-profile
|
|
|
81
81
|
`double_quotes/1` write option remains an explicitly documented EyeProlog
|
|
82
82
|
extension.
|
|
83
83
|
|
|
84
|
+
|
|
85
|
+
The current continuation of issue #65 expands that audit into Prolog-text,
|
|
86
|
+
stream, atomic-conversion, sorting, and arithmetic edge cases. Strict
|
|
87
|
+
preparation now enforces the Part 1 declaration constraints for `dynamic/1`,
|
|
88
|
+
`multifile/1`, and `discontiguous/1`, including cross-text multifile use and
|
|
89
|
+
one-time initialization per prepared program. Stream handling now distinguishes
|
|
90
|
+
text and binary permission errors, validates stream properties and stream-term
|
|
91
|
+
requirements, and tightens creation, truncation/append, repositioning, flush,
|
|
92
|
+
EOF, close, and current-stream behavior. Corrigendum 2 `keysort/2` variable and
|
|
93
|
+
non-pair errors are corrected. Strict arithmetic no longer exposes the
|
|
94
|
+
EyeProlog-only evaluable atom `e`, while the Corrigendum arithmetic additions
|
|
95
|
+
remain available. These corrections narrow the remaining `audit` rows but do
|
|
96
|
+
not by themselves close the full shall-by-shall/error-order exit criteria.
|
|
97
|
+
|
|
84
98
|
## Strict-core boundary
|
|
85
99
|
|
|
86
100
|
`isoStrict: true` is intentionally a **Part 1 + Corrigenda 1-3** mode. It does
|
|
@@ -36,7 +36,7 @@ Status values are:
|
|
|
36
36
|
| 7.1.4.1 | Set `C` of characters represented by one-char atoms | In strict mode `C` is exactly the ASCII PCS U+0000..U+007F. Normal mode extends character predicates to Unicode scalar values U+0000..U+10FFFF excluding surrogates. | **defined** — `src/iso-character.js`, `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
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
|
-
| 7.4.2.6 | Order of `initialization/1` goals | Initialization goals run once
|
|
39
|
+
| 7.4.2.6 | Order of `initialization/1` goals | Initialization goals run once per prepared `Program`, in source/inclusion order, before requested goals; each must obtain a first solution. Reusing the same prepared program in another solver does not execute its initialization goals again. | **defined** — `Program.initializations`, preparation state, `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`. |
|
|
42
42
|
| 7.4.2.8 | Position at which an ensured text is included | The first `ensure_loaded/1` expands the text in place at the directive; later requests for the same resolved path are ignored. | **defined** — `src/program.js` (`ensured` set and in-place builder loading). |
|
|
@@ -11,14 +11,14 @@ compliance audit and the remaining work before a full conformance claim.
|
|
|
11
11
|
| Clause 6 lexical and term syntax | tokenizer, operator parser, lists, curly terms, quotes, numeric syntax, comments, strict ASCII PCS/collation | `scryer_lexical_terms`, `lexical_and_curly_terms`, `double_quoted_lists`, `corrigendum1_double_quote_operator`, `wg17_syntax_high_risk`, `wg17_invalid_octal_escape`, `wg17_unterminated_quoted_token`, `wg17_literal_newline_in_quote`, `wg17_non_iso_escape`, strict PCS/collation tests in `run-iso-strict.mjs`, 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
|
-
| 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 |
|
|
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, including Corrigendum 2 `keysort/2` variable/non-pair errors |
|
|
15
15
|
| 8.6-8.7 arithmetic predicates | `is/2` and all six arithmetic comparisons | `arithmetic`, comparison cases, isolated evaluation errors |
|
|
16
16
|
| 8.8-8.10 database and solutions | logical update view, dynamic mutation, all-solutions grouping | `dynamic_database`, `trealla_logical_update_view`, `corrigenda_retractall`, `grouped_solutions_and_clauses` |
|
|
17
|
-
| 8.11-8.14 streams and term I/O | text/binary streams, properties, units, read/write options and operators | `streams_and_term_io`, `operators`, Corrigendum 3 option cases, stream error cases,
|
|
17
|
+
| 8.11-8.14 streams and term I/O | text/binary streams, properties, units, read/write options and operators | `streams_and_term_io`, `operators`, Corrigendum 3 option cases, stream error cases, strict 8.14.1-8.14.4 option/error-order tests, and issue #65 checks for stream-term/property validation, text-vs-binary permissions, EOF/flush/position, and open/close semantics |
|
|
18
18
|
| 8.15 logic and control | negation, once, repeat, `call/2` through `call/8`, `false/0` | `logtalk_once`, `corrigenda_call_closure`, `false_builtin` |
|
|
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 | complete required Part 1 flag set, selected defaults, standard value domains/changeability, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases, and the strict complete-flag audit in `run-iso-strict.mjs` |
|
|
21
|
-
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power` |
|
|
21
|
+
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power`, strict exclusion of the normal-mode `e` evaluable extension and zero-arity unknown-evaluable error checks |
|
|
22
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
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
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
error(permission_error(input,
|
|
1
|
+
error(permission_error(input, text_stream), '$stream'(0))
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -7653,7 +7653,13 @@ integrity relations, automatic tabling, and the EyeProlog library. The strict au
|
|
|
7653
7653
|
set and the prescribed 8.14 error precedence for `read_term/3`, `write_term/3`,
|
|
7654
7654
|
`op/3`, and `current_op/3`; these checks distinguish invalid values from
|
|
7655
7655
|
valid-but-fixed flag values and reject normal-profile write options at the
|
|
7656
|
-
strict boundary. The
|
|
7656
|
+
strict boundary. The continuing issue #65 audit also enforces Part 1
|
|
7657
|
+
preparation constraints for `dynamic/1`, `multifile/1`, and `discontiguous/1`,
|
|
7658
|
+
keeps initialization tied to preparation of a program, tightens stream
|
|
7659
|
+
creation/position/EOF behavior and text-vs-binary permission errors, corrects
|
|
7660
|
+
Corrigendum 2 `keysort/2` error cases, and keeps the non-standard evaluable atom
|
|
7661
|
+
`e` outside strict mode. These are conformance improvements, while the
|
|
7662
|
+
remaining qualifications are:
|
|
7657
7663
|
|
|
7658
7664
|
- zero-arity compound syntax such as `ready()` is represented by the atom
|
|
7659
7665
|
`ready`;
|