eyeprolog 0.5.13 → 0.5.15

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.
Files changed (124) hide show
  1. package/README.md +2 -0
  2. package/conformance-report.md +2 -2
  3. package/examples/auroracare.pl +26 -0
  4. package/examples/bmi.pl +20 -1
  5. package/examples/cdcl-sat-solver.pl +6 -6
  6. package/examples/composition-of-injective-functions-is-injective.pl +4 -1
  7. package/examples/deep-taxonomy-10.pl +2 -0
  8. package/examples/deep-taxonomy-100.pl +2 -0
  9. package/examples/deep-taxonomy-1000.pl +2 -0
  10. package/examples/deep-taxonomy-10000.pl +2 -0
  11. package/examples/exoplanet-validation-worlds.pl +3 -0
  12. package/examples/family-cousins.pl +2 -0
  13. package/examples/fastpow.pl +6 -6
  14. package/examples/flandor.pl +14 -0
  15. package/examples/gd-step-certified.pl +3 -0
  16. package/examples/group-inverse-uniqueness.pl +9 -9
  17. package/examples/illegitimate-reasoning.pl +6 -0
  18. package/examples/input/odrl-dpv-fpv-trust-flow-rules.pl +3 -9
  19. package/examples/list-collection.pl +6 -6
  20. package/examples/lldm.pl +2 -0
  21. package/examples/odrl-dpv-fpv-trust-flow.pl +3 -9
  22. package/examples/output/aliases-and-namespaces.pl +1 -1
  23. package/examples/output/bmi.pl +9 -0
  24. package/examples/output/dijkstra-findall-sort.pl +1 -1
  25. package/examples/output/dijkstra.pl +16 -16
  26. package/examples/output/drone-corridor-planner.pl +17 -17
  27. package/examples/output/iso-atomic-conversion.pl +1 -1
  28. package/examples/output/list-collection.pl +6 -6
  29. package/examples/pointer-analysis.pl +3 -0
  30. package/examples/proof/aliases-and-namespaces.pl +5 -5
  31. package/examples/proof/composition-of-injective-functions-is-injective.pl +80 -97
  32. package/examples/proof/graph-reachability.pl +22 -22
  33. package/examples/proof/group-inverse-uniqueness.pl +4 -4
  34. package/examples/proof/list-collection.pl +73 -29
  35. package/examples/proof/spacecraft-battery-diagnosis.pl +8 -8
  36. package/examples/proof/term-tools.pl +1 -1
  37. package/examples/relational-cube-lookup.pl +2002 -2000
  38. package/examples/riemann-hypothesis.pl +5 -0
  39. package/examples/spacecraft-battery-diagnosis.pl +8 -8
  40. package/examples/vulnerability-impact.pl +4 -0
  41. package/index.d.ts +5 -2
  42. package/package.json +1 -1
  43. package/src/cli.js +17 -9
  44. package/src/index.js +15 -5
  45. package/src/iso.js +1 -0
  46. package/src/parser.js +57 -12
  47. package/src/program.js +8 -5
  48. package/src/solver.js +11 -3
  49. package/src/term.js +41 -10
  50. package/src/write.js +6 -0
  51. package/test/conformance/README.md +1 -1
  52. package/test/conformance/cases/arithmetic/082_comparison_semantics.pl +2 -2
  53. package/test/conformance/cases/arithmetic/extra_le_equal_string.pl +1 -1
  54. package/test/conformance/cases/context/extra_context_ignores_string_parts.pl +1 -1
  55. package/test/conformance/cases/iso/double_quoted_lists.pl +27 -0
  56. package/test/conformance/cases/strings/023_quoted_escapes_readback.pl +2 -3
  57. package/test/conformance/cases/terms/003_terms_and_readback.pl +1 -1
  58. package/test/conformance/cases/terms/073_term_introspection_edges.pl +1 -1
  59. package/test/conformance/cases/terms/096_functor_scalar_edges.pl +2 -2
  60. package/test/conformance/cases/terms/extra_functor_string_scalar.pl +1 -1
  61. package/test/conformance/cases/terms/functor_number_and_string.pl +1 -1
  62. package/test/conformance/cases/unification/extra_eq_scalar_string_number.pl +1 -1
  63. package/test/conformance/cases/unification/scalar_cross_type_same_lexical_value.pl +2 -2
  64. package/test/conformance/expected/aggregation/044_findall_user_goal.pl +1 -1
  65. package/test/conformance/expected/arithmetic/082_comparison_semantics.pl +1 -1
  66. package/test/conformance/expected/arithmetic/extra_le_equal_string.pl +1 -1
  67. package/test/conformance/expected/context/extra_context_atom_parts.pl +1 -1
  68. package/test/conformance/expected/context/extra_context_ignores_string_parts.pl +1 -0
  69. package/test/conformance/expected/context/extra_context_list_parts.pl +1 -1
  70. package/test/conformance/expected/iso/atomic_term_processing.pl +1 -1
  71. package/test/conformance/expected/iso/double_quoted_lists.pl +8 -0
  72. package/test/conformance/expected/iso/exceptions_and_flags.pl +1 -1
  73. package/test/conformance/expected/iso/grouped_solutions_and_clauses.pl +1 -1
  74. package/test/conformance/expected/iso/logtalk_atom_chars.pl +1 -1
  75. package/test/conformance/expected/iso/logtalk_number_chars.pl +1 -1
  76. package/test/conformance/expected/iso/logtalk_prolog_flags.pl +1 -1
  77. package/test/conformance/expected/iso/logtalk_univ.pl +1 -1
  78. package/test/conformance/expected/iso/scryer_lexical_terms.pl +1 -1
  79. package/test/conformance/expected/lists/005_list_deconstruction.pl +1 -1
  80. package/test/conformance/expected/lists/031_lists_aggregation_ordering.pl +4 -4
  81. package/test/conformance/expected/lists/035_list_relations.pl +3 -3
  82. package/test/conformance/expected/lists/036_append_splits.pl +3 -3
  83. package/test/conformance/expected/lists/045_sort_deduplicates_atoms.pl +1 -1
  84. package/test/conformance/expected/lists/046_append_bound_prefix_suffix.pl +2 -2
  85. package/test/conformance/expected/lists/048_set_nth0_edges.pl +2 -2
  86. package/test/conformance/expected/lists/049_select_duplicate_occurrences.pl +2 -2
  87. package/test/conformance/expected/lists/067_list_edges.pl +1 -1
  88. package/test/conformance/expected/lists/068_list_generation_order.pl +6 -6
  89. package/test/conformance/expected/lists/084_append_and_select_composition.pl +9 -9
  90. package/test/conformance/expected/lists/085_nth_and_update_edges.pl +1 -1
  91. package/test/conformance/expected/lists/087_sort_reverse_length.pl +2 -2
  92. package/test/conformance/expected/lists/extra_append_all_splits_three.pl +4 -4
  93. package/test/conformance/expected/lists/extra_list_to_set_stable_order.pl +1 -1
  94. package/test/conformance/expected/lists/extra_reverse_nested_terms.pl +1 -1
  95. package/test/conformance/expected/lists/extra_slice_end.pl +1 -1
  96. package/test/conformance/expected/lists/extra_take_all.pl +1 -1
  97. package/test/conformance/expected/lists/list_pattern_last.pl +1 -1
  98. package/test/conformance/expected/lists/select_all_occurrences.pl +3 -3
  99. package/test/conformance/expected/lists/set_nth0_middle.pl +1 -1
  100. package/test/conformance/expected/rules/077_recursive_path_with_lists.pl +3 -3
  101. package/test/conformance/expected/strings/023_quoted_escapes_readback.pl +1 -1
  102. package/test/conformance/expected/strings/071_string_edges.pl +1 -1
  103. package/test/conformance/expected/strings/extra_split_empty_separator.pl +1 -1
  104. package/test/conformance/expected/terms/003_terms_and_readback.pl +3 -3
  105. package/test/conformance/expected/terms/065_reusable_term_control_builtins.pl +1 -1
  106. package/test/conformance/expected/terms/073_term_introspection_edges.pl +2 -2
  107. package/test/conformance/expected/terms/095_term_introspection_roundtrip.pl +1 -1
  108. package/test/conformance/expected/terms/096_functor_scalar_edges.pl +1 -1
  109. package/test/conformance/expected/terms/extra_compound_name_arguments_decompose_list.pl +1 -1
  110. package/test/conformance/expected/terms/extra_functor_string_scalar.pl +1 -1
  111. package/test/conformance/expected/terms/functor_number_and_string.pl +1 -1
  112. package/test/conformance/expected/unification/012_nested_compound_unification.pl +1 -1
  113. package/test/conformance/expected/unification/034_equality_and_inequality.pl +1 -1
  114. package/test/conformance/expected/unification/extra_eq_list_tail_binding.pl +1 -1
  115. package/test/conformance/expected/unification/list_tail_binding.pl +1 -1
  116. package/test/conformance/expected-proofs/proofs/bindings_nested_term.pl +5 -5
  117. package/test/conformance/expected-proofs/proofs/extra_compound_name_arguments.pl +4 -4
  118. package/test/conformance/expected-proofs/proofs/extra_findall_builtin.pl +5 -5
  119. package/test/conformance/expected-proofs/proofs/extra_nested_list_binding.pl +5 -5
  120. package/test/conformance/expected-proofs/proofs/extra_once_member.pl +2 -2
  121. package/test/conformance/expected-proofs/proofs/list_builtin.pl +1 -1
  122. package/test/run-playground.mjs +2 -2
  123. package/test/run-regression.mjs +69 -11
  124. package/the-art-of-eyeprolog.md +297 -38
