eyeprolog 1.5.1 → 1.5.2
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/index.d.ts +6 -0
- package/package.json +3 -2
- package/src/cli.js +1 -1
- package/src/index.js +3 -0
- package/src/library-autoload-index.js +346 -0
- package/src/program.js +37 -15
- package/src/repl.js +38 -3
- package/src/solver.js +7 -6
- package/src/standard-library.js +11 -6
- package/test/run-regression.mjs +88 -8
- package/the-art-of-eyeprolog.md +58 -31
- package/tools/generate-library-autoload-index.mjs +107 -0
package/index.d.ts
CHANGED
|
@@ -289,6 +289,9 @@ export const eyePrologLibraryIndicators: readonly string[];
|
|
|
289
289
|
export const eyePrologNativeLibraryIndicators: readonly string[];
|
|
290
290
|
export const eyePrologPortableLibraryIndicators: readonly string[];
|
|
291
291
|
export const eyePrologInteropAutoload: Readonly<Record<string, string>>;
|
|
292
|
+
export const eyePrologLibraryAutoload: Readonly<Record<string, string>>;
|
|
293
|
+
export const eyePrologAmbiguousLibraryAutoload: Readonly<Record<string, readonly string[]>>;
|
|
294
|
+
export const eyePrologLibraryAutoloadModules: readonly string[];
|
|
292
295
|
export const eyePrologInteropLibraryIndicators: readonly string[];
|
|
293
296
|
export const eyePrologInteropLibraryModules: readonly string[];
|
|
294
297
|
export class PrologError extends Error {
|
|
@@ -374,6 +377,9 @@ declare const eyeprolog: {
|
|
|
374
377
|
eyePrologNativeLibraryIndicators: typeof eyePrologNativeLibraryIndicators;
|
|
375
378
|
eyePrologPortableLibraryIndicators: typeof eyePrologPortableLibraryIndicators;
|
|
376
379
|
eyePrologInteropAutoload: typeof eyePrologInteropAutoload;
|
|
380
|
+
eyePrologLibraryAutoload: typeof eyePrologLibraryAutoload;
|
|
381
|
+
eyePrologAmbiguousLibraryAutoload: typeof eyePrologAmbiguousLibraryAutoload;
|
|
382
|
+
eyePrologLibraryAutoloadModules: typeof eyePrologLibraryAutoloadModules;
|
|
377
383
|
eyePrologInteropLibraryIndicators: typeof eyePrologInteropLibraryIndicators;
|
|
378
384
|
eyePrologInteropLibraryModules: typeof eyePrologInteropLibraryModules;
|
|
379
385
|
run: typeof run;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.5.
|
|
6
|
+
"version": "1.5.2",
|
|
7
7
|
"description": "EyeProlog turns facts and rules into answers and proofs.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./index.js",
|
|
@@ -46,7 +46,8 @@
|
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"test": "node test/run-all.mjs",
|
|
49
|
-
"generate": "node tools/extract-book-examples.mjs",
|
|
49
|
+
"generate": "node tools/generate-library-autoload-index.mjs && node tools/extract-book-examples.mjs",
|
|
50
|
+
"generate:autoload": "node tools/generate-library-autoload-index.mjs",
|
|
50
51
|
"test:eyeprolog": "node test/run-all.mjs",
|
|
51
52
|
"test:conformance": "node test/run-conformance.mjs",
|
|
52
53
|
"test:iso-strict": "node test/run-iso-strict.mjs",
|
package/src/cli.js
CHANGED
|
@@ -281,7 +281,7 @@ Options:
|
|
|
281
281
|
--iso-strict Use ISO/IEC 13211-1 core + Corrigenda 1-3 only;
|
|
282
282
|
reject EyeProlog language extensions.
|
|
283
283
|
--portable Enforce the EyeProlog/Trealla/Scryer interop profile.
|
|
284
|
-
--no-autoload Disable
|
|
284
|
+
--no-autoload Disable bundled library predicate autoloading.
|
|
285
285
|
-v, --version Show the package version and exit.
|
|
286
286
|
-w, --warnings Print non-fatal portability warnings to stderr.
|
|
287
287
|
-g, --goal goal Solve goal and print its ground answers; may be repeated.
|
package/src/index.js
CHANGED
|
@@ -21,6 +21,9 @@ export {
|
|
|
21
21
|
eyePrologNativeLibraryIndicators,
|
|
22
22
|
eyePrologPortableLibraryIndicators,
|
|
23
23
|
eyePrologInteropAutoload,
|
|
24
|
+
eyePrologLibraryAutoload,
|
|
25
|
+
eyePrologAmbiguousLibraryAutoload,
|
|
26
|
+
eyePrologLibraryAutoloadModules,
|
|
24
27
|
eyePrologInteropLibraryIndicators,
|
|
25
28
|
eyePrologInteropLibraryModules,
|
|
26
29
|
} from './standard-library.js';
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
// Generated by tools/generate-library-autoload-index.mjs.
|
|
2
|
+
// Do not edit by hand; module/2 export lists under src/lib/ are the source of truth.
|
|
3
|
+
|
|
4
|
+
export const eyePrologLibraryAutoload = Object.freeze({
|
|
5
|
+
",/3": "reif",
|
|
6
|
+
";/3": "reif",
|
|
7
|
+
".../2": "iso_ext",
|
|
8
|
+
"*/1": "debug",
|
|
9
|
+
"\\/1": "lambda",
|
|
10
|
+
"\\/2": "lambda",
|
|
11
|
+
"\\/3": "lambda",
|
|
12
|
+
"\\/4": "lambda",
|
|
13
|
+
"\\/5": "lambda",
|
|
14
|
+
"\\/6": "lambda",
|
|
15
|
+
"\\/7": "lambda",
|
|
16
|
+
"\\/8": "lambda",
|
|
17
|
+
"#/\\/2": "clpz",
|
|
18
|
+
"#\\//2": "clpz",
|
|
19
|
+
"#\\/1": "clpz",
|
|
20
|
+
"#\\/2": "clpz",
|
|
21
|
+
"#\\=/2": "clpz",
|
|
22
|
+
"#</2": "clpz",
|
|
23
|
+
"#</3": "clpz",
|
|
24
|
+
"#<==/2": "clpz",
|
|
25
|
+
"#<==>/2": "clpz",
|
|
26
|
+
"#=/2": "clpz",
|
|
27
|
+
"#=/3": "clpz",
|
|
28
|
+
"#=</2": "clpz",
|
|
29
|
+
"#==>/2": "clpz",
|
|
30
|
+
"#>/2": "clpz",
|
|
31
|
+
"#>=/2": "clpz",
|
|
32
|
+
"^/10": "lambda",
|
|
33
|
+
"^/3": "lambda",
|
|
34
|
+
"^/4": "lambda",
|
|
35
|
+
"^/5": "lambda",
|
|
36
|
+
"^/6": "lambda",
|
|
37
|
+
"^/7": "lambda",
|
|
38
|
+
"^/8": "lambda",
|
|
39
|
+
"^/9": "lambda",
|
|
40
|
+
"+\\/2": "lambda",
|
|
41
|
+
"+\\/3": "lambda",
|
|
42
|
+
"+\\/4": "lambda",
|
|
43
|
+
"+\\/5": "lambda",
|
|
44
|
+
"+\\/6": "lambda",
|
|
45
|
+
"+\\/7": "lambda",
|
|
46
|
+
"+\\/8": "lambda",
|
|
47
|
+
"+\\/9": "lambda",
|
|
48
|
+
"=/3": "reif",
|
|
49
|
+
"$-/1": "debug",
|
|
50
|
+
"$/1": "debug",
|
|
51
|
+
"abolish_all_tables/0": "tabling",
|
|
52
|
+
"add_edges/3": "ugraphs",
|
|
53
|
+
"add_vertices/3": "ugraphs",
|
|
54
|
+
"aggregate_max/5": "aggregate",
|
|
55
|
+
"aggregate_min/5": "aggregate",
|
|
56
|
+
"all_different/1": "clpz",
|
|
57
|
+
"all_distinct/1": "clpz",
|
|
58
|
+
"append/2": "lists",
|
|
59
|
+
"append/3": "lists",
|
|
60
|
+
"assoc_to_keys/2": "assoc",
|
|
61
|
+
"assoc_to_list/2": "assoc",
|
|
62
|
+
"assoc_to_values/2": "assoc",
|
|
63
|
+
"atom_si/1": "si",
|
|
64
|
+
"atom_string/2": "strings",
|
|
65
|
+
"atomic_si/1": "si",
|
|
66
|
+
"automaton/3": "clpz",
|
|
67
|
+
"automaton/8": "clpz",
|
|
68
|
+
"bb_b_put/2": "debug",
|
|
69
|
+
"bb_get/2": "debug",
|
|
70
|
+
"bb_global_get/2": "debug",
|
|
71
|
+
"bb_put/2": "debug",
|
|
72
|
+
"between/3": "between",
|
|
73
|
+
"call_nth/2": "iso_ext",
|
|
74
|
+
"call_residue_vars/2": "atts",
|
|
75
|
+
"call_with_error_context/2": "error",
|
|
76
|
+
"can_be/2": "error",
|
|
77
|
+
"cfor/3": "iso_ext",
|
|
78
|
+
"chain/2": "clpz",
|
|
79
|
+
"char_type/2": "charsio",
|
|
80
|
+
"character_si/1": "si",
|
|
81
|
+
"chars_si/1": "si",
|
|
82
|
+
"circuit/1": "clpz",
|
|
83
|
+
"clpz_t/2": "clpz",
|
|
84
|
+
"complement/2": "ugraphs",
|
|
85
|
+
"compose/3": "ugraphs",
|
|
86
|
+
"cond_t/3": "reif",
|
|
87
|
+
"connect_ugraph/3": "ugraphs",
|
|
88
|
+
"contains/2": "strings",
|
|
89
|
+
"copy_term_nat/2": "terms",
|
|
90
|
+
"countall/2": "iso_ext",
|
|
91
|
+
"cumulative/1": "clpz",
|
|
92
|
+
"cumulative/2": "clpz",
|
|
93
|
+
"current_time/1": "time",
|
|
94
|
+
"debug/1": "debug",
|
|
95
|
+
"debug/3": "debug",
|
|
96
|
+
"del_assoc/4": "assoc",
|
|
97
|
+
"del_attr/2": "atts",
|
|
98
|
+
"del_edges/3": "ugraphs",
|
|
99
|
+
"del_max_assoc/4": "assoc",
|
|
100
|
+
"del_min_assoc/4": "assoc",
|
|
101
|
+
"del_vertices/3": "ugraphs",
|
|
102
|
+
"dif_si/2": "si",
|
|
103
|
+
"dif/2": "dif",
|
|
104
|
+
"dif/3": "reif",
|
|
105
|
+
"difference/3": "dates",
|
|
106
|
+
"disjoint2/1": "clpz",
|
|
107
|
+
"domain_error/2": "error",
|
|
108
|
+
"domain_error/3": "error",
|
|
109
|
+
"drop/3": "lists",
|
|
110
|
+
"edges/2": "ugraphs",
|
|
111
|
+
"element/3": "clpz",
|
|
112
|
+
"empty_assoc/1": "assoc",
|
|
113
|
+
"fd_dom/2": "clpz",
|
|
114
|
+
"fd_inf/2": "clpz",
|
|
115
|
+
"fd_size/2": "clpz",
|
|
116
|
+
"fd_sup/2": "clpz",
|
|
117
|
+
"fd_var/1": "clpz",
|
|
118
|
+
"findall/4": "iso_ext",
|
|
119
|
+
"foldl/4": "lists",
|
|
120
|
+
"foldl/5": "lists",
|
|
121
|
+
"foldl/6": "lists",
|
|
122
|
+
"forall/2": "iso_ext",
|
|
123
|
+
"format_/4": "format",
|
|
124
|
+
"format_time/4": "time",
|
|
125
|
+
"format/2": "format",
|
|
126
|
+
"format/3": "format",
|
|
127
|
+
"freeze/2": "freeze",
|
|
128
|
+
"ge/2": "comparison",
|
|
129
|
+
"gen_assoc/3": "assoc",
|
|
130
|
+
"gen_int/1": "between",
|
|
131
|
+
"gen_nat/1": "between",
|
|
132
|
+
"gensym/2": "gensym",
|
|
133
|
+
"get_assoc/3": "assoc",
|
|
134
|
+
"get_assoc/5": "assoc",
|
|
135
|
+
"get_attr/3": "atts",
|
|
136
|
+
"get_atts/2": "atts",
|
|
137
|
+
"get_line_to_chars/3": "charsio",
|
|
138
|
+
"get_n_chars/3": "charsio",
|
|
139
|
+
"get_single_char/1": "charsio",
|
|
140
|
+
"global_cardinality/2": "clpz",
|
|
141
|
+
"global_cardinality/3": "clpz",
|
|
142
|
+
"group_pairs_by_key/2": "pairs",
|
|
143
|
+
"gt/2": "comparison",
|
|
144
|
+
"if_/3": "reif",
|
|
145
|
+
"in/2": "clpz",
|
|
146
|
+
"indomain/1": "clpz",
|
|
147
|
+
"ins/2": "clpz",
|
|
148
|
+
"instantiation_error/0": "error",
|
|
149
|
+
"instantiation_error/1": "error",
|
|
150
|
+
"integer_si/1": "si",
|
|
151
|
+
"is_assoc/1": "assoc",
|
|
152
|
+
"is_ordset/1": "ordsets",
|
|
153
|
+
"join/3": "strings",
|
|
154
|
+
"label/1": "clpz",
|
|
155
|
+
"labeling/1": "clpb",
|
|
156
|
+
"labeling/2": "clpz",
|
|
157
|
+
"last/2": "lists",
|
|
158
|
+
"le/2": "comparison",
|
|
159
|
+
"length/2": "lists",
|
|
160
|
+
"lex_chain/1": "clpz",
|
|
161
|
+
"list_max/2": "lists",
|
|
162
|
+
"list_min/2": "lists",
|
|
163
|
+
"list_si/1": "si",
|
|
164
|
+
"list_to_assoc/2": "assoc",
|
|
165
|
+
"list_to_ord_set/2": "ordsets",
|
|
166
|
+
"list_to_set/2": "lists",
|
|
167
|
+
"listing/1": "format",
|
|
168
|
+
"lowercase/2": "strings",
|
|
169
|
+
"lsb/2": "arithmetic",
|
|
170
|
+
"lt/2": "comparison",
|
|
171
|
+
"map_assoc/2": "assoc",
|
|
172
|
+
"map_assoc/3": "assoc",
|
|
173
|
+
"map_list_to_pairs/3": "pairs",
|
|
174
|
+
"maplist/2": "lists",
|
|
175
|
+
"maplist/3": "lists",
|
|
176
|
+
"maplist/4": "lists",
|
|
177
|
+
"maplist/5": "lists",
|
|
178
|
+
"maplist/6": "lists",
|
|
179
|
+
"maplist/7": "lists",
|
|
180
|
+
"maplist/8": "lists",
|
|
181
|
+
"matches/2": "strings",
|
|
182
|
+
"matches/3": "strings",
|
|
183
|
+
"max_assoc/3": "assoc",
|
|
184
|
+
"max_list/2": "lists",
|
|
185
|
+
"maybe/0": "random",
|
|
186
|
+
"member/2": "lists",
|
|
187
|
+
"memberchk/2": "lists",
|
|
188
|
+
"memberd_t/3": "reif",
|
|
189
|
+
"min_assoc/3": "assoc",
|
|
190
|
+
"min_list/2": "lists",
|
|
191
|
+
"msb/2": "arithmetic",
|
|
192
|
+
"must_be/2": "error",
|
|
193
|
+
"neighbors/3": "ugraphs",
|
|
194
|
+
"neighbours/3": "ugraphs",
|
|
195
|
+
"nodebug/1": "debug",
|
|
196
|
+
"not_si/1": "si",
|
|
197
|
+
"nth0/3": "lists",
|
|
198
|
+
"nth0/4": "lists",
|
|
199
|
+
"nth1/3": "lists",
|
|
200
|
+
"nth1/4": "lists",
|
|
201
|
+
"number_string/2": "strings",
|
|
202
|
+
"numbervars/3": "terms",
|
|
203
|
+
"numlist/2": "between",
|
|
204
|
+
"numlist/3": "between",
|
|
205
|
+
"nvalue/2": "clpz",
|
|
206
|
+
"ord_add_element/3": "ordsets",
|
|
207
|
+
"ord_del_element/3": "ordsets",
|
|
208
|
+
"ord_disjoint/2": "ordsets",
|
|
209
|
+
"ord_empty/1": "ordsets",
|
|
210
|
+
"ord_intersect/2": "ordsets",
|
|
211
|
+
"ord_intersect/3": "ordsets",
|
|
212
|
+
"ord_intersection/2": "ordsets",
|
|
213
|
+
"ord_intersection/3": "ordsets",
|
|
214
|
+
"ord_intersection/4": "ordsets",
|
|
215
|
+
"ord_list_to_assoc/2": "assoc",
|
|
216
|
+
"ord_memberchk/2": "ordsets",
|
|
217
|
+
"ord_selectchk/3": "ordsets",
|
|
218
|
+
"ord_seteq/2": "ordsets",
|
|
219
|
+
"ord_subset/2": "ordsets",
|
|
220
|
+
"ord_subtract/3": "ordsets",
|
|
221
|
+
"ord_symdiff/3": "ordsets",
|
|
222
|
+
"ord_union/2": "ordsets",
|
|
223
|
+
"ord_union/3": "ordsets",
|
|
224
|
+
"ord_union/4": "ordsets",
|
|
225
|
+
"pairs_keys_values/3": "pairs",
|
|
226
|
+
"pairs_keys/2": "pairs",
|
|
227
|
+
"pairs_values/2": "pairs",
|
|
228
|
+
"permutation/2": "lists",
|
|
229
|
+
"phrase_from_file/2": "pio",
|
|
230
|
+
"phrase_from_file/3": "pio",
|
|
231
|
+
"phrase_to_file/2": "pio",
|
|
232
|
+
"phrase_to_file/3": "pio",
|
|
233
|
+
"phrase_to_stream/2": "pio",
|
|
234
|
+
"phrase/2": "dcgs",
|
|
235
|
+
"phrase/3": "dcgs",
|
|
236
|
+
"popcount/2": "arithmetic",
|
|
237
|
+
"portray_clause_/3": "format",
|
|
238
|
+
"portray_clause/1": "format",
|
|
239
|
+
"portray_clause/2": "format",
|
|
240
|
+
"put_assoc/4": "assoc",
|
|
241
|
+
"put_attr/3": "atts",
|
|
242
|
+
"put_atts/2": "atts",
|
|
243
|
+
"random_integer/3": "random",
|
|
244
|
+
"random_labeling/2": "clpb",
|
|
245
|
+
"random/1": "random",
|
|
246
|
+
"random/3": "random",
|
|
247
|
+
"reachable/3": "ugraphs",
|
|
248
|
+
"repeat/1": "between",
|
|
249
|
+
"replace/4": "strings",
|
|
250
|
+
"representation_error/1": "error",
|
|
251
|
+
"reset_gensym/1": "gensym",
|
|
252
|
+
"resource_error/1": "error",
|
|
253
|
+
"reverse/2": "lists",
|
|
254
|
+
"same_length/2": "lists",
|
|
255
|
+
"sat_count/2": "clpb",
|
|
256
|
+
"sat/1": "clpb",
|
|
257
|
+
"scalar_product/4": "clpz",
|
|
258
|
+
"select/3": "lists",
|
|
259
|
+
"seq/3": "dcgs",
|
|
260
|
+
"seqq/3": "dcgs",
|
|
261
|
+
"serialized/2": "clpz",
|
|
262
|
+
"set_nth0/4": "lists",
|
|
263
|
+
"set_random/1": "random",
|
|
264
|
+
"slice/4": "lists",
|
|
265
|
+
"smallest_divisor_from/3": "primes",
|
|
266
|
+
"split/3": "strings",
|
|
267
|
+
"start_tabling/2": "tabling",
|
|
268
|
+
"string_concat/3": "strings",
|
|
269
|
+
"substring/4": "strings",
|
|
270
|
+
"succ/2": "iso_ext",
|
|
271
|
+
"sum_list/2": "lists",
|
|
272
|
+
"sum/3": "clpz",
|
|
273
|
+
"sumall/3": "aggregate",
|
|
274
|
+
"take/3": "lists",
|
|
275
|
+
"taut/2": "clpb",
|
|
276
|
+
"term_attributed_variables/2": "atts",
|
|
277
|
+
"term_si/1": "si",
|
|
278
|
+
"term_string/2": "strings",
|
|
279
|
+
"tfilter/3": "reif",
|
|
280
|
+
"time/1": "iso_ext",
|
|
281
|
+
"tmember_t/3": "reif",
|
|
282
|
+
"tmember/2": "reif",
|
|
283
|
+
"top_sort/2": "ugraphs",
|
|
284
|
+
"top_sort/3": "ugraphs",
|
|
285
|
+
"tpartition/4": "reif",
|
|
286
|
+
"transitive_closure/2": "ugraphs",
|
|
287
|
+
"transpose_ugraph/2": "ugraphs",
|
|
288
|
+
"transpose/2": "lists",
|
|
289
|
+
"trim/2": "strings",
|
|
290
|
+
"tuples_in/2": "clpz",
|
|
291
|
+
"type_error/2": "error",
|
|
292
|
+
"type_error/3": "error",
|
|
293
|
+
"ugraph_union/3": "ugraphs",
|
|
294
|
+
"uppercase/2": "strings",
|
|
295
|
+
"uuid_string/2": "uuid",
|
|
296
|
+
"uuid/3": "uuid",
|
|
297
|
+
"uuidv4_string/1": "uuid",
|
|
298
|
+
"uuidv4/1": "uuid",
|
|
299
|
+
"variant/2": "iso_ext",
|
|
300
|
+
"vertices_edges_to_ugraph/3": "ugraphs",
|
|
301
|
+
"vertices/2": "ugraphs",
|
|
302
|
+
"weighted_maximum/3": "clpb",
|
|
303
|
+
"when_si/2": "si",
|
|
304
|
+
"when/2": "when",
|
|
305
|
+
"zcompare/3": "clpz"
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
export const eyePrologAmbiguousLibraryAutoload = Object.freeze({});
|
|
309
|
+
|
|
310
|
+
export const eyePrologLibraryAutoloadModules = Object.freeze([
|
|
311
|
+
"aggregate",
|
|
312
|
+
"arithmetic",
|
|
313
|
+
"assoc",
|
|
314
|
+
"atts",
|
|
315
|
+
"between",
|
|
316
|
+
"charsio",
|
|
317
|
+
"clpb",
|
|
318
|
+
"clpz",
|
|
319
|
+
"comparison",
|
|
320
|
+
"dates",
|
|
321
|
+
"dcgs",
|
|
322
|
+
"debug",
|
|
323
|
+
"dif",
|
|
324
|
+
"error",
|
|
325
|
+
"format",
|
|
326
|
+
"freeze",
|
|
327
|
+
"gensym",
|
|
328
|
+
"iso_ext",
|
|
329
|
+
"lambda",
|
|
330
|
+
"lists",
|
|
331
|
+
"ordsets",
|
|
332
|
+
"pairs",
|
|
333
|
+
"pio",
|
|
334
|
+
"primes",
|
|
335
|
+
"prologue",
|
|
336
|
+
"random",
|
|
337
|
+
"reif",
|
|
338
|
+
"si",
|
|
339
|
+
"strings",
|
|
340
|
+
"tabling",
|
|
341
|
+
"terms",
|
|
342
|
+
"time",
|
|
343
|
+
"ugraphs",
|
|
344
|
+
"uuid",
|
|
345
|
+
"when"
|
|
346
|
+
]);
|
package/src/program.js
CHANGED
|
@@ -12,11 +12,12 @@ import {
|
|
|
12
12
|
parseGoalText,
|
|
13
13
|
tryParseClausesFastInto,
|
|
14
14
|
} from './parser.js';
|
|
15
|
-
import { PrologError, convertClauseBodyTerm, getStrictIsoRegistry } from './iso.js';
|
|
15
|
+
import { PrologError, convertClauseBodyTerm, getDefaultRegistry, getStrictIsoRegistry } from './iso.js';
|
|
16
16
|
import { currentWorkingDirectory, fs, path } from './platform.js';
|
|
17
17
|
import {
|
|
18
18
|
standardLibrarySources,
|
|
19
|
-
|
|
19
|
+
eyePrologAmbiguousLibraryAutoload,
|
|
20
|
+
eyePrologLibraryAutoload,
|
|
20
21
|
eyePrologInteropLibraryIndicators,
|
|
21
22
|
eyePrologInteropLibraryModules,
|
|
22
23
|
} from './standard-library.js';
|
|
@@ -255,7 +256,10 @@ export class Program {
|
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
258
|
this.initializations = [];
|
|
258
|
-
|
|
259
|
+
// Track how many initialization/1 directives have run so a REPL can
|
|
260
|
+
// autoload a library after startup and execute only that library's newly
|
|
261
|
+
// appended initializations without repeating earlier side effects.
|
|
262
|
+
this._initializationsExecutedCount = 0;
|
|
259
263
|
this.quads = [];
|
|
260
264
|
this.prologFlagDirectives = [];
|
|
261
265
|
this.charConversionDirectives = [];
|
|
@@ -1045,7 +1049,7 @@ export function autoloadProgramGoals(program, inputs, options = {}) {
|
|
|
1045
1049
|
const builder = new ProgramBuilder({ ...options, isoStrict: false }, program);
|
|
1046
1050
|
const loadedModules = new Set([...program.modules.keys()].filter((name) => name !== 'user'));
|
|
1047
1051
|
const autoloadCount = program.autoloadedPredicates.length;
|
|
1048
|
-
|
|
1052
|
+
autoloadLibraryDependencies(builder, {
|
|
1049
1053
|
...options,
|
|
1050
1054
|
isoStrict: false,
|
|
1051
1055
|
operatorState,
|
|
@@ -1094,7 +1098,7 @@ function loadSourcesIntoBuilder(builder, sources, options, fast) {
|
|
|
1094
1098
|
parserFlagState,
|
|
1095
1099
|
}, builder.program);
|
|
1096
1100
|
if (options.isoStrict !== true && options.autoload !== false) {
|
|
1097
|
-
|
|
1101
|
+
autoloadLibraryDependencies(builder, {
|
|
1098
1102
|
...options,
|
|
1099
1103
|
operatorState,
|
|
1100
1104
|
parserFlagState,
|
|
@@ -1164,14 +1168,32 @@ function extraGoalDependencies(goals) {
|
|
|
1164
1168
|
return dependencies;
|
|
1165
1169
|
}
|
|
1166
1170
|
|
|
1167
|
-
function
|
|
1171
|
+
function procedureResolvedBeforeAutoload(program, dependency, targetModule) {
|
|
1172
|
+
if (program.findGroup(dependency.name, dependency.arity, targetModule)) return true;
|
|
1173
|
+
return program.moduleImports.get(targetModule)?.has(dependency.key) === true;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
function autoloadLibraryFor(dependency) {
|
|
1177
|
+
// ISO built-ins always take precedence over optional library autoloading.
|
|
1178
|
+
if (getDefaultRegistry().get(dependency.name, dependency.arity) != null) return null;
|
|
1179
|
+
const ambiguous = eyePrologAmbiguousLibraryAutoload[dependency.key];
|
|
1180
|
+
if (ambiguous != null) {
|
|
1181
|
+
throw new PrologError(
|
|
1182
|
+
'permission_error(import, procedure)',
|
|
1183
|
+
compound('/', [atom(dependency.name), numberTerm(dependency.arity)]),
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
return eyePrologLibraryAutoload[dependency.key] ?? null;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function libraryAutoloadRequests(program, extraGoals = []) {
|
|
1168
1190
|
const requests = new Map();
|
|
1169
1191
|
for (const group of program.groups.values()) {
|
|
1170
1192
|
if (bundledLibraryModule(program, group.module)) continue;
|
|
1171
1193
|
for (const dependency of groupDependencies(group)) {
|
|
1172
1194
|
const targetModule = dependency.module ?? group.module;
|
|
1173
|
-
if (program
|
|
1174
|
-
const library =
|
|
1195
|
+
if (procedureResolvedBeforeAutoload(program, dependency, targetModule)) continue;
|
|
1196
|
+
const library = autoloadLibraryFor(dependency);
|
|
1175
1197
|
if (library == null) continue;
|
|
1176
1198
|
const requestKey = `${targetModule}\u0000${dependency.key}`;
|
|
1177
1199
|
requests.set(requestKey, {
|
|
@@ -1186,8 +1208,8 @@ function interopAutoloadRequests(program, extraGoals = []) {
|
|
|
1186
1208
|
for (const goal of program.initializations) {
|
|
1187
1209
|
for (const dependency of collectGoalDependencies(goal, false, true)) {
|
|
1188
1210
|
const targetModule = dependency.module ?? 'user';
|
|
1189
|
-
if (program
|
|
1190
|
-
const library =
|
|
1211
|
+
if (procedureResolvedBeforeAutoload(program, dependency, targetModule)) continue;
|
|
1212
|
+
const library = autoloadLibraryFor(dependency);
|
|
1191
1213
|
if (library == null) continue;
|
|
1192
1214
|
const requestKey = `${targetModule}\u0000${dependency.key}`;
|
|
1193
1215
|
requests.set(requestKey, {
|
|
@@ -1201,8 +1223,8 @@ function interopAutoloadRequests(program, extraGoals = []) {
|
|
|
1201
1223
|
}
|
|
1202
1224
|
for (const dependency of extraGoalDependencies(extraGoals)) {
|
|
1203
1225
|
const targetModule = dependency.module ?? 'user';
|
|
1204
|
-
if (program
|
|
1205
|
-
const library =
|
|
1226
|
+
if (procedureResolvedBeforeAutoload(program, dependency, targetModule)) continue;
|
|
1227
|
+
const library = autoloadLibraryFor(dependency);
|
|
1206
1228
|
if (library == null) continue;
|
|
1207
1229
|
const requestKey = `${targetModule}\u0000${dependency.key}`;
|
|
1208
1230
|
requests.set(requestKey, {
|
|
@@ -1216,14 +1238,14 @@ function interopAutoloadRequests(program, extraGoals = []) {
|
|
|
1216
1238
|
return [...requests.values()];
|
|
1217
1239
|
}
|
|
1218
1240
|
|
|
1219
|
-
function
|
|
1241
|
+
function autoloadLibraryDependencies(builder, options, ensured, loadedModules, extraGoals = []) {
|
|
1220
1242
|
const program = builder.program;
|
|
1221
1243
|
while (true) {
|
|
1222
|
-
const requests =
|
|
1244
|
+
const requests = libraryAutoloadRequests(program, extraGoals);
|
|
1223
1245
|
if (requests.length === 0) return;
|
|
1224
1246
|
let changed = false;
|
|
1225
1247
|
for (const request of requests) {
|
|
1226
|
-
if (program
|
|
1248
|
+
if (procedureResolvedBeforeAutoload(program, request, request.targetModule)) continue;
|
|
1227
1249
|
if (!loadedModules.has(request.library)) {
|
|
1228
1250
|
const designation = compound('library', [atom(request.library)]);
|
|
1229
1251
|
const loaded = readModuleSource(designation, options);
|
package/src/repl.js
CHANGED
|
@@ -4,6 +4,7 @@ import { readSync } from 'node:fs';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { createInterface } from 'node:readline';
|
|
6
6
|
import { formalErrorTerm } from './iso.js';
|
|
7
|
+
import { autoloadProgramGoals } from './program.js';
|
|
7
8
|
import {
|
|
8
9
|
characterCodeConstantEnd, continuesGraphicToken, isTerminatingFullStop, quotedEscapeEnd,
|
|
9
10
|
} from './syntax-scan.js';
|
|
@@ -67,6 +68,19 @@ export async function runRepl(engine, options = {}) {
|
|
|
67
68
|
continue;
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
// Queries entered after the initial Program was prepared need the same
|
|
72
|
+
// bundled-library autoload resolution as file and -g execution. This
|
|
73
|
+
// is intentionally after parsing: predicate autoloading cannot provide
|
|
74
|
+
// syntax operators retroactively (libraries such as clpz must still be
|
|
75
|
+
// imported explicitly before their operators are used).
|
|
76
|
+
if (!state.strictIso && options.autoload !== false) {
|
|
77
|
+
autoloadProgramGoals(state.program, [goal], {
|
|
78
|
+
autoload: true,
|
|
79
|
+
doubleQuotes: state.solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
80
|
+
});
|
|
81
|
+
runWithTerminalSignals(reader, () => state.solver.runInitializations());
|
|
82
|
+
}
|
|
83
|
+
|
|
70
84
|
const flagsBefore = snapshotFlagValues(state.solver);
|
|
71
85
|
try {
|
|
72
86
|
await prepareInteractiveTermInput(state, goal, reader);
|
|
@@ -284,7 +298,11 @@ function runWithTerminalSignals(reader, operation) {
|
|
|
284
298
|
|
|
285
299
|
function makeState(engine, sources, output, options = {}, previousState = null, reader = null) {
|
|
286
300
|
const strictIso = options.isoStrict === true;
|
|
287
|
-
const program = engine.Program.parseSources(sources, {
|
|
301
|
+
const program = engine.Program.parseSources(sources, {
|
|
302
|
+
isoStrict: strictIso,
|
|
303
|
+
sourceMetadata: strictIso,
|
|
304
|
+
autoload: !strictIso && options.autoload !== false,
|
|
305
|
+
});
|
|
288
306
|
const solver = new engine.Solver(program, {
|
|
289
307
|
registry: strictIso ? engine.getStrictIsoRegistry() : engine.getEyePrologRegistry(),
|
|
290
308
|
isoStrict: strictIso,
|
|
@@ -756,12 +774,29 @@ function formatAnswer(engine, state, variables, env) {
|
|
|
756
774
|
collectUnboundVariables(engine, residual, env, names, () => `_${letterName(generated++)}`);
|
|
757
775
|
bindings.push(engine.formatTermForWrite(residual, env, answerWriteOptions));
|
|
758
776
|
}
|
|
777
|
+
// Residual attributes are part of the answer even when the attributed
|
|
778
|
+
// variable was created inside a called predicate and is not itself a query
|
|
779
|
+
// variable (issue #87). First retain visible query names for attributed
|
|
780
|
+
// roots, then project every remaining live attributed root with a generated
|
|
781
|
+
// top-level variable name. This also makes call_residue_vars/2 useful at the
|
|
782
|
+
// top level: variables returned through its list share the same name with the
|
|
783
|
+
// projected constraint instead of appearing unconstrained.
|
|
759
784
|
const projectedAttributeRoots = new Set();
|
|
785
|
+
const attributeRoots = [];
|
|
760
786
|
for (const variable of variables) {
|
|
761
787
|
const root = engine.deref(variable, env);
|
|
762
|
-
if (root.type !== 'var' ||
|
|
763
|
-
projectedAttributeRoots.add(root.name);
|
|
788
|
+
if (root.type !== 'var' || !env.hasPrologAttributes?.(root.name)) continue;
|
|
764
789
|
names.set(root.name, variable.name);
|
|
790
|
+
attributeRoots.push(root);
|
|
791
|
+
}
|
|
792
|
+
for (const name of env.attributedVariableNames?.() ?? []) {
|
|
793
|
+
const root = engine.deref(engine.variable(name), env);
|
|
794
|
+
if (root.type === 'var') attributeRoots.push(root);
|
|
795
|
+
}
|
|
796
|
+
for (const root of attributeRoots) {
|
|
797
|
+
if (projectedAttributeRoots.has(root.name) || !env.hasPrologAttributes?.(root.name)) continue;
|
|
798
|
+
projectedAttributeRoots.add(root.name);
|
|
799
|
+
if (!names.has(root.name)) names.set(root.name, `_${letterName(generated++)}`);
|
|
765
800
|
for (const residual of state.solver.attributeResidualGoals(root, env)) {
|
|
766
801
|
collectUnboundVariables(engine, residual, env, names, () => `_${letterName(generated++)}`);
|
|
767
802
|
bindings.push(engine.formatTermForWrite(residual, env, answerWriteOptions));
|
package/src/solver.js
CHANGED
|
@@ -375,18 +375,19 @@ export class Solver {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
runInitializations() {
|
|
378
|
-
|
|
379
|
-
|
|
378
|
+
const goals = this.program.initializations ?? [];
|
|
379
|
+
let index = this.program._initializationsExecutedCount ?? 0;
|
|
380
|
+
for (; index < goals.length; index++) {
|
|
380
381
|
let succeeded = false;
|
|
381
|
-
for (const _ of this.solve([
|
|
382
|
+
for (const _ of this.solve([goals[index]], new Env(), 0)) {
|
|
382
383
|
succeeded = true;
|
|
383
384
|
break;
|
|
384
385
|
}
|
|
385
386
|
if (!succeeded) throw new PrologError('initialization_error');
|
|
387
|
+
// Mark each successful initialization immediately. If a later one fails,
|
|
388
|
+
// retrying does not repeat already completed initialization side effects.
|
|
389
|
+
this.program._initializationsExecutedCount = index + 1;
|
|
386
390
|
}
|
|
387
|
-
// 7.4.2.6 attaches initialization to preparation of the Prolog text, not
|
|
388
|
-
// to each later Solver instance created for the same prepared Program.
|
|
389
|
-
this.program._initializationsExecuted = true;
|
|
390
391
|
}
|
|
391
392
|
|
|
392
393
|
*solve(goals, env = new Env(), depth = 0) {
|
package/src/standard-library.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Prolog library modules shipped with EyeProlog and loaded through its documented module compatibility surface.
|
|
2
2
|
// The sources are registered here so library(Name) works in Node and browsers.
|
|
3
|
-
// Modules are loaded on demand by explicit use_module/1-2 or by the
|
|
4
|
-
// source-level
|
|
3
|
+
// Modules are loaded on demand by explicit use_module/1-2 or by the generic
|
|
4
|
+
// source-level autoloader generated from every bundled module/2 export list.
|
|
5
5
|
import { PrologError, createDefaultRegistry, eyePrologLibraryBuiltins } from './iso.js';
|
|
6
6
|
import { attsBuiltins } from './atts.js';
|
|
7
7
|
import { expansionBuiltins } from './expansion-builtins.js';
|
|
@@ -9,6 +9,9 @@ import { scryerCompatibilityBuiltins } from './scryer-compat.js';
|
|
|
9
9
|
import { registerCleanupBuiltins } from './cleanup.js';
|
|
10
10
|
import { fs, isNode, memoryStatistics } from './platform.js';
|
|
11
11
|
import { ATOM, VAR, atom, deref, numberTerm, unify } from './term.js';
|
|
12
|
+
import { eyePrologAmbiguousLibraryAutoload, eyePrologLibraryAutoload, eyePrologLibraryAutoloadModules } from './library-autoload-index.js';
|
|
13
|
+
|
|
14
|
+
export { eyePrologAmbiguousLibraryAutoload, eyePrologLibraryAutoload, eyePrologLibraryAutoloadModules };
|
|
12
15
|
|
|
13
16
|
const moduleFiles = Object.freeze({
|
|
14
17
|
aggregate: 'aggregate.pl',
|
|
@@ -152,10 +155,12 @@ export const eyePrologLibraryIndicators = Object.freeze([
|
|
|
152
155
|
]);
|
|
153
156
|
|
|
154
157
|
|
|
155
|
-
// Conservative cross-implementation profile.
|
|
156
|
-
// intentionally limited to interfaces that EyeProlog wants to keep source-
|
|
157
|
-
// compatible with the Trealla/Scryer ecosystem.
|
|
158
|
-
//
|
|
158
|
+
// Conservative cross-implementation profile metadata. These predicate indicators
|
|
159
|
+
// are intentionally limited to interfaces that EyeProlog wants to keep source-
|
|
160
|
+
// compatible with the Trealla/Scryer ecosystem. The complete runtime autoload
|
|
161
|
+
// index is generated separately from every bundled module/2 export; this smaller
|
|
162
|
+
// map is retained as the canonical interoperability subset used by diagnostics
|
|
163
|
+
// and public metadata.
|
|
159
164
|
//
|
|
160
165
|
// This is deliberately separate from eyePrologPortableLibraryIndicators:
|
|
161
166
|
// "portable" there means implemented in Prolog rather than as a host builtin;
|
package/test/run-regression.mjs
CHANGED
|
@@ -26,6 +26,9 @@ import {
|
|
|
26
26
|
eyePrologNativeLibraryIndicators,
|
|
27
27
|
eyePrologPortableLibraryIndicators,
|
|
28
28
|
eyePrologInteropAutoload,
|
|
29
|
+
eyePrologLibraryAutoload,
|
|
30
|
+
eyePrologAmbiguousLibraryAutoload,
|
|
31
|
+
eyePrologLibraryAutoloadModules,
|
|
29
32
|
eyePrologInteropLibraryIndicators,
|
|
30
33
|
eyePrologInteropLibraryModules,
|
|
31
34
|
atom,
|
|
@@ -519,6 +522,24 @@ attribute_goals(X) --> { get_atts(X, required(V)) }, [required(X,V)].
|
|
|
519
522
|
assertIncludes(repl.stdout, 'dif(Y, c), freeze:freeze(Y, a).',
|
|
520
523
|
'freeze/2 and dif/2 residuals share the query variable (issue #73)');
|
|
521
524
|
|
|
525
|
+
const hiddenResidualFile = path.join(tmp, `atts-hidden-residual-${++tmpCounter}.pl`);
|
|
526
|
+
fs.writeFileSync(hiddenResidualFile, 'ffalse :- freeze(_, false).\n');
|
|
527
|
+
const hiddenResidual = runCli([], {
|
|
528
|
+
input:
|
|
529
|
+
`[${sourceAtom(hiddenResidualFile)}].\n` +
|
|
530
|
+
'ffalse.\n' +
|
|
531
|
+
'call_residue_vars(ffalse, Vs).\n' +
|
|
532
|
+
'freeze(V,false).\n' +
|
|
533
|
+
'halt.\n',
|
|
534
|
+
});
|
|
535
|
+
assertEqual(hiddenResidual.status, 0, 'hidden attribute residual REPL status');
|
|
536
|
+
assertIncludes(hiddenResidual.stdout, 'freeze:freeze(_A, false).',
|
|
537
|
+
'top level projects hidden attributed variables (issue #87)');
|
|
538
|
+
assertIncludes(hiddenResidual.stdout, 'Vs = [_A], freeze:freeze(_A, false).',
|
|
539
|
+
'call_residue_vars/2 shares its returned variable with the projected residue (issue #87)');
|
|
540
|
+
assertIncludes(hiddenResidual.stdout, 'freeze:freeze(V, false).',
|
|
541
|
+
'direct top-level freeze/2 keeps the visible query variable name');
|
|
542
|
+
|
|
522
543
|
const scryerShape = Program.parse(`:- module(scryer_shape, [op(700, xfx, #=), probe/0]).
|
|
523
544
|
:- op(700, xfx, #=).
|
|
524
545
|
:- use_module(library(atts)).
|
|
@@ -2850,7 +2871,7 @@ c4 ?- call((!;1)).
|
|
|
2850
2871
|
},
|
|
2851
2872
|
},
|
|
2852
2873
|
{
|
|
2853
|
-
name: 'REPL applies
|
|
2874
|
+
name: 'REPL applies bundled-library autoloading to interactive time/1 and consulted ... //0',
|
|
2854
2875
|
run: () => {
|
|
2855
2876
|
const filename = path.join(tmp, `issue49-handoff-${tmpCounter++}.pl`);
|
|
2856
2877
|
fs.writeFileSync(filename, 'a --> ..., epsilon.\nepsilon --> [].\n');
|
|
@@ -3632,14 +3653,14 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
3632
3653
|
},
|
|
3633
3654
|
},
|
|
3634
3655
|
{
|
|
3635
|
-
name: 'REPL use_module
|
|
3656
|
+
name: 'REPL autoload and explicit use_module coexist for Part 2 library predicates',
|
|
3636
3657
|
run: () => {
|
|
3637
3658
|
const result = runCli([], {
|
|
3638
3659
|
input: 'append(X, Y, [1, 2, 3, 4]).\nuse_module(library(lists)).\nappend(X, Y, [1, 2, 3, 4]).\n\nhalt.\n',
|
|
3639
3660
|
});
|
|
3640
3661
|
assertEqual(result.status, 0, 'exit status');
|
|
3641
3662
|
assertEqual(result.stdout,
|
|
3642
|
-
'?-
|
|
3663
|
+
'?- X = [], Y = [1, 2, 3, 4].\n' +
|
|
3643
3664
|
'?- true.\n' +
|
|
3644
3665
|
'?- X = [], Y = [1, 2, 3, 4]\n; ... .\n?- ',
|
|
3645
3666
|
'stdout');
|
|
@@ -3946,6 +3967,50 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
3946
3967
|
assertEqual(result.stderr, '', 'stderr');
|
|
3947
3968
|
},
|
|
3948
3969
|
},
|
|
3970
|
+
{
|
|
3971
|
+
name: 'interactive top level autoloads unresolved bundled predicates',
|
|
3972
|
+
run: () => {
|
|
3973
|
+
const result = runCli([], {
|
|
3974
|
+
input: 'append(X,Y,[1,2]).\n;\nhalt.\n',
|
|
3975
|
+
});
|
|
3976
|
+
assertEqual(result.status, 0, 'exit status');
|
|
3977
|
+
assertIncludes(result.stdout, 'X = [], Y = [1, 2]', 'first append/3 answer');
|
|
3978
|
+
assertIncludes(result.stdout, 'X = [1], Y = [2].', 'second append/3 answer');
|
|
3979
|
+
assertNotIncludes(result.stdout, 'existence_error(procedure, append / 3)', 'autoload failure');
|
|
3980
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
3981
|
+
},
|
|
3982
|
+
},
|
|
3983
|
+
{
|
|
3984
|
+
name: 'all bundled library exports participate in generic autoloading',
|
|
3985
|
+
run: () => {
|
|
3986
|
+
const input = [
|
|
3987
|
+
'%% goal: pair_answer(K,V)',
|
|
3988
|
+
'%% goal: string_answer(X)',
|
|
3989
|
+
'%% goal: prime_answer(X)',
|
|
3990
|
+
'pair_answer(K,V) :- pairs_keys_values([a-1,b-2], K, V).',
|
|
3991
|
+
'string_answer(X) :- uppercase("hello", X).',
|
|
3992
|
+
'prime_answer(X) :- smallest_divisor_from(91, 2, X).',
|
|
3993
|
+
'',
|
|
3994
|
+
].join('\n');
|
|
3995
|
+
const result = runCli(['-'], { input });
|
|
3996
|
+
assertEqual(result.status, 0, 'exit status');
|
|
3997
|
+
assertEqual(result.stdout,
|
|
3998
|
+
'pair_answer("ab", [1, 2]).\nstring_answer(\'HELLO\').\nprime_answer(7).\n', 'stdout');
|
|
3999
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
4000
|
+
},
|
|
4001
|
+
},
|
|
4002
|
+
{
|
|
4003
|
+
name: 'generic autoload index is generated from every bundled library module',
|
|
4004
|
+
run: () => {
|
|
4005
|
+
assertEqual(eyePrologLibraryAutoload['member/2'], 'lists', 'facade duplicate resolves to defining lists module');
|
|
4006
|
+
assertEqual(eyePrologLibraryAutoload['pairs_keys_values/3'], 'pairs', 'pairs export is autoloadable');
|
|
4007
|
+
assertEqual(eyePrologLibraryAutoload['uppercase/2'], 'strings', 'strings export is autoloadable');
|
|
4008
|
+
assertEqual(eyePrologLibraryAutoload['smallest_divisor_from/3'], 'primes', 'primes export is autoloadable');
|
|
4009
|
+
assertEqual(Object.keys(eyePrologAmbiguousLibraryAutoload).length, 0, 'current bundled surface has no unresolved export ambiguities');
|
|
4010
|
+
assertEqual(eyePrologLibraryAutoloadModules.length, standardLibrarySources.size, 'every registered bundled module is indexed');
|
|
4011
|
+
assertEqual([...standardLibrarySources.keys()].sort().join(','), [...eyePrologLibraryAutoloadModules].sort().join(','), 'autoload module index matches bundled sources');
|
|
4012
|
+
},
|
|
4013
|
+
},
|
|
3949
4014
|
{
|
|
3950
4015
|
name: 'between/3 generated values avoid recursive environment chains (issue #52)',
|
|
3951
4016
|
run: () => {
|
|
@@ -3994,7 +4059,7 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
3994
4059
|
},
|
|
3995
4060
|
},
|
|
3996
4061
|
{
|
|
3997
|
-
name: 'strict ISO mode disables
|
|
4062
|
+
name: 'strict ISO mode disables bundled-library autoloading',
|
|
3998
4063
|
run: () => {
|
|
3999
4064
|
const input = '%% goal: answer(X)\nanswer(X) :- member(X, [a,b]).\n';
|
|
4000
4065
|
const result = runCli(['--iso-strict', '-'], { input });
|
|
@@ -4004,7 +4069,7 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
4004
4069
|
},
|
|
4005
4070
|
},
|
|
4006
4071
|
{
|
|
4007
|
-
name: 'CLI top-level goals participate in
|
|
4072
|
+
name: 'CLI top-level goals participate in bundled-library autoloading',
|
|
4008
4073
|
run: () => {
|
|
4009
4074
|
const result = runCli(['-g', 'member(X,[a,b])', '-'], { input: '' });
|
|
4010
4075
|
assertEqual(result.status, 0, 'exit status');
|
|
@@ -4707,6 +4772,18 @@ answer(Result) :- countdown(2048, Result), Result = 2048.
|
|
|
4707
4772
|
|
|
4708
4773
|
function documentationSyncCases() {
|
|
4709
4774
|
return [
|
|
4775
|
+
{
|
|
4776
|
+
name: 'generated bundled-library autoload index matches src/lib module exports',
|
|
4777
|
+
run: () => {
|
|
4778
|
+
const result = spawnSync(process.execPath, [path.join(packageRoot, 'tools', 'generate-library-autoload-index.mjs'), '--check'], {
|
|
4779
|
+
cwd: packageRoot,
|
|
4780
|
+
encoding: 'utf8',
|
|
4781
|
+
});
|
|
4782
|
+
assertEqual(result.status, 0, 'exit status');
|
|
4783
|
+
assertIncludes(result.stdout, 'library-autoload-index.js is up to date', 'stdout');
|
|
4784
|
+
assertEqual(result.stderr, '', 'stderr');
|
|
4785
|
+
},
|
|
4786
|
+
},
|
|
4710
4787
|
{
|
|
4711
4788
|
name: 'WG17 syntax status matches its executable-coverage manifest',
|
|
4712
4789
|
run: () => {
|
|
@@ -5678,7 +5755,7 @@ answer(ok) :-
|
|
|
5678
5755
|
},
|
|
5679
5756
|
},
|
|
5680
5757
|
{
|
|
5681
|
-
name: 'autoload metadata records canonical
|
|
5758
|
+
name: 'autoload metadata records canonical bundled-library imports',
|
|
5682
5759
|
run: () => {
|
|
5683
5760
|
const program = Program.parse('answer(X) :- member(X, [a]), between(1, 1, _).\n');
|
|
5684
5761
|
assertEqual(program.autoloadedPredicates.length, 2, 'autoloaded predicate count');
|
|
@@ -5688,7 +5765,7 @@ answer(ok) :-
|
|
|
5688
5765
|
},
|
|
5689
5766
|
},
|
|
5690
5767
|
{
|
|
5691
|
-
name: 'JavaScript run top-level goals participate in
|
|
5768
|
+
name: 'JavaScript run top-level goals participate in bundled-library autoloading',
|
|
5692
5769
|
run: () => {
|
|
5693
5770
|
const result = runEyeProlog('', { goal: 'member(X,[a,b])' });
|
|
5694
5771
|
assertEqual(result.stdout, 'member(a, "ab").\nmember(b, "ab").\n', 'stdout');
|
|
@@ -6192,7 +6269,10 @@ answer(ok) :-
|
|
|
6192
6269
|
assertEqual(eyePrologInteropAutoload['call_residue_vars/2'], 'atts', 'call_residue_vars/2 canonical interop autoload');
|
|
6193
6270
|
assertEqual(eyePrologInteropAutoload['time/1'], 'iso_ext', 'time/1 canonical interop autoload');
|
|
6194
6271
|
assertEqual(eyePrologInteropAutoload['.../2'], 'iso_ext', '.../2 canonical interop autoload');
|
|
6195
|
-
assertEqual(eyePrologInteropAutoload['set_nth0/4'] ?? null, null, '
|
|
6272
|
+
assertEqual(eyePrologInteropAutoload['set_nth0/4'] ?? null, null, 'set_nth0/4 is outside the conservative interop subset');
|
|
6273
|
+
assertEqual(eyePrologLibraryAutoload['set_nth0/4'], 'lists', 'complete library autoload includes EyeProlog-only exports');
|
|
6274
|
+
assertEqual(eyePrologLibraryAutoload['pairs_keys_values/3'], 'pairs', 'complete library autoload includes library(pairs)');
|
|
6275
|
+
assertEqual(eyePrologLibraryAutoloadModules.length, 35, 'all bundled src/lib modules are indexed for autoload');
|
|
6196
6276
|
assertEqual(eyePrologNativeLibraryIndicators.length, 58, 'host-supported library count');
|
|
6197
6277
|
assertEqual(eyePrologNativeLibraryIndicators.slice(0, 3).join(','), 'call_nth/2,freeze/2,dif/2', 'control and constraint predicates requiring host support');
|
|
6198
6278
|
assertEqual(eyePrologNativeLibraryIndicators.includes('random/1'), true, 'stateful random/1 is classified as host-supported');
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -1860,10 +1860,11 @@ additional logical answer.
|
|
|
1860
1860
|
`analyzeNegation`. It returns `stdout`, the solver's numeric `stats`, and a
|
|
1861
1861
|
nullable `haltCode`; it does not write to the process streams.
|
|
1862
1862
|
|
|
1863
|
-
When `run` receives an already parsed `Program`,
|
|
1863
|
+
When `run` receives an already parsed `Program`, bundled-library imports
|
|
1864
1864
|
needed only by its host-supplied goals are added to that Program before solving,
|
|
1865
|
-
just as they are while source text is parsed.
|
|
1866
|
-
|
|
1865
|
+
just as they are while source text is parsed. The autoload index covers every
|
|
1866
|
+
exported predicate in the bundled `src/lib/` modules. Pass `autoload: false`
|
|
1867
|
+
when the Program must retain only explicitly imported predicates.
|
|
1867
1868
|
|
|
1868
1869
|
For applications that inspect or prepare a theory before running it, use
|
|
1869
1870
|
`Program` directly:
|
|
@@ -6303,8 +6304,9 @@ as a compatibility facade over the canonical `lists`, `between`, `iso_ext`,
|
|
|
6303
6304
|
and `freeze` modules. The published Prologue, `call_nth/2`, and `length/2`
|
|
6304
6305
|
quads are retained as offline regressions. `src/standard-library.js` registers the module sources and private control and
|
|
6305
6306
|
constraint adapters for Node and browser resolution. Explicit `use_module/1-2`
|
|
6306
|
-
loads remain supported; outside strict ISO mode, the
|
|
6307
|
-
|
|
6307
|
+
loads remain supported; outside strict ISO mode, the bundled-library autoloader
|
|
6308
|
+
may also load the canonical owner of any exported `src/lib/` predicate. The
|
|
6309
|
+
autoload index is generated from the libraries' `module/2` declarations.
|
|
6308
6310
|
The core registry remains available through `createDefaultRegistry()` and
|
|
6309
6311
|
`getDefaultRegistry()` for low-level embedding. The stricter Part 1 +
|
|
6310
6312
|
Corrigenda registry is exposed as `createStrictIsoRegistry()` and
|
|
@@ -6382,7 +6384,7 @@ bindings make one aligned subterm pair sufficient. For example:
|
|
|
6382
6384
|
|
|
6383
6385
|
<!-- eyeprolog-library-catalog:end -->
|
|
6384
6386
|
|
|
6385
|
-
#### Interoperability profile and
|
|
6387
|
+
#### Interoperability profile and bundled-library autoloading
|
|
6386
6388
|
|
|
6387
6389
|
EyeProlog keeps four related concepts separate:
|
|
6388
6390
|
|
|
@@ -6391,7 +6393,7 @@ EyeProlog keeps four related concepts separate:
|
|
|
6391
6393
|
| **ISO core** | The documented ISO predicate profile built into the processor. No EyeProlog library import is involved. |
|
|
6392
6394
|
| **EyeProlog library surface** | Every module and exported predicate listed in the catalog above. Programs normally access these with `use_module/1-2`. |
|
|
6393
6395
|
| **Interoperability profile** | A deliberately smaller set of library names and predicate interfaces that EyeProlog intends to keep source-compatible with Trealla and Scryer where practical. |
|
|
6394
|
-
| **Autoload
|
|
6396
|
+
| **Autoload index** | A generated index of every predicate exported by the bundled `src/lib/` modules, with one canonical provider per unambiguous predicate. |
|
|
6395
6397
|
|
|
6396
6398
|
These layers answer different questions. A predicate may be implemented entirely
|
|
6397
6399
|
as ordinary Prolog and still be outside the cross-processor interoperability
|
|
@@ -6452,9 +6454,9 @@ EyeProlog API belongs to the shared profile. `call_nth/2`, `time/1`, and
|
|
|
6452
6454
|
prints elapsed time, EyeProlog inference count, and MLips in Trealla-style form,
|
|
6453
6455
|
for example `% Time elapsed 0.832s, 65551 Inferences, 0.079 MLips`; `... //0`
|
|
6454
6456
|
describes an arbitrary number of input elements. Together they let the
|
|
6455
|
-
Trealla/Scryer DCG hand-off benchmark run in EyeProlog without source changes
|
|
6456
|
-
|
|
6457
|
-
|
|
6457
|
+
Trealla/Scryer DCG hand-off benchmark run in EyeProlog without source changes;
|
|
6458
|
+
the interactive top level uses the same bundled-predicate autoloader as file and
|
|
6459
|
+
CLI/API goal execution. The focused modules have one canonical implementation owner per
|
|
6458
6460
|
predicate. `library(prologue)` re-exports those same owners, so legacy code can
|
|
6459
6461
|
combine the facade with `library(lists)`, `library(iso_ext)`, and
|
|
6460
6462
|
`library(freeze)` in either import order without an accidental collision.
|
|
@@ -6488,25 +6490,41 @@ too few parameters raises `existence_error(lambda_parameter, ...)`. EyeProlog
|
|
|
6488
6490
|
uses its ISO `copy_term/2` implementation for the fresh-copy step and does not
|
|
6489
6491
|
require a separate `copy_term_nat/2` predicate.
|
|
6490
6492
|
|
|
6491
|
-
Autoloading is a convenience layered on top of the
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6493
|
+
Autoloading is a convenience layered on top of the module system and is
|
|
6494
|
+
independent of the smaller interoperability profile. At build time EyeProlog
|
|
6495
|
+
uses a generated index derived from every bundled `src/lib/*.pl` `module/2`
|
|
6496
|
+
export list. An otherwise unresolved predicate in source, initialization code, an explicit
|
|
6497
|
+
CLI/API goal, or an interactive top-level query therefore autoloads its canonical
|
|
6498
|
+
bundled provider.
|
|
6499
|
+
For example:
|
|
6498
6500
|
|
|
6499
6501
|
| Predicate | Canonical autoload provider |
|
|
6500
6502
|
| --- | --- |
|
|
6501
6503
|
| `member/2` | `library(lists)` |
|
|
6502
|
-
| `
|
|
6503
|
-
| `
|
|
6504
|
-
|
|
|
6504
|
+
| `pairs_keys_values/3` | `library(pairs)` |
|
|
6505
|
+
| `uppercase/2` | `library(strings)` |
|
|
6506
|
+
| `smallest_divisor_from/3` | `library(primes)` |
|
|
6505
6507
|
| `between/3` | `library(between)` |
|
|
6506
6508
|
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
library
|
|
6509
|
+
The resolution order is deliberately conservative with respect to Prolog
|
|
6510
|
+
semantics: a predicate already defined by the program wins; ISO/standard
|
|
6511
|
+
built-ins are not replaced by an autoloaded library; an explicit module import
|
|
6512
|
+
wins over autoloading; only then is the bundled autoload index consulted.
|
|
6513
|
+
Facade modules such as `library(prologue)` may re-export predicates from focused
|
|
6514
|
+
modules; the generated index chooses the unique module that actually defines
|
|
6515
|
+
the predicate. If more than one bundled module genuinely defines the same
|
|
6516
|
+
export, EyeProlog reports an import ambiguity and requires explicit
|
|
6517
|
+
`use_module/1-2` rather than guessing. The interactive top level applies this
|
|
6518
|
+
same resolution after a query has been parsed. Autoloading therefore supplies
|
|
6519
|
+
predicates, not retroactive syntax: a library that introduces operators (for
|
|
6520
|
+
example `library(clpz)` and `ins`) must still be explicitly imported before a
|
|
6521
|
+
query or source term uses those operators.
|
|
6522
|
+
|
|
6523
|
+
The index is generated by `npm run generate:autoload` into
|
|
6524
|
+
`src/library-autoload-index.js`, and the documentation regression suite checks
|
|
6525
|
+
that it is synchronized with the current `src/lib/` sources. Explicit imports
|
|
6526
|
+
remain the clearest way to state dependencies when portability or module intent
|
|
6527
|
+
should be visible in the source:
|
|
6510
6528
|
|
|
6511
6529
|
```text
|
|
6512
6530
|
:- use_module(library(lists)).
|
|
@@ -6515,7 +6533,8 @@ library dependencies:
|
|
|
6515
6533
|
|
|
6516
6534
|
Use `--no-autoload`, or the JavaScript option `autoload: false`, when every
|
|
6517
6535
|
library dependency should be explicit. `--iso-strict` always disables EyeProlog
|
|
6518
|
-
library autoloading
|
|
6536
|
+
library autoloading, so strict ISO execution never gains procedures from this
|
|
6537
|
+
implementation convenience.
|
|
6519
6538
|
|
|
6520
6539
|
`-w` / `--warnings` reports explicit dependencies on non-profile libraries and
|
|
6521
6540
|
calls to non-profile predicates from otherwise common modules. `--portable`
|
|
@@ -6927,9 +6946,17 @@ terminal signal handling: `Ctrl-C`
|
|
|
6927
6946
|
therefore terminates the current EyeProlog process immediately, and on POSIX
|
|
6928
6947
|
terminals `Ctrl-Z` suspends it in the usual shell-managed way. This remains a
|
|
6929
6948
|
host top-level convention rather than an ISO/IEC 13211-1 language feature. A
|
|
6930
|
-
period-terminated query with no solutions prints `false
|
|
6931
|
-
visible variable bindings prints `true
|
|
6932
|
-
|
|
6949
|
+
period-terminated query with no solutions prints `false.`. A solution without
|
|
6950
|
+
visible variable bindings prints `true.` only when it also has no pending
|
|
6951
|
+
residual goals. Residual constraints are part of the displayed answer even when
|
|
6952
|
+
the attributed variable was created inside a called predicate and is not a
|
|
6953
|
+
visible query variable; the top level assigns such variables generated names
|
|
6954
|
+
like `_A`. For example, if `ffalse :- freeze(_, false).`, the query `ffalse.`
|
|
6955
|
+
displays `freeze:freeze(_A, false).`, and
|
|
6956
|
+
`call_residue_vars(ffalse, Vs).` displays
|
|
6957
|
+
`Vs = [_A], freeze:freeze(_A, false).` rather than implying that the returned
|
|
6958
|
+
variable is unconstrained. Answer substitutions are rendered as valid Prolog
|
|
6959
|
+
syntax under the current operator table: when a bound value would
|
|
6933
6960
|
not be a valid right operand of the displayed `=/2`, EyeProlog adds parentheses,
|
|
6934
6961
|
for example `T = (a = b).` rather than the invalid `T = a = b.`. When an answer
|
|
6935
6962
|
ends in a graphic token, the top level inserts layout before its terminating
|
|
@@ -6994,9 +7021,9 @@ make the observed question explicit.
|
|
|
6994
7021
|
| `-h`, `--help` | Show usage |
|
|
6995
7022
|
| `-p`, `--proof` | Print `why/2` explanations |
|
|
6996
7023
|
| `-q`, `--quads` | Run embedded quad tests and fail if any do not hold |
|
|
6997
|
-
| `--iso-strict` | Restrict parsing and execution to ISO/IEC 13211-1:1995 + Corrigenda 1–3; reject EyeProlog language extensions (including `table` and `:+`) and disable
|
|
7024
|
+
| `--iso-strict` | Restrict parsing and execution to ISO/IEC 13211-1:1995 + Corrigenda 1–3; reject EyeProlog language extensions (including `table` and `:+`) and disable bundled-library autoloading |
|
|
6998
7025
|
| `--portable` | Enforce the conservative EyeProlog/Trealla/Scryer interoperability profile |
|
|
6999
|
-
| `--no-autoload` | Disable
|
|
7026
|
+
| `--no-autoload` | Disable bundled-library predicate autoloading |
|
|
7000
7027
|
| `-s`, `--stats` | Print final solver and memory statistics to stderr after execution |
|
|
7001
7028
|
| `-v`, `--version` | Print the package version |
|
|
7002
7029
|
| `-w`, `--warnings` | Print non-fatal portability warnings |
|
|
@@ -7011,8 +7038,8 @@ retains the Part 1 prefix `(?-)/1` operator and treats `-->/2` as ordinary Part
|
|
|
7011
7038
|
`phrase/2-3`. Part 2 module directives and EyeProlog libraries are rejected,
|
|
7012
7039
|
the implementation-specific `occurs_check` flag is absent, and the normal-profile
|
|
7013
7040
|
`table` declaration is unavailable. Normal mode continues to support Parts
|
|
7014
|
-
2–3 and EyeProlog extensions, plus
|
|
7015
|
-
|
|
7041
|
+
2–3 and EyeProlog extensions, plus generated autoloading across the bundled
|
|
7042
|
+
`src/lib/` library exports documented above.
|
|
7016
7043
|
|
|
7017
7044
|
Inputs may be local files, HTTP(S) URLs, or one `-` for stdin. The bare command
|
|
7018
7045
|
`eyeprolog` starts the normal REPL; `eyeprolog --iso-strict` starts the strict
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { parseClauses } from '../src/parser.js';
|
|
6
|
+
import { ATOM, COMPOUND, NUMBER, Env, properListItems } from '../src/term.js';
|
|
7
|
+
|
|
8
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
const libDir = path.join(root, 'src', 'lib');
|
|
10
|
+
const outputFile = path.join(root, 'src', 'library-autoload-index.js');
|
|
11
|
+
|
|
12
|
+
function predicateIndicator(nameTerm, arityTerm, extraArity = 0) {
|
|
13
|
+
if (nameTerm?.type !== ATOM || arityTerm?.type !== NUMBER || !/^\d+$/.test(arityTerm.name)) return null;
|
|
14
|
+
const arity = Number(arityTerm.name) + extraArity;
|
|
15
|
+
return { name: nameTerm.name, arity, key: `${nameTerm.name}/${arity}` };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function moduleExports(term) {
|
|
19
|
+
const items = properListItems(term, new Env());
|
|
20
|
+
if (items == null) throw new Error('module export list is not a proper list');
|
|
21
|
+
const exports = [];
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
if (item?.type === COMPOUND && item.name === 'op' && item.arity === 3) continue;
|
|
24
|
+
if (item?.type !== COMPOUND || !['/', '//'].includes(item.name) || item.arity !== 2) {
|
|
25
|
+
throw new Error(`unsupported module export: ${item?.name ?? '<unknown>'}`);
|
|
26
|
+
}
|
|
27
|
+
const indicator = predicateIndicator(item.args[0], item.args[1], item.name === '//' ? 2 : 0);
|
|
28
|
+
if (indicator == null) throw new Error('invalid predicate indicator in module export');
|
|
29
|
+
exports.push(indicator);
|
|
30
|
+
}
|
|
31
|
+
return exports;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function localDefinitionKey(clause) {
|
|
35
|
+
const head = clause?.head;
|
|
36
|
+
if (head == null) return null;
|
|
37
|
+
if (head.type === COMPOUND && head.name === ':-' && head.arity === 1) return null;
|
|
38
|
+
if (head.type === COMPOUND && head.name === '-->' && head.arity === 2) {
|
|
39
|
+
const nt = head.args[0];
|
|
40
|
+
if (nt?.type !== ATOM && nt?.type !== COMPOUND) return null;
|
|
41
|
+
return `${nt.name}/${nt.arity + 2}`;
|
|
42
|
+
}
|
|
43
|
+
if (head.type !== ATOM && head.type !== COMPOUND) return null;
|
|
44
|
+
return `${head.name}/${head.arity}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const candidates = new Map();
|
|
48
|
+
const modules = [];
|
|
49
|
+
for (const filename of fs.readdirSync(libDir).filter((name) => name.endsWith('.pl')).sort()) {
|
|
50
|
+
const source = fs.readFileSync(path.join(libDir, filename), 'utf8');
|
|
51
|
+
const clauses = parseClauses(source, { filename: `src/lib/${filename}`, sourceMetadata: false });
|
|
52
|
+
const definitions = new Set(clauses.map(localDefinitionKey).filter(Boolean));
|
|
53
|
+
let moduleName = null;
|
|
54
|
+
let exports = null;
|
|
55
|
+
for (const clause of clauses) {
|
|
56
|
+
const directive = clause?.head?.type === COMPOUND && clause.head.name === ':-' && clause.head.arity === 1
|
|
57
|
+
? clause.head.args[0]
|
|
58
|
+
: null;
|
|
59
|
+
if (directive?.type !== COMPOUND || directive.name !== 'module' || directive.arity !== 2) continue;
|
|
60
|
+
if (directive.args[0]?.type !== ATOM) throw new Error(`${filename}: invalid module name`);
|
|
61
|
+
moduleName = directive.args[0].name;
|
|
62
|
+
exports = moduleExports(directive.args[1]);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (moduleName == null || exports == null) throw new Error(`${filename}: missing module/2 declaration`);
|
|
66
|
+
modules.push(moduleName);
|
|
67
|
+
for (const indicator of exports) {
|
|
68
|
+
const list = candidates.get(indicator.key) ?? [];
|
|
69
|
+
list.push({ library: moduleName, local: definitions.has(indicator.key) });
|
|
70
|
+
candidates.set(indicator.key, list);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const providers = {};
|
|
75
|
+
const ambiguous = {};
|
|
76
|
+
for (const [key, list] of [...candidates.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
77
|
+
if (list.length === 1) {
|
|
78
|
+
providers[key] = list[0].library;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const local = list.filter((entry) => entry.local);
|
|
82
|
+
if (local.length === 1) {
|
|
83
|
+
providers[key] = local[0].library;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
ambiguous[key] = list.map((entry) => entry.library).sort();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const renderObject = (value) => JSON.stringify(value, null, 2)
|
|
90
|
+
.replace(/^\{/m, 'Object.freeze({')
|
|
91
|
+
.replace(/\}$/m, '})');
|
|
92
|
+
const text = `// Generated by tools/generate-library-autoload-index.mjs.\n` +
|
|
93
|
+
`// Do not edit by hand; module/2 export lists under src/lib/ are the source of truth.\n\n` +
|
|
94
|
+
`export const eyePrologLibraryAutoload = ${renderObject(providers)};\n\n` +
|
|
95
|
+
`export const eyePrologAmbiguousLibraryAutoload = ${renderObject(ambiguous)};\n\n` +
|
|
96
|
+
`export const eyePrologLibraryAutoloadModules = Object.freeze(${JSON.stringify(modules.sort(), null, 2)});\n`;
|
|
97
|
+
if (process.argv.includes('--check')) {
|
|
98
|
+
const current = fs.existsSync(outputFile) ? fs.readFileSync(outputFile, 'utf8') : '';
|
|
99
|
+
if (current !== text) {
|
|
100
|
+
console.error(`${path.relative(root, outputFile)} is stale; run npm run generate:autoload`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
console.log(`${path.relative(root, outputFile)} is up to date`);
|
|
104
|
+
} else {
|
|
105
|
+
fs.writeFileSync(outputFile, text);
|
|
106
|
+
console.log(`wrote ${path.relative(root, outputFile)} (${Object.keys(providers).length} predicates, ${Object.keys(ambiguous).length} ambiguous)`);
|
|
107
|
+
}
|