eyeprolog 1.3.0 → 1.3.1

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 CHANGED
@@ -138,14 +138,17 @@ predicates use ISO atoms or character lists. The old catch-all
138
138
  EyeProlog keeps a conservative source-level interoperability profile separate
139
139
  from the larger EyeProlog library surface. `library(lists)` follows the common
140
140
  Trealla/Scryer organization for predicates such as `member/2`, `memberchk/2`,
141
- `append/2-3`, `nth0/3-4`, `nth1/3-4`, `maplist/2-8`, and `foldl/4-6`.
141
+ `append/2-3`, `nth0/3-4`, `nth1/3-4`, `length/2`, `maplist/2-8`, and
142
+ `foldl/4-6`. Its `length/2` remains relational: with both arguments variable,
143
+ `length(Xs, N)` enumerates lists of increasing length together with `N = 0, 1,
144
+ 2, ...`.
142
145
 
143
146
  Outside `--iso-strict`, an otherwise undefined unqualified call may autoload a
144
147
  predicate only when the interop profile has one canonical EyeProlog provider.
145
- For example, `member/2` autoloads from `library(lists)` and `between/3` can use
146
- EyeProlog's internal Prologue implementation without requiring portable source
147
- to name `library(prologue)`. Use `--no-autoload` to disable this convenience.
148
- Strict ISO mode never autoloads library predicates.
148
+ For example, `member/2` autoloads from `library(lists)`, while `between/3` and
149
+ `call_nth/2` can use EyeProlog's internal Prologue implementation without
150
+ requiring portable source to name `library(prologue)`. Use `--no-autoload` to
151
+ disable this convenience. Strict ISO mode never autoloads library predicates.
149
152
 
150
153
  Use `-w` / `--warnings` to diagnose dependencies outside the interop profile,
151
154
  or `--portable` to make such diagnostics fail the run. This catches both
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.0",
6
+ "version": "1.3.1",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/lib/lists.pl CHANGED
@@ -133,8 +133,17 @@ nth1(N, List, Elem, Rest) :-
133
133
 
134
134
  reverse(List, Reversed) :- lists__reverse(List, [], Reversed).
135
135
 
136
- length(List, Length) :- nonvar(List), lists__length_count(List, 0, Length).
137
- length(List, Length) :- var(List), integer(Length), Length >= 0, lists__length_make(Length, List).
136
+ % Keep the common lists:length/2 relation fully relational. In particular,
137
+ % length(List, Length) with both arguments variable enumerates lists of
138
+ % increasing length, as required by portable generators such as the issue #28
139
+ % number_chars/2 stress test.
140
+ length(List, Length) :-
141
+ nonvar(Length), !,
142
+ lists__integer(Length),
143
+ lists__not_less_than_zero(Length),
144
+ lists__length_fixed(Length, List).
145
+ length(List, Length) :-
146
+ lists__length_generate(List, 0, Length).
138
147
 
139
148
  foldl(_, [], Acc, Acc).
140
149
  foldl(Closure, [A|As], Acc0, Acc) :-
@@ -186,10 +195,25 @@ slice(Start, Count, List, Slice) :-
186
195
  lists__reverse([], Acc, Acc).
187
196
  lists__reverse([X|Xs], Acc, Out) :- lists__reverse(Xs, [X|Acc], Out).
188
197
 
189
- lists__length_count([], N, N).
190
- lists__length_count([_|Xs], N0, N) :- N1 is N0 + 1, lists__length_count(Xs, N1, N).
191
- lists__length_make(0, []).
192
- lists__length_make(N, [_|Xs]) :- N > 0, N1 is N - 1, lists__length_make(N1, Xs).
198
+ lists__length_fixed(0, []).
199
+ lists__length_fixed(N, [_|Xs]) :-
200
+ N > 0,
201
+ N1 is N - 1,
202
+ lists__length_fixed(N1, Xs).
203
+
204
+ lists__length_generate([], N, N).
205
+ lists__length_generate([_|Xs], N0, N) :-
206
+ N1 is N0 + 1,
207
+ lists__length_generate(Xs, N1, N).
208
+
209
+ lists__integer(X) :- integer(X), !.
210
+ lists__integer(X) :- var(X), !, 0 is X.
211
+ % arg/3 performs the ISO integer type check before inspecting its term.
212
+ lists__integer(X) :- arg(X, type_check, _).
213
+
214
+ lists__not_less_than_zero(X) :- X >= 0, !.
215
+ % atom_length/2 reports domain_error(not_less_than_zero) for a negative value.
216
+ lists__not_less_than_zero(X) :- atom_length('', X).
193
217
 
194
218
  lists__sum_list([], Sum, Sum).
195
219
  lists__sum_list([X|Xs], Acc, Sum) :- Next is Acc + X, lists__sum_list(Xs, Next, Sum).
@@ -109,6 +109,10 @@ export const eyePrologInteropAutoload = Object.freeze({
109
109
  'foldl/6': 'lists',
110
110
  'sum_list/2': 'lists',
111
111
  'list_to_set/2': 'lists',
112
+ // call_nth/2 is available in both Trealla and Scryer (the latter via
113
+ // library(iso_ext)). EyeProlog keeps its adapter in library(prologue), so
114
+ // autoloading hides that implementation-specific location from source.
115
+ 'call_nth/2': 'prologue',
112
116
  // Trealla and Scryer expose between/3 without an EyeProlog-style
113
117
  // library(prologue) dependency. EyeProlog keeps its implementation in the
114
118
  // Prologue module but autoloads it so portable source need not name that
@@ -2030,6 +2030,21 @@ c4 ?- call((!;1)).
2030
2030
  assertEqual(result.stderr, '', 'stderr');
2031
2031
  },
2032
2032
  },