@@ -142,7 +142,7 @@ why(
142
142
  bindings([binding("X", a)]),
143
143
  uses([
144
144
  proof(
145
- goal(member(a, [a])),
145
+ goal(member(a, "a")),
146
146
  by(library(member, 2))
147
147
  )
148
148
  ])
@@ -491,18 +491,22 @@ why(
491
491
  const univPath = path.join(tmp, `read-univ-${++tmpCounter}.term`);
492
492
  const customPath = path.join(tmp, `read-custom-${++tmpCounter}.term`);
493
493
  const invalidPath = path.join(tmp, `read-invalid-${++tmpCounter}.term`);
494
+ const quotesPath = path.join(tmp, `read-quotes-${++tmpCounter}.term`);
494
495
  fs.writeFileSync(univPath, 'foo =.. [bar]/* term end */.\n');
495
496
  fs.writeFileSync(customPath, 'alice likes bob.\n');
496
497
  fs.writeFileSync(invalidPath, 'a..b.\n');
498
+ fs.writeFileSync(quotesPath, '"ab".\n');
497
499
  const source = [
498
500
  `read_univ(T) :- open(${sourceAtom(univPath)}, read, S, []), read(S, T), close(S).`,
499
501
  `read_custom(T) :- op(500, xfx, likes), open(${sourceAtom(customPath)}, read, S, []), read(S, T), close(S).`,
500
502
  `read_invalid(ok) :- open(${sourceAtom(invalidPath)}, read, S, []), catch(read(S, _), error(syntax_error(read_term), _), true), close(S).`,
503
+ `read_codes(ok) :- set_prolog_flag(double_quotes, codes), open(${sourceAtom(quotesPath)}, read, S, []), read(S, [97, 98]), close(S).`,
501
504
  '',
502
505
  ].join('\n');
503
506
  assertEqual(run(source, { goal: 'read_univ(T)' }).stdout, "read_univ('=..'(foo, [bar])).\n", 'univ term');
504
507
  assertEqual(run(source, { goal: 'read_custom(T)' }).stdout, 'read_custom(likes(alice, bob)).\n', 'custom operator term');
505
508
  assertEqual(run(source, { goal: 'read_invalid(ok)' }).stdout, 'read_invalid(ok).\n', 'invalid dotted term');
509
+ assertEqual(run(source, { goal: 'read_codes(ok)' }).stdout, 'read_codes(ok).\n', 'double_quotes read flag');
506
510
  },
507
511
  },
508
512
  {
@@ -779,7 +783,7 @@ function apiCases() {
779
783
  ' assertz(edge(b, c)),',
780
784
  ' findall(Y, path(a, Y), After).',
781
785
  ].join('\n'));
782
- assertEqual(result.stdout, 'test([b], [b, c]).\n', 'stdout');
786
+ assertEqual(result.stdout, 'test("b", "bc").\n', 'stdout');
783
787
  },
784
788
  },
785
789
  {
@@ -959,7 +963,7 @@ open(X) :- candidate(X), \\+ closed(X).
959
963
  name: 'run loads the EyeProlog library by default',
960
964
  run: () => {
961
965
  const result = run('answer(X) :- append([a], [b], X).', { goal: 'answer(X)' });
962
- assertEqual(result.stdout, 'answer([a, b]).\n', 'stdout');
966
+ assertEqual(result.stdout, 'answer("ab").\n', 'stdout');
963
967
  },
964
968
  },
965
969
  {
@@ -969,7 +973,7 @@ open(X) :- candidate(X), \\+ closed(X).
969
973
  const solver = new Solver(program);
970
974
  const goal = parseGoalText('answer(X)');
971
975
  const answers = [...solver.solve([goal], new Env(), 0)].map((env) => termToString(goal, env, true));
972
- assertEqual(answers.join('\n'), 'answer([a, b])', 'answers');
976
+ assertEqual(answers.join('\n'), 'answer("ab")', 'answers');
973
977
  },
974
978
  },
975
979
  {
@@ -1051,7 +1055,7 @@ open(X) :- candidate(X), \\+ closed(X).
1051
1055
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'eyeprolog-common-library.pl')), true, 'pure-Prolog common library exists');
1052
1056
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'library-source.js')), false, 'duplicate source loader is absent');
1053
1057
  assertEqual(fs.existsSync(path.join(packageRoot, 'src', 'portable-library.js')), false, 'obsolete duplicate module remains absent');
