eyeprolog 1.3.43 → 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 +12 -0
- package/package.json +1 -1
- package/src/io.js +33 -10
- package/src/iso-arithmetic.js +12 -10
- package/src/iso.js +339 -116
- package/src/program.js +130 -19
- package/src/solver.js +8 -4
- package/test/conformance/ISO-COMPLIANCE.md +32 -6
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +5 -5
- package/test/conformance/ISO-MATRIX.md +4 -4
- package/test/conformance/README.md +3 -1
- package/test/conformance/expected-errors/iso/get_byte_text_stream.txt +1 -1
- package/test/conformance/expected-errors/iso/swipl_current_op_float_priority.txt +1 -1
- package/test/conformance/expected-errors/iso/swipl_current_op_priority_type.txt +1 -1
- package/test/conformance/expected-errors/iso/swipl_current_op_specifier_type.txt +1 -1
- package/test/run-iso-strict.mjs +106 -0
- package/the-art-of-eyeprolog.md +19 -4
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.
|
|
@@ -690,44 +698,49 @@ function* setPrologFlagBuiltin({ solver, goal, env }) {
|
|
|
690
698
|
|
|
691
699
|
const operatorSpecifiers = new Set(['fx', 'fy', 'xf', 'yf', 'xfx', 'xfy', 'yfx']);
|
|
692
700
|
|
|
693
|
-
function
|
|
701
|
+
function opListPreflight(term, env) {
|
|
694
702
|
const value = deref(term, env);
|
|
695
703
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
696
|
-
if (value.type
|
|
697
|
-
const
|
|
698
|
-
if (
|
|
699
|
-
|
|
704
|
+
if (value.type === ATOM) return { value, items: [value], proper: true, atom: true };
|
|
705
|
+
const { items, tail } = listElements(value, env);
|
|
706
|
+
if (tail.type === VAR || items.some((item) => item.type === VAR)) {
|
|
707
|
+
throw new PrologError('instantiation_error');
|
|
708
|
+
}
|
|
709
|
+
const proper = tail.type === ATOM && tail.name === '[]';
|
|
710
|
+
return { value, items, proper, atom: false };
|
|
700
711
|
}
|
|
701
712
|
|
|
702
|
-
function
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
if (value.type !== ATOM) throw new PrologError('type_error(atom)', value);
|
|
706
|
-
if (!operatorSpecifiers.has(value.name)) throw new PrologError('domain_error(operator_specifier)', value);
|
|
707
|
-
return value.name;
|
|
708
|
-
}
|
|
713
|
+
function* opBuiltin({ solver, goal, env }) {
|
|
714
|
+
const priorityTerm = deref(goal.args[0], env);
|
|
715
|
+
const specifierTerm = deref(goal.args[1], env);
|
|
709
716
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
717
|
+
// ISO 8.14.3.3 prescribes the ordering below. In particular, all three
|
|
718
|
+
// instantiation cases precede type/domain errors, and Operator list-shape
|
|
719
|
+
// errors precede priority/specifier domain errors.
|
|
720
|
+
if (priorityTerm.type === VAR) throw new PrologError('instantiation_error');
|
|
721
|
+
if (specifierTerm.type === VAR) throw new PrologError('instantiation_error');
|
|
722
|
+
const operator = opListPreflight(goal.args[2], env);
|
|
723
|
+
|
|
724
|
+
if (priorityTerm.type !== NUMBER || !isDecimalInteger(priorityTerm.name)) {
|
|
725
|
+
throw new PrologError('type_error(integer)', priorityTerm);
|
|
718
726
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (
|
|
727
|
+
if (specifierTerm.type !== ATOM) throw new PrologError('type_error(atom)', specifierTerm);
|
|
728
|
+
if (!operator.atom && !operator.proper) throw new PrologError('type_error(list)', operator.value);
|
|
729
|
+
for (const item of operator.items) {
|
|
730
|
+
if (item.type !== ATOM) throw new PrologError('type_error(atom)', item);
|
|
723
731
|
}
|
|
724
|
-
return items.map((item) => deref(item, env));
|
|
725
|
-
}
|
|
726
732
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
733
|
+
const priorityBig = BigInt(priorityTerm.name);
|
|
734
|
+
if (priorityBig < 0n || priorityBig > 1200n) {
|
|
735
|
+
throw new PrologError('domain_error(operator_priority)', priorityTerm);
|
|
736
|
+
}
|
|
737
|
+
if (!operatorSpecifiers.has(specifierTerm.name)) {
|
|
738
|
+
throw new PrologError('domain_error(operator_specifier)', specifierTerm);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const priority = Number(priorityBig);
|
|
742
|
+
const specifier = specifierTerm.name;
|
|
743
|
+
for (const name of operator.items) {
|
|
731
744
|
if (name.name === ',') {
|
|
732
745
|
throw new PrologError('permission_error(modify, operator)', name);
|
|
733
746
|
}
|
|
@@ -755,19 +768,16 @@ function* currentOpBuiltin({ solver, goal, env }) {
|
|
|
755
768
|
const priority = deref(goal.args[0], env);
|
|
756
769
|
const specifier = deref(goal.args[1], env);
|
|
757
770
|
const name = deref(goal.args[2], env);
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
}
|
|
771
|
+
// ISO 8.14.4.3 defines the first two argument errors by domain, not by
|
|
772
|
+
// their underlying atom/integer representation type.
|
|
773
|
+
if (priority.type !== VAR &&
|
|
774
|
+
(priority.type !== NUMBER || !isDecimalInteger(priority.name) ||
|
|
775
|
+
BigInt(priority.name) < 0n || BigInt(priority.name) > 1200n)) {
|
|
776
|
+
throw new PrologError('domain_error(operator_priority)', priority);
|
|
765
777
|
}
|
|
766
|
-
if (specifier.type !== VAR
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
throw new PrologError('domain_error(operator_specifier)', specifier);
|
|
770
|
-
}
|
|
778
|
+
if (specifier.type !== VAR &&
|
|
779
|
+
(specifier.type !== ATOM || !operatorSpecifiers.has(specifier.name))) {
|
|
780
|
+
throw new PrologError('domain_error(operator_specifier)', specifier);
|
|
771
781
|
}
|
|
772
782
|
if (name.type !== VAR && name.type !== ATOM) throw new PrologError('type_error(atom)', name);
|
|
773
783
|
for (const definition of solver.program.operators.values()) {
|
|
@@ -828,6 +838,16 @@ function streamReference(term, env) {
|
|
|
828
838
|
throw new PrologError('domain_error(stream_or_alias)', value);
|
|
829
839
|
}
|
|
830
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
|
+
|
|
831
851
|
function requireStream(solver, term, env, mode = null) {
|
|
832
852
|
const culprit = deref(term, env);
|
|
833
853
|
const stream = solver.io.resolve(streamReference(term, env));
|
|
@@ -849,6 +869,28 @@ function optionList(term, env) {
|
|
|
849
869
|
return items.map((item) => deref(item, env));
|
|
850
870
|
}
|
|
851
871
|
|
|
872
|
+
// read_term/3 and write_term/3 have prescribed error ordering in ISO 8.14.
|
|
873
|
+
// In particular, a partial option list (or a variable option element) is
|
|
874
|
+
// diagnosed before several stream errors, while the relative order of a
|
|
875
|
+
// non-list Options term and an invalid stream-or-alias differs between the
|
|
876
|
+
// read and write predicates. Split list-shape inspection from full option
|
|
877
|
+
// validation so those predicates can follow the standard order exactly.
|
|
878
|
+
function preflightOptionList(term, env) {
|
|
879
|
+
const value = deref(term, env);
|
|
880
|
+
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
881
|
+
const { items, tail } = listElements(value, env);
|
|
882
|
+
if (tail.type === VAR || items.some((item) => item.type === VAR)) {
|
|
883
|
+
throw new PrologError('instantiation_error');
|
|
884
|
+
}
|
|
885
|
+
const proper = tail.type === ATOM && tail.name === '[]';
|
|
886
|
+
return { value, items, proper };
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function requireProperOptionList(preflight) {
|
|
890
|
+
if (!preflight.proper) throw new PrologError('type_error(list)', preflight.value);
|
|
891
|
+
return preflight.items;
|
|
892
|
+
}
|
|
893
|
+
|
|
852
894
|
function optionAtom(option, name) {
|
|
853
895
|
if (option.type !== COMPOUND || option.name !== name || option.arity !== 1) return null;
|
|
854
896
|
return option.args[0];
|
|
@@ -862,13 +904,14 @@ function openOptions(term, env) {
|
|
|
862
904
|
if ((value = optionAtom(option, 'type'))) {
|
|
863
905
|
value = deref(value, env);
|
|
864
906
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
865
|
-
if (value.type !== ATOM
|
|
866
|
-
|
|
907
|
+
if (value.type !== ATOM || !['text', 'binary'].includes(value.name)) {
|
|
908
|
+
throw new PrologError('domain_error(stream_option)', option);
|
|
909
|
+
}
|
|
867
910
|
result.type = value.name;
|
|
868
911
|
} else if ((value = optionAtom(option, 'alias'))) {
|
|
869
912
|
value = deref(value, env);
|
|
870
913
|
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
871
|
-
if (value.type !== ATOM) throw new PrologError('
|
|
914
|
+
if (value.type !== ATOM) throw new PrologError('domain_error(stream_option)', option);
|
|
872
915
|
result.alias = value.name;
|
|
873
916
|
} else if ((value = optionAtom(option, 'reposition'))) {
|
|
874
917
|
value = deref(value, env);
|
|
@@ -887,19 +930,45 @@ function openOptions(term, env) {
|
|
|
887
930
|
return result;
|
|
888
931
|
}
|
|
889
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
|
+
|
|
890
940
|
function* openBuiltin({ solver, goal, env }) {
|
|
891
|
-
const
|
|
892
|
-
const mode =
|
|
893
|
-
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);
|
|
894
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);
|
|
895
957
|
if (streamTarget.type !== VAR) throw new PrologError('uninstantiation_error', streamTarget);
|
|
896
|
-
|
|
897
|
-
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
|
+
}
|
|
898
964
|
let stream;
|
|
899
965
|
try {
|
|
900
|
-
stream = solver.io.open(
|
|
901
|
-
} catch (
|
|
902
|
-
|
|
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);
|
|
903
972
|
}
|
|
904
973
|
const next = env.clone();
|
|
905
974
|
if (unify(goal.args[2], streamHandle(stream.id), next)) yield next;
|
|
@@ -907,22 +976,35 @@ function* openBuiltin({ solver, goal, env }) {
|
|
|
907
976
|
}
|
|
908
977
|
|
|
909
978
|
function* closeBuiltin({ solver, goal, env }) {
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
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);
|
|
919
995
|
}
|
|
996
|
+
forceClose = value.name === 'true'; // rightmost contradictory option applies
|
|
920
997
|
}
|
|
921
998
|
const stream = requireStream(solver, goal.args[0], env);
|
|
922
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
|
+
}
|
|
923
1006
|
if (solver.io.currentInput === stream.id) solver.io.currentInput = 0;
|
|
924
1007
|
if (solver.io.currentOutput === stream.id) solver.io.currentOutput = 1;
|
|
925
|
-
solver.io.close(stream);
|
|
926
1008
|
}
|
|
927
1009
|
yield env;
|
|
928
1010
|
}
|
|
@@ -930,7 +1012,8 @@ function* closeBuiltin({ solver, goal, env }) {
|
|
|
930
1012
|
function* currentInputBuiltin({ solver, goal, env }) {
|
|
931
1013
|
const value = deref(goal.args[0], env);
|
|
932
1014
|
if (value.type !== VAR) {
|
|
933
|
-
const
|
|
1015
|
+
const id = streamTermReference(goal.args[0], env);
|
|
1016
|
+
const stream = solver.io.resolve(id);
|
|
934
1017
|
if (!stream) throw new PrologError('domain_error(stream)', value);
|
|
935
1018
|
if (stream.id === solver.io.currentInput) yield env;
|
|
936
1019
|
return;
|
|
@@ -941,7 +1024,8 @@ function* currentInputBuiltin({ solver, goal, env }) {
|
|
|
941
1024
|
function* currentOutputBuiltin({ solver, goal, env }) {
|
|
942
1025
|
const value = deref(goal.args[0], env);
|
|
943
1026
|
if (value.type !== VAR) {
|
|
944
|
-
const
|
|
1027
|
+
const id = streamTermReference(goal.args[0], env);
|
|
1028
|
+
const stream = solver.io.resolve(id);
|
|
945
1029
|
if (!stream) throw new PrologError('domain_error(stream)', value);
|
|
946
1030
|
if (stream.id === solver.io.currentOutput) yield env;
|
|
947
1031
|
return;
|
|
@@ -949,6 +1033,7 @@ function* currentOutputBuiltin({ solver, goal, env }) {
|
|
|
949
1033
|
const next = env.clone();
|
|
950
1034
|
if (unify(goal.args[0], streamHandle(solver.io.currentOutput), next)) yield next;
|
|
951
1035
|
}
|
|
1036
|
+
|
|
952
1037
|
function setCurrentStreamBuiltin(mode) {
|
|
953
1038
|
return function* ({ solver, goal, env }) {
|
|
954
1039
|
const stream = requireStream(solver, goal.args[0], env, mode);
|
|
@@ -958,7 +1043,14 @@ function setCurrentStreamBuiltin(mode) {
|
|
|
958
1043
|
};
|
|
959
1044
|
}
|
|
960
1045
|
function* flushOutputBuiltin({ solver, goal, env }) {
|
|
961
|
-
|
|
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
|
+
}
|
|
962
1054
|
yield env;
|
|
963
1055
|
}
|
|
964
1056
|
|
|
@@ -979,23 +1071,51 @@ function streamProperties(stream) {
|
|
|
979
1071
|
return properties;
|
|
980
1072
|
}
|
|
981
1073
|
function* setStreamPositionBuiltin({ solver, goal, env }) {
|
|
982
|
-
const
|
|
983
|
-
if (
|
|
1074
|
+
const reference = deref(goal.args[0], env);
|
|
1075
|
+
if (reference.type === VAR) throw new PrologError('instantiation_error');
|
|
984
1076
|
let position = deref(goal.args[1], env);
|
|
985
|
-
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.
|
|
986
1083
|
position = deref(position.args[0], env);
|
|
987
1084
|
}
|
|
988
|
-
if (position.type === VAR
|
|
989
|
-
|
|
1085
|
+
if (position.type === VAR || position.type !== NUMBER || !isDecimalInteger(position.name)) {
|
|
1086
|
+
throw new PrologError('domain_error(stream_position)', position);
|
|
1087
|
+
}
|
|
990
1088
|
const offset = BigInt(position.name);
|
|
991
|
-
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);
|
|
992
1093
|
stream.position = Number(offset);
|
|
993
1094
|
stream.pastEnd = false;
|
|
994
1095
|
yield env;
|
|
995
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
|
+
|
|
996
1106
|
function* streamPropertyBuiltin({ solver, goal, env }) {
|
|
997
1107
|
const reference = deref(goal.args[0], env);
|
|
998
|
-
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))];
|
|
999
1119
|
for (const stream of streams) {
|
|
1000
1120
|
for (const property of streamProperties(stream)) {
|
|
1001
1121
|
const next = env.clone();
|
|
@@ -1011,27 +1131,69 @@ function outputStreamFor(solver, goal, env) {
|
|
|
1011
1131
|
return goal.arity === 1 ? solver.io.resolve(solver.io.currentOutput) : requireStream(solver, goal.args[0], env, 'write');
|
|
1012
1132
|
}
|
|
1013
1133
|
function* atEndBuiltin({ solver, goal, env }) {
|
|
1014
|
-
|
|
1015
|
-
|
|
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));
|
|
1016
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
|
+
|
|
1017
1174
|
function inputUnitBuiltin(name) {
|
|
1018
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);
|
|
1019
1180
|
const stream = inputStreamFor(solver, goal, env);
|
|
1020
1181
|
const binary = name.endsWith('byte');
|
|
1021
|
-
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
|
+
}
|
|
1022
1188
|
if (stream.pastEnd && stream.eofAction === 'error') {
|
|
1023
1189
|
throw new PrologError('permission_error(input, past_end_of_stream)', streamHandle(stream.id));
|
|
1024
1190
|
}
|
|
1025
1191
|
if (stream.pastEnd && stream.eofAction === 'reset') {
|
|
1026
|
-
// A terminal EOF (Ctrl-D) is local to one input operation. The next
|
|
1027
|
-
// operation should wait for fresh terminal input rather than rewinding
|
|
1028
|
-
// and replaying the already-consumed interactive buffer.
|
|
1029
1192
|
if (typeof stream.interactiveReadUnit !== 'function') stream.position = 0;
|
|
1030
1193
|
stream.pastEnd = false;
|
|
1031
1194
|
}
|
|
1032
1195
|
const peek = name.startsWith('peek');
|
|
1033
|
-
if (stream.position >= stream.content.length &&
|
|
1034
|
-
typeof stream.interactiveReadUnit === 'function') {
|
|
1196
|
+
if (stream.position >= stream.content.length && typeof stream.interactiveReadUnit === 'function') {
|
|
1035
1197
|
const text = stream.interactiveReadUnit();
|
|
1036
1198
|
if (text != null) {
|
|
1037
1199
|
stream.content += String(text);
|
|
@@ -1042,50 +1204,68 @@ function inputUnitBuiltin(name) {
|
|
|
1042
1204
|
try {
|
|
1043
1205
|
unit = solver.io.readUnit(stream, peek);
|
|
1044
1206
|
} catch (error) {
|
|
1045
|
-
if (error?.name === 'InvalidCharacterEncodingError')
|
|
1046
|
-
throw new PrologError('representation_error(character)');
|
|
1047
|
-
}
|
|
1207
|
+
if (error?.name === 'InvalidCharacterEncodingError') throw new PrologError('representation_error(character)');
|
|
1048
1208
|
throw error;
|
|
1049
1209
|
}
|
|
1050
1210
|
if (unit == null && !peek) stream.pastEnd = true;
|
|
1051
1211
|
if (!binary && unit != null && solver.isoStrict && !isStrictIsoPcsCharacter(unit)) {
|
|
1052
1212
|
throw new PrologError('representation_error(character)');
|
|
1053
1213
|
}
|
|
1054
|
-
const result = unit == null
|
|
1214
|
+
const result = unit == null
|
|
1215
|
+
? (binary ? numberTerm(-1) : name.endsWith('code') ? numberTerm(-1) : atom('end_of_file'))
|
|
1055
1216
|
: binary ? numberTerm(unit) : name.endsWith('code') ? numberTerm(unit.codePointAt(0)) : atom(unit);
|
|
1056
|
-
const target = goal.args[goal.arity - 1];
|
|
1057
1217
|
const next = env.clone();
|
|
1058
1218
|
if (unify(target, result, next)) yield next;
|
|
1059
1219
|
};
|
|
1060
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
|
+
|
|
1061
1239
|
function outputUnitBuiltin(name) {
|
|
1062
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);
|
|
1063
1244
|
const stream = outputStreamFor(solver, goal, env);
|
|
1064
|
-
const value = deref(goal.args[goal.arity - 1], env);
|
|
1065
|
-
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
1066
1245
|
if (name === 'put_char') {
|
|
1067
1246
|
if (stream.type !== 'text') throw new PrologError('permission_error(output, binary_stream)', streamHandle(stream.id));
|
|
1068
|
-
if (!oneChar(value)) throw new PrologError('type_error(character)', value);
|
|
1069
1247
|
if (solver.isoStrict && !isStrictIsoPcsCharacter(value.name)) {
|
|
1070
1248
|
throw new PrologError('representation_error(character)', value);
|
|
1071
1249
|
}
|
|
1072
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));
|
|
1073
1254
|
} else {
|
|
1074
|
-
if (
|
|
1075
|
-
throw new PrologError('permission_error(output, stream)', streamHandle(stream.id));
|
|
1076
|
-
}
|
|
1077
|
-
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));
|
|
1078
1256
|
const code = BigInt(value.name);
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
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))) {
|
|
1082
1261
|
throw new PrologError('representation_error(character_code)');
|
|
1083
1262
|
}
|
|
1084
|
-
solver.io.writeUnit(stream,
|
|
1263
|
+
solver.io.writeUnit(stream, String.fromCodePoint(Number(code)));
|
|
1085
1264
|
}
|
|
1086
1265
|
yield env;
|
|
1087
1266
|
};
|
|
1088
1267
|
}
|
|
1268
|
+
|
|
1089
1269
|
function* nlBuiltin({ solver, goal, env }) {
|
|
1090
1270
|
const stream = goal.arity === 0
|
|
1091
1271
|
? solver.io.resolve(solver.io.currentOutput)
|
|
@@ -1250,6 +1430,13 @@ export function isCompleteReadTermText(text, solver) {
|
|
|
1250
1430
|
}
|
|
1251
1431
|
|
|
1252
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
|
+
}
|
|
1253
1440
|
let requestedInteractiveTerm = false;
|
|
1254
1441
|
while (true) {
|
|
1255
1442
|
let sawCandidate = false;
|
|
@@ -1293,6 +1480,7 @@ function readTermFromStream(stream, solver) {
|
|
|
1293
1480
|
stream.position = source.length;
|
|
1294
1481
|
if (!sawCandidate) {
|
|
1295
1482
|
if (hasNonLayoutRemainder(source, remainderStart)) throw new PrologError('syntax_error(read_term)');
|
|
1483
|
+
stream.pastEnd = true;
|
|
1296
1484
|
return { term: atom('end_of_file'), variables: [] };
|
|
1297
1485
|
}
|
|
1298
1486
|
throw new PrologError('syntax_error(read_term)');
|
|
@@ -1306,17 +1494,38 @@ function* readBuiltin({ solver, goal, env }) {
|
|
|
1306
1494
|
const { term } = readTermFromStream(stream, solver);
|
|
1307
1495
|
if (unify(goal.args[goal.arity - 1], term, next)) yield next;
|
|
1308
1496
|
}
|
|
1497
|
+
function validateReadOptions(options) {
|
|
1498
|
+
for (const option of options) {
|
|
1499
|
+
if (option.type !== COMPOUND || option.arity !== 1 ||
|
|
1500
|
+
!['variables', 'variable_names', 'singletons'].includes(option.name)) {
|
|
1501
|
+
throw new PrologError('domain_error(read_option)', option);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1309
1506
|
function* readTermBuiltin({ solver, goal, env }) {
|
|
1310
|
-
const
|
|
1507
|
+
const optionTerm = goal.args[goal.arity - 1];
|
|
1508
|
+
const streamTerm = goal.arity === 3 ? deref(goal.args[0], env) : null;
|
|
1509
|
+
|
|
1510
|
+
// ISO 8.14.1.3 error order:
|
|
1511
|
+
// a) variable stream; b) partial/options-variable; c) bad stream-or-alias;
|
|
1512
|
+
// d) non-list options; e) invalid read option; then stream existence/mode.
|
|
1513
|
+
if (streamTerm?.type === VAR) throw new PrologError('instantiation_error');
|
|
1514
|
+
const preflight = preflightOptionList(optionTerm, env);
|
|
1515
|
+
if (goal.arity === 3) streamReference(goal.args[0], env);
|
|
1516
|
+
const options = requireProperOptionList(preflight);
|
|
1517
|
+
validateReadOptions(options);
|
|
1518
|
+
|
|
1519
|
+
const stream = goal.arity === 2
|
|
1520
|
+
? solver.io.resolve(solver.io.currentInput)
|
|
1521
|
+
: requireStream(solver, goal.args[0], env, 'read');
|
|
1311
1522
|
if (stream.type !== 'text') throw new PrologError('permission_error(input, binary_stream)', streamHandle(stream.id));
|
|
1312
|
-
|
|
1523
|
+
|
|
1313
1524
|
const target = goal.args[goal.arity - 2];
|
|
1314
1525
|
const { term, variables } = readTermFromStream(stream, solver);
|
|
1315
1526
|
const next = env.clone();
|
|
1316
1527
|
if (!unify(target, term, next)) return;
|
|
1317
1528
|
for (const option of options) {
|
|
1318
|
-
if (option.type === VAR) throw new PrologError('instantiation_error');
|
|
1319
|
-
if (option.type !== COMPOUND || option.arity !== 1) throw new PrologError('domain_error(read_option)', option);
|
|
1320
1529
|
let value;
|
|
1321
1530
|
if (option.name === 'variables') {
|
|
1322
1531
|
value = listFromItems(variables.map((item) => item.term));
|
|
@@ -1324,12 +1533,10 @@ function* readTermBuiltin({ solver, goal, env }) {
|
|
|
1324
1533
|
value = listFromItems(variables
|
|
1325
1534
|
.filter((item) => !item.anonymous)
|
|
1326
1535
|
.map((item) => compound('=', [atom(item.sourceName), item.term])));
|
|
1327
|
-
} else
|
|
1536
|
+
} else {
|
|
1328
1537
|
value = listFromItems(variables
|
|
1329
1538
|
.filter((item) => !item.anonymous && item.count === 1)
|
|
1330
1539
|
.map((item) => compound('=', [atom(item.sourceName), item.term])));
|
|
1331
|
-
} else {
|
|
1332
|
-
throw new PrologError('domain_error(read_option)', option);
|
|
1333
1540
|
}
|
|
1334
1541
|
if (!unify(option.args[0], value, next)) return;
|
|
1335
1542
|
}
|
|
@@ -1378,23 +1585,26 @@ function writeVariableNames(value, env, option) {
|
|
|
1378
1585
|
return names;
|
|
1379
1586
|
}
|
|
1380
1587
|
|
|
1381
|
-
function
|
|
1588
|
+
function termWriteOptionsFromItems(options, env, mode = 'write_term', solver = null) {
|
|
1382
1589
|
const result = defaultTermWriteOptions(mode);
|
|
1383
|
-
for (const option of
|
|
1384
|
-
if (option.type === VAR) throw new PrologError('instantiation_error');
|
|
1590
|
+
for (const option of options) {
|
|
1385
1591
|
if (option.type !== COMPOUND || option.arity !== 1) {
|
|
1386
1592
|
throw new PrologError('domain_error(write_option)', option);
|
|
1387
1593
|
}
|
|
1388
1594
|
if (option.name === 'quoted') result.quoted = writeOptionBoolean(option.args[0], env, option);
|
|
1389
1595
|
else if (option.name === 'ignore_ops') result.ignoreOps = writeOptionBoolean(option.args[0], env, option);
|
|
1390
1596
|
else if (option.name === 'numbervars') result.numbervars = writeOptionBoolean(option.args[0], env, option);
|
|
1391
|
-
else if (option.name === 'double_quotes') result.doubleQuotes = writeOptionBoolean(option.args[0], env, option);
|
|
1392
1597
|
else if (option.name === 'variable_names') result.variableNames = writeVariableNames(option.args[0], env, option);
|
|
1393
|
-
else
|
|
1598
|
+
else if (option.name === 'double_quotes' && !solver?.isoStrict) {
|
|
1599
|
+
result.doubleQuotes = writeOptionBoolean(option.args[0], env, option);
|
|
1600
|
+
} else {
|
|
1601
|
+
throw new PrologError('domain_error(write_option)', option);
|
|
1602
|
+
}
|
|
1394
1603
|
}
|
|
1395
1604
|
return result;
|
|
1396
1605
|
}
|
|
1397
1606
|
|
|
1607
|
+
|
|
1398
1608
|
function writeBuiltin(mode) {
|
|
1399
1609
|
return function* ({ solver, goal, env }) {
|
|
1400
1610
|
const stream = outputStreamFor(solver, goal, env);
|
|
@@ -1410,9 +1620,22 @@ function writeBuiltin(mode) {
|
|
|
1410
1620
|
};
|
|
1411
1621
|
}
|
|
1412
1622
|
function* writeTermBuiltin({ solver, goal, env }) {
|
|
1413
|
-
const
|
|
1623
|
+
const optionTerm = goal.args[goal.arity - 1];
|
|
1624
|
+
const streamTerm = goal.arity === 3 ? deref(goal.args[0], env) : null;
|
|
1625
|
+
|
|
1626
|
+
// ISO 8.14.2.3 error order:
|
|
1627
|
+
// a) variable stream; b) partial/options-variable; c) non-list options;
|
|
1628
|
+
// d) bad stream-or-alias; e) invalid write option; then stream errors.
|
|
1629
|
+
if (streamTerm?.type === VAR) throw new PrologError('instantiation_error');
|
|
1630
|
+
const preflight = preflightOptionList(optionTerm, env);
|
|
1631
|
+
const optionItems = requireProperOptionList(preflight);
|
|
1632
|
+
if (goal.arity === 3) streamReference(goal.args[0], env);
|
|
1633
|
+
const options = termWriteOptionsFromItems(optionItems, env, 'write_term', solver);
|
|
1634
|
+
|
|
1635
|
+
const stream = goal.arity === 2
|
|
1636
|
+
? solver.io.resolve(solver.io.currentOutput)
|
|
1637
|
+
: requireStream(solver, goal.args[0], env, 'write');
|
|
1414
1638
|
if (stream.type !== 'text') throw new PrologError('permission_error(output, binary_stream)', streamHandle(stream.id));
|
|
1415
|
-
const options = termWriteOptions(goal.args[goal.arity - 1], env);
|
|
1416
1639
|
if (options.doubleQuotes === true) {
|
|
1417
1640
|
options.doubleQuotes = solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars';
|
|
1418
1641
|
} else {
|
|
@@ -1730,14 +1953,14 @@ function canonicalNumberText(value) {
|
|
|
1730
1953
|
return text;
|
|
1731
1954
|
}
|
|
1732
1955
|
|
|
1733
|
-
function numberListText(list, env, kind, valueIsBound) {
|
|
1956
|
+
function numberListText(list, env, kind, valueIsBound, solver = null) {
|
|
1734
1957
|
const whole = deref(list, env);
|
|
1735
1958
|
const { items, tail } = listElements(list, env);
|
|
1736
1959
|
const proper = tail.type === ATOM && tail.name === '[]';
|
|
1737
1960
|
if (tail.type !== VAR && !proper) throw new PrologError('type_error(list)', whole);
|
|
1738
1961
|
|
|
1739
1962
|
const invalid = items.find((item) => item.type !== VAR &&
|
|
1740
|
-
(kind === 'chars' ? !oneChar(item) : !validCharacterCode(item)));
|
|
1963
|
+
(kind === 'chars' ? !oneChar(item) : !validCharacterCode(item, solver)));
|
|
1741
1964
|
if (invalid) {
|
|
1742
1965
|
if (kind === 'chars') throw new PrologError('type_error(character)', invalid);
|
|
1743
1966
|
if (invalid.type !== NUMBER || !isDecimalInteger(invalid.name)) {
|
|
@@ -1757,12 +1980,12 @@ function numberListText(list, env, kind, valueIsBound) {
|
|
|
1757
1980
|
const numberSyntaxError = new PrologError('syntax_error(number)');
|
|
1758
1981
|
|
|
1759
1982
|
function numberListBuiltin(kind) {
|
|
1760
|
-
return function* ({ goal, env }) {
|
|
1983
|
+
return function* ({ solver, goal, env }) {
|
|
1761
1984
|
const value = deref(goal.args[0], env);
|
|
1762
1985
|
if (value.type !== VAR && value.type !== NUMBER) throw new PrologError('type_error(number)', value);
|
|
1763
1986
|
const list = deref(goal.args[1], env);
|
|
1764
1987
|
if (value.type === VAR && list.type === VAR) throw new PrologError('instantiation_error');
|
|
1765
|
-
const text = numberListText(list, env, kind, value.type === NUMBER);
|
|
1988
|
+
const text = numberListText(list, env, kind, value.type === NUMBER, solver);
|
|
1766
1989
|
if (value.type === NUMBER) {
|
|
1767
1990
|
if (text != null) {
|
|
1768
1991
|
const parsed = parseIsoNumber(text);
|