2033
+ {
2034
+ name: 'library(lists) length/2 stays relational and call_nth/2 autoloads (issue #28)',
2035
+ run: () => {
2036
+ const input = [
2037
+ ':- use_module(library(lists)).',
2038
+ '%% goal: fourth_length(N)',
2039
+ 'fourth_length(N) :- call_nth(length(_Xs, N), 4).',
2040
+ '',
2041
+ ].join('\n');
2042
+ const result = runCli(['-'], { input });
2043
+ assertEqual(result.status, 0, 'exit status');
2044
+ assertEqual(result.stdout, 'fourth_length(3).\n', 'stdout');
2045
+ assertEqual(result.stderr, '', 'stderr');
2046
+ },
2047
+ },
2033
2048
  {
2034
2049
  name: 'strict ISO mode disables interop autoloading',
2035
2050
  run: () => {
@@ -3433,10 +3448,11 @@ check(A, B, C, D, E, F) :-
3433
3448
  assertEqual(Boolean(library.get('statistics', 2)), true, 'statistics/2 is an EyeProlog observability extension');
3434
3449
  assertEqual(registeredNativeEyePrologLibraryNames().length, 40, 'public native EyeProlog builtin count');
3435
3450
  assertEqual(eyePrologPortableLibraryIndicators.length, 62, 'portable Prolog library count');
3436
- assertEqual(eyePrologInteropLibraryIndicators.length, 26, 'cross-implementation interop profile count');
3451
+ assertEqual(eyePrologInteropLibraryIndicators.length, 27, 'cross-implementation interop profile count');
3437
3452
  assertEqual(eyePrologInteropLibraryModules.join(','), 'lists', 'common explicit library module profile');
3438
3453
  assertEqual(eyePrologInteropAutoload['member/2'], 'lists', 'member/2 canonical autoload');
3439
3454
  assertEqual(eyePrologInteropAutoload['between/3'], 'prologue', 'between/3 canonical internal autoload');
3455
+ assertEqual(eyePrologInteropAutoload['call_nth/2'], 'prologue', 'call_nth/2 canonical internal autoload');
3440
3456
  assertEqual(eyePrologInteropAutoload['set_nth0/4'] ?? null, null, 'EyeProlog-only set_nth0/4 is not autoloadable');
3441
3457
  assertEqual(eyePrologNativeLibraryIndicators.length, 40, 'native host library count');
3442
3458
  assertEqual(eyePrologNativeLibraryIndicators.slice(0, 2).join(','), 'call_nth/2,freeze/2', 'control predicates requiring host support');
@@ -6037,17 +6037,21 @@ still have an API that is not shared by the other systems.
6037
6037
  `library(lists)` is the first aligned common module. Its interop surface includes
6038
6038
  `member/2`, `memberchk/2`, `select/3`, `append/2-3`, `last/2`,
6039
6039
  `same_length/2`, `nth0/3-4`, `nth1/3-4`, `reverse/2`, `length/2`,
6040
- `maplist/2-8`, `foldl/4-6`, `sum_list/2`, and `list_to_set/2`. EyeProlog-only
6041
- helpers such as `set_nth0/4`, `take/3`, `drop/3`, and `slice/4` remain available
6042
- for compatibility but are outside this profile.
6040
+ `maplist/2-8`, `foldl/4-6`, `sum_list/2`, and `list_to_set/2`. Its `length/2`
6041
+ is fully relational: when both arguments are variables, `length(Xs, N)`
6042
+ enumerates `Xs = [], N = 0`, then one-element lists with `N = 1`, and so on.
6043
+ This generator mode is important for portable stress and enumeration programs.
6044
+ EyeProlog-only helpers such as `set_nth0/4`, `take/3`, `drop/3`, and `slice/4`
6045
+ remain available for compatibility but are outside this profile.
6043
6046
 
6044
6047
  Normal EyeProlog execution can autoload an otherwise undefined unqualified call
6045
6048
  only when the interop table assigns it one canonical provider. Thus `member/2`
6046
- autoloads from `library(lists)`. `between/3` is also in the interop profile;
6047
- EyeProlog can satisfy it from its internal `library(prologue)` implementation
6048
- without forcing portable source to mention that EyeProlog-specific library
6049
- name. Autoloading is disabled by `--no-autoload`, by the JavaScript option
6050
- `autoload: false`, and always by `--iso-strict`.
6049
+ autoloads from `library(lists)`. `between/3` and `call_nth/2` are also in the
6050
+ interop profile; EyeProlog can satisfy them from its internal
6051
+ `library(prologue)` implementation without forcing portable source to mention
6052
+ that EyeProlog-specific library name. Autoloading is disabled by
6053
+ `--no-autoload`, by the JavaScript option `autoload: false`, and always by
6054
+ `--iso-strict`.
6051
6055
 
6052
6056
  `-w` / `--warnings` reports explicit non-profile library dependencies and calls
6053
6057
  to non-profile predicates from common libraries. `--portable` turns those