1054
- assertEqual(run(program, { goal: 'answer(X)' }).stdout, 'answer([a, b]).\n', 'autoloaded append execution');
1058
+ assertEqual(run(program, { goal: 'answer(X)' }).stdout, 'answer("ab").\n', 'autoloaded append execution');
1055
1059
  assertEqual(Boolean(program.findGroup('random', 3)), true, 'random/3 is autoloaded as a clause group');
1056
1060
  },
1057
1061
  },
@@ -1067,7 +1071,7 @@ portable_check(A, B, C) :- lowercase('HELLO', A), replace('banana', 'na', 'NA',
1067
1071
  const solver = new Solver(program, { registry: createDefaultRegistry() });
1068
1072
  const goal = parseGoalText('portable_check(A, B, C)');
1069
1073
  const answers = [...solver.solve([goal], new Env(), 0)].map((env) => termToString(goal, env, true));
1070
- assertEqual(answers.join('\n'), "portable_check(hello, baNANA, [x, y])", 'ISO-only portable execution');
1074
+ assertEqual(answers.join('\n'), 'portable_check(hello, baNANA, "xy")', 'ISO-only portable execution');
1071
1075
  assertEqual(Boolean(program.findGroup('uuid', 3)), true, 'uuid/3 is implemented in the portable file');
1072
1076
  assertEqual(program.findGroup('uuid', 1), null, 'obsolete uuid/1 is absent');
1073
1077
  assertEqual(program.findGroup('local_time', 1), null, 'local_time/1 is absent from the library');
@@ -1085,9 +1089,9 @@ portable_check(A, B, C) :- lowercase('HELLO', A), replace('banana', 'na', 'NA',
1085
1089
  '',
1086
1090
  ].join('\n'));
1087
1091
  assertEqual(result.stdout, [
1088
- 'answer([], [a, b], 5, 9007199254740993).',
1089
- 'answer([a], [b], 5, 9007199254740993).',
1090
- 'answer([a, b], [], 5, 9007199254740993).',
1092
+ 'answer([], "ab", 5, 9007199254740993).',
1093
+ 'answer("a", "b", 5, 9007199254740993).',
1094
+ 'answer("ab", [], 5, 9007199254740993).',
1091
1095
  '',
1092
1096
  ].join('\n'), 'EyeProlog library behavior');
1093
1097
  },
@@ -1197,14 +1201,68 @@ function whiteBoxCases() {
1197
1201
  name: 'parser preserves list syntax readback',
1198
1202
  run: () => {
1199
1203
  const goal = parseGoalText('member(X, [a, b])');
1200
- assertEqual(termToString(goal, new Env(), true), 'member(X, [a, b])', 'goal');
1204
+ assertEqual(termToString(goal, new Env(), true), 'member(X, "ab")', 'goal');
1205
+ },
1206
+ },
1207
+ {
1208
+ name: 'double-quoted lists honor every ISO double_quotes value',
1209
+ run: () => {
1210
+ const chars = parseGoalText('p("aλ")').args[0];
1211
+ const charItems = properListItems(chars, new Env());
1212
+ assertEqual(charItems.map((item) => `${item.type}:${item.name}`).join('|'), 'atom:a|atom:λ', 'chars');
1213
+
1214
+ const codes = parseGoalText('p("aλ")', { doubleQuotes: 'codes' }).args[0];
1215
+ const codeItems = properListItems(codes, new Env());
1216
+ assertEqual(codeItems.map((item) => `${item.type}:${item.name}`).join('|'), 'number:97|number:955', 'codes');
1217
+
1218
+ const quotedAtom = parseGoalText('p("aλ")', { doubleQuotes: 'atom' }).args[0];
1219
+ assertEqual(`${quotedAtom.type}:${quotedAtom.name}`, 'atom:aλ', 'atom');
1220
+ },
1221
+ },
1222
+ {
1223
+ name: 'double_quotes directives affect subsequent source text',
1224
+ run: () => {
1225
+ const clauses = parseProgramText([
1226
+ 'chars("a").',
1227
+ ':- set_prolog_flag(double_quotes, codes).',
1228
+ 'codes("a").',
1229
+ ':- set_prolog_flag(double_quotes, atom).',
1230
+ 'quoted_atom("a").',
1231
+ '',
1232
+ ].join('\n'), { sourceMetadata: false });
1233
+ const facts = clauses.filter((clause) => clause.head.name !== ':-');
1234
+ assertEqual(termToString(facts[0].head), 'chars("a")', 'chars fact');
1235
+ assertEqual(termToString(facts[1].head, new Env(), true, { doubleQuotes: 'codes' }), 'codes("a")', 'codes fact');
1236
+ assertEqual(termToString(facts[2].head), 'quoted_atom(a)', 'atom fact');
1237
+ },
1238
+ },
1239
+ {
1240
+ name: 'double_quotes parser state flows across source files',
1241
+ run: () => {
1242
+ const program = Program.parseSources([
1243
+ ':- set_prolog_flag(double_quotes, codes).',
1244
+ 'value("A").',
1245
+ ], { sourceMetadata: false });
1246
+ const value = program.findGroup('value', 1).clauses[0].head.args[0];
1247
+ assertEqual(properListItems(value, new Env())[0].name, '65', 'code in second source');
1248
+ assertEqual(program.doubleQuotes, 'codes', 'final parser flag');
1249
+ },
1250
+ },
1251
+ {
1252
+ name: 'parser double_quotes option flows into solver flags',
1253
+ run: () => {
1254
+ const result = run('answer(atom) :- atom("text").', {
1255
+ goal: 'answer(X)',
1256
+ doubleQuotes: 'atom',
1257
+ });
1258
+ assertEqual(result.stdout, 'answer(atom).\n', 'atom-mode execution');
1201
1259
  },
1202
1260
  },
1203
1261
  {
1204
1262
  name: 'parser accepts ISO-style uppercase variables',
1205
1263
  run: () => {
1206
1264
  const goal = parseGoalText('member(X, [a, b])');
1207
- assertEqual(termToString(goal, new Env(), true), 'member(X, [a, b])', 'goal');
1265
+ assertEqual(termToString(goal, new Env(), true), 'member(X, "ab")', 'goal');
1208
1266
  },
1209
1267
  },
1210
1268
  {
@@ -447,7 +447,8 @@ grandparents, then only the grandparents of `diego`.
447
447
  Prolog programs accepted by EyeProlog are built from terms:
448
448
 
449
449
  - atom constants: `ada`, `accepted`, `'atom with spaces'`;
450
- - strings: `"sensor too hot"`;
450
+ - double-quoted character lists: `"sensor too hot"` (the default shorthand for
451
+ `[s,e,n,s,o,r,' ',t,o,o,' ',h,o,t]`);
451
452
  - numbers: `42`, `-7`, `3.14159`, `1.2e3`;
452
453
  - variables: `X`, `Person`, `_temporary`;
453
454
  - compound terms: `point(3, 4)`, `reading(temp, 91)`;
@@ -504,8 +505,9 @@ route(a, d, path([a, b, d], cost(9))).
504
505
  As a fact head, `measurement(...)` is an atomic formula. Nested terms are data.
505
506
  The same surface form serves both roles; context decides which.
506
507
 
507
- `ready` is an atom constant and `"ready"` is a string. Keep symbolic vocabulary
508
- as atoms and human text as strings. Quoted atoms remain atoms:
508
+ `ready` is an atom constant and `"ready"` is, by default, a proper list of
509
+ one-character atoms. Keep symbolic vocabulary as atoms and use character lists
510
+ when text must be inspected relationally. Quoted atoms remain atoms:
509
511
 
510
512
  ```eyeprolog
511
513
  label(sensor_1, "Cabin temperature").
@@ -632,7 +634,7 @@ book rather than appearing only as historical attribution.
632
634
  The declarative reading needs a precise answer to a deceptively simple
633
635
  question: what can a term denote? EyeProlog uses **Herbrand semantics**. Its
634
636
  universe contains exactly the ground terms that can be constructed from the
635
- program's atom constants, strings, numbers, list constructors, and compound
637
+ program's atom constants, numbers, list constructors, and compound
636
638
  functors. There are no unnamed elements hiding behind the notation. A ground
637
639
  term denotes itself.
638
640
 
@@ -1179,10 +1181,9 @@ normalized(Input, Words) :-
1179
1181
 
1180
1182
  Conversions include `number_string/2`, `atom_string/2`, and `term_string/2`.
1181
1183
  Pattern operations include `contains/2`, `matches/2`, and named-capture
1182
- `matches/3`. Turn text into structured terms early; keep central rules
1183
- relational. Double-quoted EyeProlog strings remain available as a distinct term
1184
- type for data models such as RDF, but they are not the portable library text
1185
- API.
1184
+ `matches/3`. Turn text into structured terms early and keep central rules
1185
+ relational. Double-quoted source notation follows the ISO `double_quotes` flag;
1186
+ it does not create a separate Prolog string type.
1186
1187
 
1187
1188
  Parenthesized comma terms can serve as context data:
1188
1189
 
@@ -4868,9 +4869,9 @@ Atomic conversion predicates expose reversible representations:
4868
4869
  - `char_code/2` converts one character; and
4869
4870
  - `number_chars/2` and `number_codes/2` parse or render ISO numbers.
4870
4871
 
4871
- These are atom relations, not the EyeProlog library's string convenience
4872
- predicates. Quoted atoms such as `'λ'` remain atoms; double-quoted values remain
4873
- EyeProlog strings.
4872
+ These are atom relations, distinct from the EyeProlog library predicates whose
4873
+ historical names contain `string`. Quoted atoms such as `'λ'` remain atoms;
4874
+ with the default flag, `"λ"` denotes the character list `['λ']`.
4874
4875
 
4875
4876
  [`iso-reflective-terms.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-reflective-terms.pl)
4876
4877
  walks through shape, rebuilding, fresh copying, variables, and order.
@@ -5043,23 +5044,23 @@ conforming processor.
5043
5044
  Prolog source accepted by EyeProlog is UTF-8. `%` starts a line comment and
5044
5045
  `/* ... */` delimits a block comment. Plain atoms begin with a
5045
5046
  lowercase ASCII letter. Variables begin with uppercase or underscore. The bare
5046
- `_` is fresh each time. Single quotes delimit quoted atoms; double quotes
5047
- delimit strings. Integers, decimals, scientific notation, binary/octal/
5047
+ `_` is fresh each time. Single quotes delimit quoted atoms; double quotes use
5048
+ ISO double-quoted-list notation. Integers, decimals, scientific notation, binary/octal/
5048
5049
  hexadecimal integers, and character-code constants are accepted.
5049
5050
 
5050
5051
  Unquoted names deliberately use ASCII spelling. Unicode belongs inside quoted
5051
- atoms and strings:
5052
+ atoms and double-quoted lists:
5052
5053
 
5053
5054
  ```eyeprolog
5054
5055
  city('München').
5055
5056
  message("café").
5056
5057
  ```
5057
5058
 
5058
- Inside a quoted atom, a single quote is doubled: `'don''t'`. Strings support
5059
+ Inside a quoted atom, a single quote is doubled: `'don''t'`. Double-quoted lists support
5059
5060
  the common escapes `\n`, `\t`, `\"`, and `\\`. Whitespace is insignificant
5060
5061
  between tokens, and a `%` comment continues to the end of its line. Doubling
5061
5062
  the active delimiter is also accepted inside either quoted form, so `""`
5062
- inside a string denotes one literal double quote.
5063
+ inside double-quoted notation denotes one literal double quote character.
5063
5064
 
5064
5065
  Graphic atoms may contain `#$&*+-/<=>@^~\;`. Colon names and unquoted
5065
5066
  angle-bracket IRIs are not syntax; quote names containing such punctuation.
@@ -5074,11 +5075,12 @@ clause ::= head "."
5074
5075
  | head ":-" goal-list "."
5075
5076
  head ::= term
5076
5077
  goal-list ::= term { "," term }
5077
- term ::= variable | atom-constant | string | number
5078
+ term ::= variable | atom-constant | double-quoted-list | number
5078
5079
  | compound | list | curly-term | parenthesized-term
5079
5080
  compound ::= atom-constant "(" term { "," term } ")"
5080
5081
  list ::= "[" "]"
5081
5082
  | "[" term { "," term } [ "|" term ] "]"
5083
+ double-quoted-list ::= '"' { quoted-character } '"'
5082
5084
  curly-term ::= "{}" | "{" term "}"
5083
5085
  parenthesized-term ::= "(" term [ "," term { "," term } ] ")"
5084
5086
  variable ::= "_"
@@ -5221,19 +5223,257 @@ contains 115 name/arity entries across 94 names.
5221
5223
  `false/0` and `fail/0` both fail as goals. `false/0` is a protected static
5222
5224
  procedure and cannot be defined by source clauses or declared dynamic.
5223
5225
 
5226
+ ### Reading the built-in reference
5227
+
5228
+ The call patterns below use `+` for an argument that must be sufficiently
5229
+ instantiated, `-` for a result normally produced by the call, and `?` for an
5230
+ argument that may be supplied or returned. These are principal operational
5231
+ modes, not a separate mode system enforced by the parser. A call described as
5232
+ *semidet* succeeds at most once; a *nondet* call may yield further answers on
5233
+ backtracking. Unless stated otherwise, checking a result that does not unify
5234
+ simply fails.
5235
+
5236
+ Built-in dispatch is authoritative and built-in procedures cannot be modified
5237
+ through the dynamic database predicates. Source clauses with the same indicator
5238
+ do not replace a built-in implementation. `clause/2` and `op/3` are the two
5239
+ dispatch exceptions: when a program defines a source predicate with that same
5240
+ indicator, EyeProlog uses the source clauses. `false/0` is stricter still and is
5241
+ rejected as a source-clause head. Portable programs should avoid every such
5242
+ collision because other Prolog systems commonly reject it while loading.
5243
+
5244
+ ### Control, search, and exceptions
5245
+
5246
+ | Predicate and principal call | Behavior |
5247
+ | --- | --- |
5248
+ | `true` | Succeeds once without binding variables. |
5249
+ | `fail`, `false` | Always fail. `false/0` is provided as a compatibility alias and is also forbidden as a source-clause head. |
5250
+ | `!` | Commits to choices made since entry into the current predicate invocation. It does not erase alternatives belonging to an enclosing caller. |
5251
+ | `call(+Goal)` | Calls an atom or compound goal. An unbound argument raises *instantiation_error*; another non-callable term raises *type_error(callable)*. |
5252
+ | `\+(+Goal)` | Negation as finite failure. It succeeds once when `Goal` has no solution and never exports bindings made while testing `Goal`. Bind variables needed by the test first. |
5253
+ | `once(+Goal)` | Returns only the first solution of `Goal`, or fails when there is none. |
5254
+ | `repeat` | Produces an unbounded sequence of successes; normally paired with a test, cut, exception, or `halt/0`. |
5255
+ | `Left ; Right` | Enumerates `Left`, then `Right`, restoring the incoming environment between branches. |
5256
+ | `If -> Then` | Commits to the first solution of `If` and runs `Then`; it does not provide an else branch by itself. |
5257
+ | `(If -> Then ; Else)` | Runs `Then` from the first solution of `If`, otherwise runs `Else`. Alternatives of `If` are discarded. |
5258
+ | `catch(+Goal,?Catcher,+Recovery)` | Runs `Goal`; on a matching thrown ball or `PrologError`, unifies it with `Catcher` and calls `Recovery`. Runtime errors are exposed as *error(Formal,eyeprolog)*. |
5259
+ | `throw(+Ball)` | Throws a copied nonvariable term. An unbound ball raises `instantiation_error`. |
5260
+ | `halt`, `halt(+Status)` | Stops the processor with status `0` or the supplied integer. The JavaScript API reports the status without terminating its host process. |
5261
+
5262
+ `;/2` recognizes an `->/2` term on its left and implements the ISO
5263
+ if-then-else commitment described above. Cuts and committed conditions are
5264
+ operational controls; use ordinary relations when all alternatives should
5265
+ remain observable.
5266
+
5267
+ ### Unification, type tests, and term order
5268
+
5269
+ | Predicate and principal call | Behavior |
5270
+ | --- | --- |
5271
+ | `?Left = ?Right` | Unifies two terms and returns the resulting bindings. EyeProlog rejects direct and indirect cyclic bindings. |
5272
+ | `unify_with_occurs_check(?Left,?Right)` | Performs occurs-check-safe unification. Because ordinary EyeProlog unification is already cycle-safe, it has the same successful bindings as `=/2`. |
5273
+ | `?Left \= ?Right` | Succeeds only when the terms cannot unify at call time. It is a test, not a delayed disequality constraint. |
5274
+ | `?Left == ?Right`, `?Left \== ?Right` | Test term identity or non-identity without binding variables. Two distinct unbound variables are not identical. |
5275
+ | `var(?Term)`, `nonvar(?Term)` | Test whether the dereferenced term is or is not an unbound variable. |
5276
+ | `atom(?Term)`, `integer(?Term)`, `float(?Term)`, `number(?Term)` | Test the corresponding scalar category. Integer values retain arbitrary precision; finite noninteger numeric values are floats. |
5277
+ | `atomic(?Term)`, `compound(?Term)`, `callable(?Term)`, `ground(?Term)` | Test for an ISO atomic term, a compound, a callable atom/compound, or a term containing no unbound variables. A default double-quoted value is a list and is therefore compound unless it is empty. |
5278
+ | `compare(?Order,+Left,+Right)` | Unifies `Order` with `<`, `=`, or `>` according to profile term order. A supplied order must be one of those atoms. |
5279
+ | `Left @< Right`, `Left @=< Right`, `Left @> Right`, `Left @>= Right` | Compare terms without arithmetic evaluation or bindings. These calls are semidet. |
5280
+
5281
+ For ISO terms, the profile term order is variables, numbers, atoms, then compounds;
5282
+ compound terms compare by arity, functor, and arguments. Within the numeric
5283
+ category, floats precede integers; floats compare by finite numeric value and
5284
+ integers compare exactly. This is intentionally documented as EyeProlog's
5285
+ profile order rather than claimed as the complete ISO term order. Host-created
5286
+ RDF lexical strings are an internal extension ranked between atoms and
5287
+ compounds; double-quoted Prolog source never creates that extension.
5288
+
5289
+ ### Term construction and inspection
5290
+
5291
+ | Predicate and principal call | Behavior |
5292
+ | --- | --- |
5293
+ | `functor(+Term,?Name,?Arity)` | Decomposes a term. Scalars have arity zero. |
5294
+ | `functor(-Term,+Name,+Arity)` | Constructs a scalar when `Arity` is zero or a compound with fresh arguments otherwise. Arity must be a nonnegative representable integer and a positive-arity name must be an atom. |
5295
+ | `arg(+Index,+Term,?Argument)` | Selects the one-based argument of a compound. Index zero or an index beyond the arity fails; a negative index is a domain error. |
5296
+ | `?Term =.. ?List` | Converts a term to `[Functor\|Arguments]` or constructs a term from a nonempty proper list. A one-item list constructs its atomic item. |
5297
+ | `copy_term(+Term,-Copy)` | Copies the dereferenced term while replacing every distinct unbound variable with a fresh variable and preserving variable sharing. |
5298
+ | `term_variables(+Term,?Variables)` | Returns distinct variables in first-occurrence traversal order. A supplied result may be a proper or partial list. |
5299
+
5300
+ Construction calls raise `instantiation_error` when neither side supplies the
5301
+ required shape. `=../2` distinguishes an incomplete list
5302
+ (`instantiation_error`) from an improper list (`type_error(list)`).
5303
+
5304
+ ### Solution collection
5305
+
5306
+ | Predicate and principal call | Behavior |
5307
+ | --- | --- |
5308
+ | `findall(+Template,+Goal,?Bag)` | Collects a fresh copy of `Template` for every solution of `Goal`, preserving solution order. It succeeds with `[]` when there are no solutions and treats all free variables existentially. |
5309
+ | `bagof(+Template,+Goal,?Bag)` | Groups answers by free variables not present in `Template`. It yields one nonempty bag per witness group and fails when no group exists. Prefix variables with `^` in `Goal` to quantify them existentially. |
5310
+ | `setof(+Template,+Goal,?Set)` | Has the grouping behavior of `bagof/3`, then sorts each group by profile term order and removes identical duplicates. |
5311
+
5312
+ Each collector runs its goal in an isolated inner search while sharing the
5313
+ current logical program and stream state. Collected terms are copied, so local
5314
+ variables do not escape accidentally. The bag argument must be a proper or
5315
+ partial list.
5316
+
5317
+ ### Dynamic database and procedure information
5318
+
5319
+ | Predicate and principal call | Behavior |
5320
+ | --- | --- |
5321
+ | `clause(+Head,?Body)` | Enumerates fresh copies of source clauses matching the callable `Head`; facts have body `true`. Access to built-ins raises *permission_error(access,private_procedure)*. |
5322
+ | `asserta(+Clause)`, `assertz(+Clause)` | Insert a copied fact or rule at the beginning or end of a predicate declared *dynamic/1*. Static and built-in procedures cannot be modified. |
5323
+ | `retract(+Clause)` | Removes matching dynamic clauses one at a time on backtracking. A call sees the logical update view captured when it began. A fact pattern matches facts only. |
5324
+ | `abolish(+Name/+Arity)` | Removes a dynamic procedure and its clauses. The indicator must contain an atom and a nonnegative representable integer. |
5325
+ | `current_predicate(?Name/?Arity)` | Enumerates predicate groups present in the loaded program, including empty dynamic groups. It does not enumerate registry-only built-ins. |
5326
+
5327
+ Declare mutable predicates explicitly, including empty ones:
5328
+
5329
+ ```text
5330
+ :- dynamic(cache/2).
5331
+
5332
+ remember(Key, Value) :- retract(cache(Key, _)), !, assertz(cache(Key, Value)).
5333
+ remember(Key, Value) :- assertz(cache(Key, Value)).
5334
+ ```
5335
+
5336
+ Assertions and retractions invalidate affected reasoning tables. Mutating a
5337
+ predicate that was not declared dynamic raises a permission error rather than
5338
+ silently changing a static program.
5339
+
5340
+ ### Operators, character conversion, and flags
5341
+
5342
+ | Predicate and principal call | Behavior |
5343
+ | --- | --- |
5344
+ | `op(+Priority,+Specifier,+NameOrNames)` | Defines or removes operators in the current program. Priority is `0..1200`; specifiers are `fx`, `fy`, `xf`, `yf`, `xfx`, `xfy`, or `yfx`; names may be one atom or a proper list. Priority zero removes the definition. `,` and `\|` cannot be modified. |
5345
+ | `current_op(?Priority,?Specifier,?Name)` | Enumerates active operator definitions and filters supplied arguments. |
5346
+ | `char_conversion(+Input,+Output)` | Installs a one-character atom conversion for subsequent term input. Mapping a character to itself removes its custom mapping. |
5347
+ | `current_char_conversion(?Input,?Output)` | Enumerates installed nonidentity conversions. |
5348
+ | `current_prolog_flag(?Flag,?Value)` | Enumerates flags or retrieves one named flag. An unknown bound flag raises *domain_error(prolog_flag)*. |
5349
+ | `set_prolog_flag(+Flag,+Value)` | Changes a supported mutable flag after validating its allowed atom value. Read-only flags raise a permission error. |
5350
+
5351
+ | Flag | Default in normal EyeProlog | Allowed values | Mutable |
5352
+ | --- | --- | --- | --- |
5353
+ | `bounded` | `false` | `false` | no |
5354
+ | `integer_rounding_function` | `toward_zero` | `toward_zero` | no |
5355
+ | `char_conversion` | `on` | `on`, `off` | yes |
5356
+ | `debug` | `off` | `on`, `off` | yes |
5357
+ | `max_arity` | `unbounded` | `unbounded` | no |
5358
+ | `unknown` | `fail` | `error`, `fail`, `warning` | yes |
5359
+ | `double_quotes` | `chars` | `chars`, `codes`, `atom` | yes |
5360
+
5361
+ The isolated ISO-only registry defaults `unknown` to `error`; the normal
5362
+ autoloaded EyeProlog environment defaults it to `fail`. Operator and flag
5363
+ directives are processed per program rather than changing global JavaScript
5364
+ state. The `double_quotes` setting affects subsequent source text, included
5365
+ files, command-line and API goal text, and terms read by `read_term/*`:
5366
+
5367
+ ```text
5368
+ % Default: a list of one-character atoms.
5369
+ chars("ab"). % chars([a,b])
5370
+
5371
+ :- set_prolog_flag(double_quotes, codes).
5372
+ codes("ab"). % codes([97,98])
5373
+
5374
+ :- set_prolog_flag(double_quotes, atom).
5375
+ quoted_atom("ab"). % quoted_atom(ab)
5376
+ ```
5377
+
5378
+ ### Atomic-term operations and conversions
5379
+
5380
+ | Predicate and principal call | Behavior |
5381
+ | --- | --- |
5382
+ | `atom_length(+Atom,?Length)` | Counts Unicode code points, not UTF-16 code units. A supplied length must be a nonnegative integer. |
5383
+ | `atom_concat(?Prefix,?Suffix,?Whole)` | Concatenates two atoms, removes a supplied prefix or suffix, or enumerates every split when only `Whole` is bound. At least `Whole`, or both parts, must determine the operation. |
5384
+ | `sub_atom(+Atom,?Before,?Length,?After,?SubAtom)` | Enumerates substrings and their Unicode-code-point offsets. Supplied counts must be nonnegative integers. |
5385
+ | `atom_chars(?Atom,?Chars)`, `atom_codes(?Atom,?Codes)` | Convert between an atom and a proper list of one-character atoms or Unicode scalar codes. At least one side must be instantiated. |
5386
+ | `char_code(?Character,?Code)` | Converts one character atom and one Unicode scalar code. Surrogates and values outside `0..0x10ffff` raise a representation error. |
5387
+ | `number_chars(?Number,?Chars)`, `number_codes(?Number,?Codes)` | Convert finite numbers to canonical text or parse a proper character/code list. At least one side must be instantiated; malformed numeric input raises *syntax_error(number)*. |
5388
+
5389
+ Conversions accept partial output lists when the atomic input is known, but
5390
+ constructing an atom or number requires a complete proper list with no unbound
5391
+ elements. Numeric parsing accepts leading ISO layout characters, an optional
5392
+ sign, decimal fractions, and decimal exponents; it rejects trailing material
5393
+ and non-finite values.
5394
+
5395
+ ### Streams and unit I/O
5396
+
5397
+ Stream arguments accept an alias atom or the opaque handle returned by
5398
+ `open/3` or `open/4`. Omitting a stream argument selects the current standard
5399
+ input or output.
5400
+
5401
+ | Predicate and principal call | Behavior |
5402
+ | --- | --- |
5403
+ | `open(+Source,+Mode,-Stream)`, `open(+Source,+Mode,-Stream,+Options)` | Opens an atom path in `read`, `write`, or `append` mode. Options are *type(text or binary)*, *alias(Atom)*, *reposition(true or false)*, and *eof_action(error, eof_code, or reset)*. |
5404
+ | `close(+Stream)`, `close(+Stream,+Options)` | Closes a nonstandard stream. The only close option is *force(true or false)*; standard streams remain available. |
5405
+ | `current_input(?Stream)`, `current_output(?Stream)` | Return or test the current input or output handle. |
5406
+ | `set_input(+Stream)`, `set_output(+Stream)` | Select an existing stream with the required direction. |
5407
+ | `flush_output`, `flush_output(+Stream)` | Completes successfully for the current output or validates and flushes the selected output stream. EyeProlog writes synchronously. |
5408
+ | `stream_property(?Stream,?Property)` | Enumerates streams and their properties: *mode/1*, *type/1*, *reposition/1*, *eof_action/1*, *position/1*, *input*, *output*, *end_of_stream/1*, and optional *alias/1* and *file_name/1*. |
5409
+ | `set_stream_position(+Stream,+Position)` | Repositions a stream opened with *reposition(true)*. `Position` is a nonnegative integer or *position(Integer)* within the stream content. |
5410
+ | `at_end_of_stream`, `at_end_of_stream(+Stream)` | Succeeds when the current or selected input position is at or beyond its content. |
5411
+ | `get_char(?Character)`, `get_char(+Stream,?Character)` | Reads one text character; end of input is `end_of_file`. |
5412
+ | `peek_char(?Character)`, `peek_char(+Stream,?Character)` | Observes the next text character without advancing. |
5413
+ | `get_code(?Code)`, `get_code(+Stream,?Code)` | Reads a Unicode code point; end of input is `-1`. |
5414
+ | `peek_code(?Code)`, `peek_code(+Stream,?Code)` | Observes the next Unicode code point without advancing. |
5415
+ | `get_byte(?Byte)`, `get_byte(+Stream,?Byte)` | Reads one unit from a binary stream; end of input is `-1`. |
5416
+ | `peek_byte(?Byte)`, `peek_byte(+Stream,?Byte)` | Observes the next binary unit without advancing. |
5417
+ | `put_char(+Character)`, `put_char(+Stream,+Character)` | Writes one character atom to a text stream. |
5418
+ | `put_code(+Code)`, `put_code(+Stream,+Code)` | Writes one Unicode scalar code to a text stream. |
5419
+ | `put_byte(+Byte)`, `put_byte(+Stream,+Byte)` | Writes an integer in `0..255` to a binary stream. |
5420
+ | `nl`, `nl(+Stream)` | Writes a newline to a text stream. |
5421
+
5422
+ Text operations on binary streams and byte operations on text streams raise
5423
+ permission errors. After EOF, `eof_action(error)` rejects another consuming
5424
+ read, `eof_code` continues returning the EOF value, and `reset` resumes from
5425
+ the beginning. A peek does not mark the stream as past-end.
5426
+
5427
+ ### Term input and output
5428
+
5429
+ | Predicate and principal call | Behavior |
5430
+ | --- | --- |
5431
+ | `read(?Term)`, `read(+Stream,?Term)` | Reads one full-stop-terminated Prolog term from a text stream. Returns `end_of_file` when no term remains. |
5432
+ | `read_term(?Term,+Options)`, `read_term(+Stream,?Term,+Options)` | Reads a term and supports *variables(List)*, *variable_names(Pairs)*, and *singletons(Pairs)*. Unknown options raise *domain_error(read_option)*. |
5433
+ | `write(+Term)`, `write(+Stream,+Term)` | Writes readable operator notation without quoting atoms merely because quoting would be required for reparsing. Number variables are enabled. |
5434
+ | `writeq(+Term)`, `writeq(+Stream,+Term)` | Like `write`, but quotes atoms when required for unambiguous input syntax. |
5435
+ | `write_canonical(+Term)`, `write_canonical(+Stream,+Term)` | Writes quoted canonical functor notation while ignoring operators and without interpreting *$VAR/1*. |
5436
+ | `write_term(+Term,+Options)`, `write_term(+Stream,+Term,+Options)` | Writes with *quoted(true or false)*, *ignore_ops(true or false)*, *numbervars(true or false)*, and *variable_names([Name=Variable,...])*. |
5437
+
5438
+ Term input uses the program's current operator table and applies installed
5439
+ character conversions outside quoted text when the `char_conversion` flag is
5440
+ `on`. `variable_names/1` and `singletons/1` omit anonymous variables. Output
5441
+ predicates do not append a period or newline; call `write/1`, then `write('.')`
5442
+ and `nl/0` when emitting a complete source term manually.
5443
+
5224
5444
  ### Arithmetic expressions
5225
5445
 
5226
- `Result is Expression` evaluates an ISO arithmetic expression and unifies the
5227
- numeric result with `Result`. Supported expressions include integer and
5228
- floating-point literals, unary `+` and `-`, `+`, `-`, `*`, `/`, `//`, `div`,
5229
- `mod`, `rem`, bit operations, exponentiation, `abs`, rounding functions,
5230
- `sin`, `cos`, `atan`, `exp`, `log`, and `sqrt`.
5446
+ | Predicate and principal call | Behavior |
5447
+ | --- | --- |
5448
+ | `is(?Result,+Expression)` | Evaluates `Expression` once and unifies its numeric value with `Result`. It is evaluation plus unification, not mutable assignment. |
5449
+ | `Left =:= Right`, `Left =\= Right` | Evaluate both sides and test numeric equality or inequality. Integer/float representation differences do not by themselves make values unequal. |
5450
+ | `Left < Right`, `Left =< Right`, `Left > Right`, `Left >= Right` | Evaluate both sides and perform the indicated numeric comparison. |
5451
+
5452
+ Every variable in an arithmetic expression must already be bound to a number
5453
+ or evaluable expression. Integers use arbitrary-precision arithmetic while an
5454
+ operation remains in the integer domain; operations requiring floating point
5455
+ convert their operands to finite JavaScript numbers.
5456
+
5457
+ | Expression family | Supported forms and result rules |
5458
+ | --- | --- |
5459
+ | Literals and constants | Integer and finite floating-point literals; `pi` and `e` produce floating-point constants. |
5460
+ | Unary arithmetic | Unary `+`, unary `-`, and integer bitwise complement `\`. |
5461
+ | Basic binary arithmetic | `+`, `-`, and `*` preserve integers when both operands are integers. `/` produces a float and rejects a zero divisor. |
5462
+ | Exponentiation | `Base ^ Exponent` remains an integer for nonnegative integer operands; otherwise `^` and `**` use floating-point exponentiation. |
5463
+ | Integer division | `//` and `div` require integers, reject zero divisors, and round toward zero as reported by `integer_rounding_function`. |
5464
+ | Integer remainder | `rem` is the truncating remainder; `mod` normalizes the result with the divisor's sign. Both require integers and a nonzero divisor. |
5465
+ | Bit operations | Integer `\/\`, `\\/`, `<<`, and `>>`, plus unary `\`. |
5466
+ | Numeric normalization | `abs`, `sign`, and `float`. Integer `abs` and `sign` preserve integer results; `float` produces a float. |
5467
+ | Rounding | `truncate`, `round`, `ceiling`, and `floor` produce integers. |
5468
+ | Float decomposition | `float_integer_part` and `float_fractional_part` require a float and return floats. |
5469
+ | Transcendental functions | `sin`, `cos`, `atan`, `exp`, `log`, and `sqrt` produce finite floats. |
5231
5470
 
5232
5471
  Arithmetic comparisons evaluate both operands. Standard term-order predicates
5233
5472
  (`@<`, `@=<`, `@>`, `@>=`) compare terms without arithmetic evaluation.
5234
- EyeProlog's documented profile order is not the complete ISO term order: strings
5235
- are a distinct scalar category, and numeric terms share one exact numeric
5236
- ordering category.
5473
+ EyeProlog's documented profile order distinguishes floats from integers rather
5474
+ than applying arithmetic equality across representations. Double-quoted text
5475
+ does not introduce another ISO term-order category: it becomes the list or atom
5476
+ selected by `double_quotes` before comparison.
5237
5477
 
5238
5478
  ### Errors
5239
5479
 
@@ -5243,6 +5483,22 @@ instantiation raises `instantiation_error`; wrong argument categories raise
5243
5483
  `evaluation_error`. JavaScript embedders receive these as `PrologError`
5244
5484
  instances whose message contains the corresponding Prolog error term.
5245
5485
 
5486
+ | Error class | Typical cause |
5487
+ | --- | --- |
5488
+ | *instantiation_error* | A required callable, stream, number, list, option value, or construction input is still unbound. |
5489
+ | *type_error(Expected,Culprit)* | A bound value has the wrong term category, such as a noninteger index or non-callable goal. |
5490
+ | *domain_error(Domain,Culprit)* | The type is correct but the value is outside the supported domain, such as a bad stream option or operator priority. |
5491
+ | *representation_error(Flag)* | A value cannot be represented by the profile, such as an invalid Unicode scalar code. |
5492
+ | *evaluation_error(zero_divisor)* | Integer or floating-point division was attempted with a zero divisor. |
5493
+ | *evaluation_error(undefined)* | Floating-point evaluation produced a non-finite or undefined result. |
5494
+ | *permission_error(Operation,Permission,Culprit)* | A static procedure was modified, a stream was used in the wrong mode, or a protected resource was accessed. |
5495
+ | *existence_error(Object,Culprit)* | A stream, source sink, or required procedure does not exist. |
5496
+ | *syntax_error(number)*, *syntax_error(read_term)* | Lexical number conversion or streamed term parsing failed. |
5497
+
5498
+ `catch/3` converts a `PrologError` into a catchable
5499
+ `error(Formal,eyeprolog)` term. A user ball from `throw/1` is delivered as-is.
5500
+ An unmatched ball or error continues outward.
5501
+
5246
5502
  Streams belong to one solver run and are shared by nested calls, exceptions,
5247
5503
  and solution collectors. `user_input` and `user_output` are always present.
5248
5504
  `open/4` supports `type/1`, `alias/1`, `reposition/1`, and `eof_action/1`;
@@ -5405,12 +5661,14 @@ eyeprolog --goal 'answer(Kind, Value)' program.pl
5405
5661
  #### Portable text, lexical values, and pattern matching
5406
5662
 
5407
5663
  The portable text API uses **ISO atoms or proper lists of one-character atoms**.
5408
- A generated text result defaults to an atom. This is intentionally different
5409
- from EyeProlog's double-quoted `string` term extension: strings remain available
5410
- as data values, but portable library text is represented with ISO terms. The RDF
5411
- tools emit lexical forms as atoms while preserving RDF distinctions in the
5412
- surrounding `iri/1`, `literal/2`, datatype, language, and direction terms. The
5413
- 48-predicate portable file itself has no STRING or JavaScript dependency.
5664
+ A generated text result defaults to an atom. Double-quoted source text uses the
5665
+ ISO representation selected by `double_quotes`; with the default `chars`, it is
5666
+ already a proper character list accepted by this API. The RDF tools emit lexical
5667
+ forms as atoms while preserving RDF distinctions in the surrounding `iri/1`,
5668
+ `literal/2`, datatype, language, and direction terms. Their private host adapter
5669
+ may encounter an internal RDF string value, but that value is converted at the
5670
+ boundary and is never produced by double-quoted Prolog syntax. The 48-predicate
5671
+ portable file itself has no STRING or JavaScript dependency.
5414
5672
 
5415
5673
  | Predicate and principal mode | Behavior |
5416
5674
  | --- | --- |
@@ -6136,7 +6394,7 @@ proof terms, warnings, answer formatting, embedding, and external data
6136
6394
  adapters.
6137
6395
 
6138
6396
  The executable corpus under `test/conformance/` tests the JavaScript
6139
- implementation. Positive programs and exact output cover arithmetic, strings,
6397
+ implementation. Positive programs and exact output cover arithmetic, text relations,
6140
6398
  lists, terms, atoms, variables, negation, queries, rules, and
6141
6399
  syntax. Separate corpora cover expected errors, warnings, and proofs:
6142
6400
 
@@ -6146,7 +6404,7 @@ node test/run-conformance-report.mjs
6146
6404
  ```
6147
6405
 
6148
6406
  The complete suite must pass before release. The file-based conformance corpus
6149
- contains 685 cases, including 279 focused ISO
6407
+ contains 686 cases, including 280 focused ISO
6150
6408
  cases derived from the success, failure, mode, and error behavior in
6151
6409
  ISO/IEC 13211-1 clauses 7 and 8. Separate exact-output suites check 204 normal
6152
6410
  examples, 71 proof examples, and extracted book displays. The seven-case
@@ -6190,15 +6448,15 @@ additional compatibility conveniences.
6190
6448
 
6191
6449
  This breadth is not a formal certification of every processor requirement.
6192
6450
  The executable examples are EyeProlog-profile programs using host-supplied goals,
6193
- strings, explicit integrity relations, automatic tabling, and the EyeProlog library.
6451
+ character lists, explicit integrity relations, automatic tabling, and the EyeProlog library.
6194
6452
  The remaining qualifications are:
6195
6453
 
6196
6454
  - zero-arity compound syntax such as `ready()` is represented by the atom
6197
6455
  `ready`;
6198
6456
  - modules and DCG notation are outside this Part 1 profile;
6199
6457
  - variables cannot occupy functor or predicate position;
6200
- - double-quoted text is a distinct EyeProlog string scalar rather than switching
6201
- representation with the `double_quotes` flag;
6458
+ - double-quoted text follows `double_quotes` exactly; the default `chars` value
6459
+ matches Trealla and Scryer and may be changed to `codes` or `atom`;
6202
6460
  - `write_term/2-3` implements `quoted/1`, `ignore_ops/1`, `numbervars/1`,
6203
6461
  and `variable_names/1`; other write options and some option/error precedence
6204
6462
  combinations remain outside the profile;
@@ -6567,8 +6825,9 @@ replaces those variables consistently throughout a term or clause.
6567
6825
  **Tabling.** Evaluation that shares recursive calls and accumulates their
6568
6826
  answers toward a fixed point.
6569
6827
 
6570
- **Term.** An atom constant, string, number, variable, compound term, list, or
6571
- parenthesized comma term.
6828
+ **Term.** An atom constant, number, variable, compound term, list, or
6829
+ parenthesized comma term. Double-quoted notation denotes a list or atom as
6830
+ selected by the ISO `double_quotes` flag.
6572
6831
 
6573
6832
  **Termination measure.** A value in a well-founded order that strictly
6574
6833
  decreases along every recursive branch in a stated mode.