kibi-cli 2.39.0 → 2.40.0

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 (163) hide show
  1. package/dist/cli-register-foundation.js +2 -2
  2. package/dist/commands/check.d.ts.map +1 -1
  3. package/dist/commands/check.js +33 -9
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +15 -0
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +4 -0
  8. package/dist/commands/migrate-plan-record.d.ts +18 -0
  9. package/dist/commands/migrate-plan-record.d.ts.map +1 -0
  10. package/dist/commands/migrate-plan-record.js +112 -0
  11. package/dist/commands/migrate.d.ts +11 -2
  12. package/dist/commands/migrate.d.ts.map +1 -1
  13. package/dist/commands/migrate.js +53 -7
  14. package/dist/commands/prove.d.ts.map +1 -1
  15. package/dist/commands/prove.js +18 -3
  16. package/dist/commands/sync/manifest.d.ts.map +1 -1
  17. package/dist/commands/sync/manifest.js +17 -4
  18. package/dist/commands/sync.d.ts.map +1 -1
  19. package/dist/commands/sync.js +8 -0
  20. package/dist/commands/telemetry.d.ts.map +1 -1
  21. package/dist/commands/telemetry.js +12 -2
  22. package/dist/extractors/symbol-title-match.d.ts +31 -0
  23. package/dist/extractors/symbol-title-match.d.ts.map +1 -0
  24. package/dist/extractors/symbol-title-match.js +133 -0
  25. package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
  26. package/dist/extractors/symbols-coordinator.js +81 -12
  27. package/dist/operations/bootstrap/discovery-evidence.js +2 -2
  28. package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
  29. package/dist/operations/bootstrap/guidance-actions.js +12 -1
  30. package/dist/operations/bootstrap/plugin-offers.d.ts +7 -1
  31. package/dist/operations/bootstrap/plugin-offers.d.ts.map +1 -1
  32. package/dist/operations/bootstrap/plugin-offers.js +73 -3
  33. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  34. package/dist/operations/modeling/logic-modeling.js +2 -1
  35. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  36. package/dist/operations/modeling/model-requirement.js +2 -1
  37. package/dist/operations/mutation/symbol-refresh.d.ts +2 -0
  38. package/dist/operations/mutation/symbol-refresh.d.ts.map +1 -1
  39. package/dist/operations/mutation/symbol-refresh.js +6 -1
  40. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  41. package/dist/operations/mutation/upsert.js +7 -0
  42. package/dist/operations/planning/compile-intent.d.ts.map +1 -1
  43. package/dist/operations/planning/compile-intent.js +2 -1
  44. package/dist/operations/proof/claim-bindings.d.ts +11 -0
  45. package/dist/operations/proof/claim-bindings.d.ts.map +1 -1
  46. package/dist/operations/proof/claim-bindings.js +144 -1
  47. package/dist/operations/proof/code-certificates.d.ts +10 -3
  48. package/dist/operations/proof/code-certificates.d.ts.map +1 -1
  49. package/dist/operations/proof/code-certificates.js +65 -13
  50. package/dist/operations/proof/code-languages.d.ts +104 -0
  51. package/dist/operations/proof/code-languages.d.ts.map +1 -0
  52. package/dist/operations/proof/code-languages.js +119 -0
  53. package/dist/operations/proof/code-models.d.ts +2 -0
  54. package/dist/operations/proof/code-models.d.ts.map +1 -1
  55. package/dist/operations/proof/code-models.js +11 -5
  56. package/dist/operations/proof/code-runner.d.ts +12 -9
  57. package/dist/operations/proof/code-runner.d.ts.map +1 -1
  58. package/dist/operations/proof/code-runner.js +81 -38
  59. package/dist/operations/proof/code-scope.d.ts +8 -0
  60. package/dist/operations/proof/code-scope.d.ts.map +1 -1
  61. package/dist/operations/proof/code-scope.js +30 -1
  62. package/dist/operations/proof/explain.d.ts +13 -0
  63. package/dist/operations/proof/explain.d.ts.map +1 -1
  64. package/dist/operations/proof/explain.js +66 -0
  65. package/dist/operations/proof/ingest-proof.d.ts +2 -0
  66. package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
  67. package/dist/operations/proof/ingest-proof.js +1 -0
  68. package/dist/operations/proof/java-package-shadow.d.ts +14 -0
  69. package/dist/operations/proof/java-package-shadow.d.ts.map +1 -0
  70. package/dist/operations/proof/java-package-shadow.js +389 -0
  71. package/dist/operations/proof/prolog-evaluator.d.ts +14 -0
  72. package/dist/operations/proof/prolog-evaluator.d.ts.map +1 -0
  73. package/dist/operations/proof/prolog-evaluator.js +247 -0
  74. package/dist/operations/proof/prolog-source-reader.d.ts +20 -0
  75. package/dist/operations/proof/prolog-source-reader.d.ts.map +1 -0
  76. package/dist/operations/proof/prolog-source-reader.js +1123 -0
  77. package/dist/operations/proof/source-code-readers.d.ts +6 -0
  78. package/dist/operations/proof/source-code-readers.d.ts.map +1 -1
  79. package/dist/operations/proof/source-code-readers.js +879 -75
  80. package/dist/plugins/approved-source-analyzers.generated.d.ts +13 -35
  81. package/dist/plugins/approved-source-analyzers.generated.d.ts.map +1 -1
  82. package/dist/plugins/approved-source-analyzers.generated.js +13 -35
  83. package/dist/plugins/plugin-install-check.d.ts +21 -0
  84. package/dist/plugins/plugin-install-check.d.ts.map +1 -0
  85. package/dist/plugins/plugin-install-check.js +62 -0
  86. package/dist/plugins/proof-reference-extractors.d.ts +20 -0
  87. package/dist/plugins/proof-reference-extractors.d.ts.map +1 -0
  88. package/dist/plugins/proof-reference-extractors.js +104 -0
  89. package/dist/plugins/reference-language-suggestion.d.ts +51 -0
  90. package/dist/plugins/reference-language-suggestion.d.ts.map +1 -0
  91. package/dist/plugins/reference-language-suggestion.js +277 -0
  92. package/dist/plugins/registry.d.ts.map +1 -1
  93. package/dist/plugins/registry.js +3 -1
  94. package/dist/plugins/source-analysis-service.d.ts.map +1 -1
  95. package/dist/plugins/source-analysis-service.js +4 -0
  96. package/dist/plugins/source-change-analysis.d.ts +26 -1
  97. package/dist/plugins/source-change-analysis.d.ts.map +1 -1
  98. package/dist/plugins/source-change-analysis.js +77 -5
  99. package/dist/plugins/source-classification.d.ts.map +1 -1
  100. package/dist/plugins/source-classification.js +23 -3
  101. package/dist/public/operations/catalog.d.ts +32 -7
  102. package/dist/public/operations/catalog.d.ts.map +1 -1
  103. package/dist/public/operations/check-executor.d.ts.map +1 -1
  104. package/dist/public/operations/check-executor.js +3 -0
  105. package/dist/public/operations/evidence-floor.d.ts +8 -0
  106. package/dist/public/operations/evidence-floor.d.ts.map +1 -1
  107. package/dist/public/operations/evidence-floor.js +19 -1
  108. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  109. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  110. package/dist/public/operations/specs/check-rules.generated.js +2 -1
  111. package/dist/public/operations/specs/check.d.ts +1 -1
  112. package/dist/public/operations/specs/mutation-schemas.d.ts +31 -6
  113. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  114. package/dist/public/operations/specs/reporting.d.ts +8 -0
  115. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  116. package/dist/public/operations/specs/reporting.js +26 -3
  117. package/dist/public/operations/specs/semantic.d.ts.map +1 -1
  118. package/dist/public/operations/specs/semantic.js +2 -1
  119. package/dist/public/operations/specs/upsert.d.ts +31 -6
  120. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  121. package/dist/public/operations/symbol-coordinates-check.d.ts +11 -0
  122. package/dist/public/operations/symbol-coordinates-check.d.ts.map +1 -0
  123. package/dist/public/operations/symbol-coordinates-check.js +66 -0
  124. package/dist/public/proof-protocol.d.ts +31 -6
  125. package/dist/public/proof-protocol.d.ts.map +1 -1
  126. package/dist/public/proof-protocol.js +45 -15
  127. package/dist/public/proof-sources.d.ts +4 -2
  128. package/dist/public/proof-sources.d.ts.map +1 -1
  129. package/dist/public/proof-sources.js +43 -4
  130. package/dist/public/skills/kibi-usage/SKILL.md +6 -3
  131. package/dist/public/skills/kibi-usage/resources/prolog-proofs.md +207 -0
  132. package/dist/public/skills/kibi-usage/resources/proof.md +44 -3
  133. package/dist/schemas/entity.schema.json +34 -6
  134. package/dist/traceability/analysis-gate.d.ts +3 -1
  135. package/dist/traceability/analysis-gate.d.ts.map +1 -1
  136. package/dist/traceability/analysis-gate.js +9 -1
  137. package/dist/traceability/staged-file-coverage.d.ts.map +1 -1
  138. package/dist/traceability/staged-file-coverage.js +7 -2
  139. package/dist/traceability/symbol-extract.d.ts +17 -0
  140. package/dist/traceability/symbol-extract.d.ts.map +1 -1
  141. package/dist/traceability/symbol-extract.js +69 -15
  142. package/dist/traceability/validate.d.ts.map +1 -1
  143. package/dist/traceability/validate.js +5 -7
  144. package/dist/utils/first-sentence.d.ts +9 -0
  145. package/dist/utils/first-sentence.d.ts.map +1 -0
  146. package/dist/utils/first-sentence.js +52 -0
  147. package/dist/utils/rule-registry.generated.d.ts +6 -0
  148. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  149. package/dist/utils/rule-registry.generated.js +7 -0
  150. package/package.json +5 -5
  151. package/src/public/operations/check-executor.ts +8 -0
  152. package/src/public/operations/evidence-floor.ts +29 -1
  153. package/src/public/operations/specs/check-rules.generated.ts +2 -1
  154. package/src/public/operations/specs/reporting.ts +38 -3
  155. package/src/public/operations/specs/semantic.ts +2 -1
  156. package/src/public/operations/symbol-coordinates-check.ts +79 -0
  157. package/src/public/proof-protocol-types.d.ts +22 -5
  158. package/src/public/proof-protocol.ts +78 -19
  159. package/src/public/proof-sources.ts +51 -3
  160. package/src/public/skills/kibi-usage/SKILL.md +6 -3
  161. package/src/public/skills/kibi-usage/resources/prolog-proofs.md +207 -0
  162. package/src/public/skills/kibi-usage/resources/proof.md +44 -3
  163. package/src/schemas/entity.schema.json +34 -6
@@ -0,0 +1,1123 @@
1
+ /*
2
+ Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
+ Copyright (C) 2026 Piotr Franczyk
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Affero General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Affero General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ /**
19
+ * The SWI-Prolog reader behind the `references` reader for `.pl` and `.plt`
20
+ * files and the `prolog` source reader. It reads terms with read_term/3 and
21
+ * runs none of the project's code: operators come from the file's own op/3
22
+ * directives and module export lists, those of project modules it loads
23
+ * (read from their module header) and those of the SWI-Prolog libraries it
24
+ * loads. `references` mode walks clause bodies and directives through
25
+ * control constructs and meta-predicate arguments (the file's own
26
+ * meta_predicate declarations and SWI-Prolog's), the goals ~@ calls in
27
+ * format strings and message lines, and the bodies of clauses
28
+ * asserted from literal terms, and reports every place it cannot read
29
+ * statically: a variable called as a goal or closure, a goal qualified by a
30
+ * variable module, a clause asserted from data, conditional compilation, a
31
+ * term_expansion or goal_expansion clause in the files or in a project file
32
+ * they load (expansion can produce any clause), and an include of a file
33
+ * outside the files read. `terms` mode lists one argument of every fact with
34
+ * a given functor, in file order.
35
+ */
36
+ // implements REQ-kibi-prolog-source-readers
37
+ export const PROLOG_SOURCE_READER = String.raw `
38
+ :- module(kibi_source_reader, []).
39
+ :- use_module(library(http/json)).
40
+ :- use_module(library(apply)).
41
+ :- use_module(library(lists)).
42
+
43
+ :- initialization(main, main).
44
+
45
+ :- dynamic member_found/1, unresolved_found/3, local_meta/2, local_pred/2, plan_file/1, expands_checked/2,
46
+ file_module/2, loaded_meta/2, loaded_export/1, loaded_read/1, search_dir/2, search_via/3, search_unread/3,
47
+ in_message_rule/0.
48
+
49
+ main :-
50
+ current_prolog_flag(argv, Argv),
51
+ last(Argv, PlanText),
52
+ atom_json_dict(PlanText, Plan, [value_string_as(string)]),
53
+ ( catch(run(Plan, Out), Error, (failure_text(Error, Text), Out = _{ok: false, error: Text}))
54
+ -> true
55
+ ; Out = _{ok: false, error: "the read failed"}
56
+ ),
57
+ nl,
58
+ json_write_dict(current_output, Out, [width(0)]),
59
+ nl.
60
+
61
+ failure_text(kibi(Text), Text) :- !.
62
+ failure_text(Error, Text) :-
63
+ catch('$messages':translate_message(Error, Lines, []), _, fail),
64
+ !,
65
+ with_output_to(string(Raw), print_message_lines(current_output, '', Lines)),
66
+ split_string(Raw, "", "\n ", [Text]).
67
+ failure_text(Error, Text) :- format(string(Text), "~q", [Error]).
68
+
69
+ refuse(Format, Args) :- format(string(Text), Format, Args), throw(kibi(Text)).
70
+
71
+ text(Value, Text) :- string(Value), !, Text = Value.
72
+ text(Value, Text) :- atom(Value), !, atom_string(Value, Text).
73
+ text(Value, Text) :- number(Value), !, number_string(Value, Text).
74
+ text(Value, Text) :- format(string(Text), "~q", [Value]).
75
+
76
+ % ------------------------------------------------------------------------
77
+ % Reading a file's terms without running it.
78
+
79
+ % clause(Term, Line, Bindings) for every term of File, in order. Operators the
80
+ % file declares (op/3 directives and its module export list) and those of the
81
+ % system libraries it loads apply while reading; nothing in the file runs.
82
+ file_terms(File, Terms) :- file_terms(File, Terms, _).
83
+
84
+ file_terms(File, Terms, Module) :-
85
+ atom_string(Path, File),
86
+ ( exists_file(Path) -> true ; refuse("~s does not exist", [File]) ),
87
+ gensym(kibi_read_, Module),
88
+ setup_call_cleanup(
89
+ open(Path, read, Stream, [encoding(utf8)]),
90
+ read_all(Stream, File, Module, Terms),
91
+ close(Stream)).
92
+
93
+ read_all(Stream, File, Module, Terms) :-
94
+ catch(read_term(Stream, Term,
95
+ [ module(Module), term_position(Position),
96
+ variable_names(Bindings), syntax_errors(error) ]),
97
+ Error,
98
+ syntax_refusal(File, Error)),
99
+ ( Term == end_of_file
100
+ -> Terms = []
101
+ ; stream_position_data(line_count, Position, Line),
102
+ declare_operators(Term, File, Module),
103
+ search_declaration(File, Line, Term),
104
+ Terms = [term(Term, Line, Bindings)|Rest],
105
+ read_all(Stream, File, Module, Rest)
106
+ ).
107
+
108
+ syntax_refusal(File, error(syntax_error(What), Context)) :- !,
109
+ ( compound(Context), arg(2, Context, Line), integer(Line) -> true ; Line = "?" ),
110
+ refuse("~s:~w is not valid Prolog Kibi can read (~w)", [File, Line, What]).
111
+ syntax_refusal(_, Error) :- throw(Error).
112
+
113
+ declare_operators((:- Directive), File, Module) :- !,
114
+ declare_directive(Directive, File, Module).
115
+ declare_operators(_, _, _).
116
+
117
+ declare_directive(op(P, T, N), _, Module) :- !, catch(Module:op(P, T, N), _, true).
118
+ declare_directive(module(_, Exports), _, Module) :- !, export_operators(Exports, Module).
119
+ declare_directive(Load, File, Module) :-
120
+ load_directive(Load, Specs), !,
121
+ forall(member(Spec, Specs), spec_operators(Spec, File, Module)).
122
+ declare_directive(_, _, _).
123
+
124
+ export_operators(Exports, Module) :-
125
+ is_list(Exports), !,
126
+ forall(member(op(P, T, N), Exports), catch(Module:op(P, T, N), _, true)).
127
+ export_operators(_, _).
128
+
129
+ % A system library brings its operators by loading it into the reading module
130
+ % (SWI-Prolog's own code, not the project's). A project module brings the
131
+ % operators its export list declares, read from its first term.
132
+ spec_operators(Spec, _, Module) :-
133
+ nonvar(Spec), Spec = library(_), !,
134
+ catch(Module:use_module(Spec), _, true).
135
+ spec_operators(Spec, File, Module) :-
136
+ project_file(Spec, File, Path), !,
137
+ catch(setup_call_cleanup(
138
+ open(Path, read, Stream),
139
+ ( read_term(Stream, First, [module(Module), syntax_errors(quiet)]),
140
+ First = (:- module(_, Exports))
141
+ -> export_operators(Exports, Module)
142
+ ; true
143
+ ),
144
+ close(Stream)),
145
+ _, true).
146
+ spec_operators(_, _, _).
147
+
148
+ project_file(Spec, _, Path) :-
149
+ alias_spec(Spec, _), !,
150
+ alias_target(Spec, Path).
151
+ project_file(Spec, File, Path) :-
152
+ path_spec(Spec, Atom), !,
153
+ atom_string(FromAtom, File),
154
+ file_directory_name(FromAtom, Directory),
155
+ directory_file_path(Directory, Atom, Base),
156
+ ( exists_file(Base) -> Path = Base
157
+ ; member(Extension, ['.pl', '.plt', '.prolog']),
158
+ atom_concat(Base, Extension, Path),
159
+ exists_file(Path)
160
+ ), !.
161
+
162
+ % Alias(Path) with an alias SWI-Prolog does not define itself (library,
163
+ % foreign, swi, pack and the rest), such as bson(include/common) after a
164
+ % file_search_path/2 the project sets up. Kibi runs none of that setup: the
165
+ % alias resolves only through the file_search_path declarations of the files
166
+ % read that it can evaluate (see search_declaration/3), and only when they
167
+ % name exactly one existing file. A file that merely ends with Path (a
168
+ % vendored copy, or one next to a target under node_modules or outside the
169
+ % project) is never taken for the file the alias names.
170
+ alias_spec(Spec, Rel) :- alias_spec(Spec, _, Rel).
171
+
172
+ alias_spec(Spec, Alias, Rel) :-
173
+ compound(Spec), Spec =.. [Alias, Rel], atom(Alias),
174
+ \+ memberchk(Alias, [/, :, -]),
175
+ \+ system_alias(Alias),
176
+ path_spec(Rel, _).
177
+
178
+ system_alias(Alias) :- user:file_search_path(Alias, _), !.
179
+
180
+ alias_target(Spec, Path) :-
181
+ alias_spec(Spec, Alias, Rel),
182
+ alias_resolution(Alias, Rel, found([Path])).
183
+
184
+ % found(Files): the existing files the declared directories hold for Rel.
185
+ % undeclared(Alias), unread(File, Line), through(Alias): why there are none.
186
+ alias_resolution(Alias, Rel, Result) :-
187
+ catch(( alias_dirs(Alias, [Alias], Dirs),
188
+ path_spec(Rel, Atom),
189
+ findall(Abs,
190
+ ( member(Dir, Dirs),
191
+ directory_file_path(Dir, Atom, Base),
192
+ member(Extension, ['', '.pl', '.plt', '.prolog']),
193
+ atom_concat(Base, Extension, Path),
194
+ exists_file(Path),
195
+ absolute_file_name(Path, Abs) ),
196
+ Found0),
197
+ sort(Found0, Found),
198
+ Result = found(Found) ),
199
+ alias_problem(Problem), Result = Problem).
200
+
201
+ alias_dirs(Alias, Seen, Dirs) :-
202
+ ( ( search_unread(Alias, File, Line) ; search_unread('$any', File, Line) )
203
+ -> throw(alias_problem(unread(File, Line)))
204
+ ; true
205
+ ),
206
+ findall(Dir, search_dir(Alias, Dir), Own),
207
+ findall(Via-Sub, search_via(Alias, Via, Sub), Vias),
208
+ ( Own == [], Vias == [] -> throw(alias_problem(undeclared(Alias))) ; true ),
209
+ findall(Dir, ( member(Via-Sub, Vias), via_dir(Via, Sub, Seen, Dir) ), Through),
210
+ append(Own, Through, Dirs).
211
+
212
+ % file_search_path(bson, base(sub)): through another alias the files declare.
213
+ % One SWI-Prolog defines (library, pack, app) lies outside the project.
214
+ via_dir(Via, Sub, Seen, Dir) :-
215
+ ( ( memberchk(Via, Seen) ; system_alias(Via) ) -> throw(alias_problem(through(Via))) ; true ),
216
+ alias_dirs(Via, [Via|Seen], Dirs),
217
+ member(Base, Dirs),
218
+ directory_file_path(Base, Sub, Dir).
219
+
220
+ % The file_search_path/2 declarations of a file read: a fact or clause for
221
+ % file_search_path/2 (user: or not), or a directive that asserts one. Kibi
222
+ % evaluates prolog_load_context(directory, D) (the declaring file's
223
+ % directory), directory_file_path/3 and atom_concat/3 on known atoms; the
224
+ % directory must then be absolute, or Alias(Sub) of another alias. Any other
225
+ % declaration (a relative directory, which depends on the working directory
226
+ % at run time, or one computed otherwise) leaves its alias unresolved.
227
+ search_declaration(File, Line, Term) :-
228
+ atom_string(Atom, File), absolute_file_name(Atom, Abs), plan_file(Abs), !,
229
+ findall(Alias,
230
+ ( sub_term(Sub, Term), compound(Sub), Sub = file_search_path(A, _),
231
+ ( atom(A) -> Alias = A ; Alias = '$any' ) ),
232
+ Aliases0),
233
+ sort(Aliases0, Aliases),
234
+ file_directory_name(Abs, Here),
235
+ forall(member(Alias, Aliases),
236
+ ( Alias \== '$any', search_values(Term, Alias, Here, Values)
237
+ -> forall(member(Value, Values), assertz(Value))
238
+ ; assertz(search_unread(Alias, File, Line))
239
+ )).
240
+ search_declaration(_, _, _).
241
+
242
+ search_values((:- Body), Alias, Here, Values) :- !,
243
+ copy_term(Body, Goals0),
244
+ conjunction_goals(Goals0, Goals),
245
+ search_goals(Goals, Here, Alias, Values),
246
+ Values \== [].
247
+ search_values((Head :- Body), Alias, Here, [Value]) :- !,
248
+ copy_term(Head-Body, H-B),
249
+ search_head(H, Alias, Dir),
250
+ conjunction_goals(B, Goals),
251
+ search_goals(Goals, Here, Alias, []),
252
+ search_value(Alias, Dir, Value).
253
+ search_values(Head, Alias, _, [Value]) :-
254
+ copy_term(Head, H),
255
+ search_head(H, Alias, Dir),
256
+ search_value(Alias, Dir, Value).
257
+
258
+ search_head(user:file_search_path(Alias, Dir), Alias, Dir) :- !.
259
+ search_head(file_search_path(Alias, Dir), Alias, Dir).
260
+
261
+ conjunction_goals(Body, Goals) :-
262
+ nonvar(Body), Body = (A, B), !,
263
+ conjunction_goals(A, GA), conjunction_goals(B, GB), append(GA, GB, Goals).
264
+ conjunction_goals(Body, [Body]) :- nonvar(Body).
265
+
266
+ % Evaluate the goals in order; the declarations they assert for Alias.
267
+ search_goals([], _, _, []).
268
+ search_goals([Goal|Goals], Here, Alias, Values) :-
269
+ search_goal(Goal, Here, Alias, Own),
270
+ search_goals(Goals, Here, Alias, Rest),
271
+ append(Own, Rest, Values).
272
+
273
+ search_goal(prolog_load_context(directory, Dir), Here, _, []) :- !, var(Dir), Dir = Here.
274
+ search_goal(directory_file_path(D, F, P), _, _, []) :- !,
275
+ atomic(D), atomic(F), var(P), directory_file_path(D, F, P).
276
+ search_goal(atom_concat(A, B, C), _, _, []) :- !,
277
+ atomic(A), atomic(B), var(C), atom_concat(A, B, C).
278
+ search_goal(Assert, _, Alias, Values) :-
279
+ nonvar(Assert), memberchk(Assert, [assert(H), asserta(H), assertz(H)]), !,
280
+ nonvar(H), search_head(H, Name, Dir), atom(Name),
281
+ ( Name == Alias -> search_value(Alias, Dir, Value), Values = [Value] ; Values = [] ).
282
+
283
+ search_value(Alias, Dir, search_dir(Alias, Abs)) :-
284
+ ( atom(Dir) ; string(Dir) ), !,
285
+ atom_string(Abs0, Dir), is_absolute_file_name(Abs0),
286
+ absolute_file_name(Abs0, Abs).
287
+ search_value(Alias, Spec, search_via(Alias, Via, Sub)) :-
288
+ compound(Spec), Spec =.. [Via, Rel], atom(Via), Via \== Alias,
289
+ \+ memberchk(Via, [/, :, -]),
290
+ path_spec(Rel, Sub).
291
+
292
+ % A workspace file as the files: list names it.
293
+ shown_path(Abs, Shown) :-
294
+ working_directory(Dir, Dir),
295
+ ( atom_concat(Dir, Relative, Abs) -> Shown = Relative ; Shown = Abs ).
296
+
297
+ % A file no files: pattern can name: outside the workspace, or under a
298
+ % directory proofs never read.
299
+ unreadable_place(Abs, Shown) :-
300
+ ( Abs == Shown
301
+ -> true
302
+ ; atomic_list_concat(Parts, /, Shown),
303
+ member(Part, Parts), memberchk(Part, [node_modules, '.git', '.kb'])
304
+ ), !.
305
+
306
+ % Why an aliased load does not resolve, and what would resolve it. A file is
307
+ % named to add to files: only when it is the one the declarations resolve to.
308
+ alias_note(Spec, Note) :-
309
+ alias_spec(Spec, Alias, Rel),
310
+ path_spec(Rel, Atom),
311
+ alias_resolution(Alias, Rel, Result),
312
+ alias_result_note(Result, Alias, Atom, Note).
313
+
314
+ alias_result_note(undeclared(Name), Alias, Atom, Note) :-
315
+ format(string(Note), "no file Kibi reads declares file_search_path(~w, Dir), so Kibi cannot tell which file ~w(~w) names (a file that ends with ~w is not taken for it); add the file that declares it to files:", [Name, Alias, Atom, Atom]).
316
+ alias_result_note(unread(File, Line), Alias, Atom, Note) :-
317
+ format(string(Note), "Kibi cannot evaluate the file_search_path declaration at ~s:~w (a relative directory or one computed at run time), so it cannot tell which file ~w(~w) names", [File, Line, Alias, Atom]).
318
+ alias_result_note(through(Via), Alias, Atom, Note) :-
319
+ format(string(Note), "file_search_path(~w, ...) goes through ~w, which Kibi does not resolve to a project directory, so it cannot tell which file ~w(~w) names", [Alias, Via, Alias, Atom]).
320
+ alias_result_note(found([]), Alias, Atom, Note) :-
321
+ format(string(Note), "no directory file_search_path(~w, Dir) declares holds ~w", [Alias, Atom]).
322
+ alias_result_note(found([One]), Alias, Atom, Note) :-
323
+ shown_path(One, Shown),
324
+ ( unreadable_place(One, Shown)
325
+ -> format(string(Note), "~w(~w) names ~w, which lies outside the files Kibi reads (node_modules, .git, .kb or outside the project)", [Alias, Atom, Shown])
326
+ ; format(string(Note), "add ~w to files: so Kibi reads it", [Shown])
327
+ ).
328
+ alias_result_note(found(Found), _, Atom, Note) :-
329
+ Found = [_, _|_],
330
+ maplist(shown_path, Found, Shown), atomic_list_concat(Shown, ', ', List),
331
+ format(string(Note), "~w could be any of ~w, so Kibi cannot tell which file the alias names", [Atom, List]).
332
+
333
+ % What would let Kibi read a load it refuses, said after its place.
334
+ load_note(include(Spec), File, Note) :- !,
335
+ ( nonvar(Spec), alias_spec(Spec, _)
336
+ -> alias_note(Spec, Note0), format(string(Note), "the included file: ~s", [Note0])
337
+ ; nonvar(Spec), project_file(Spec, File, Path)
338
+ -> absolute_file_name(Path, Abs), shown_path(Abs, Shown),
339
+ format(string(Note), "add ~w to files: so Kibi reads the included file", [Shown])
340
+ ; Note = "add the included file to files: so Kibi reads it"
341
+ ).
342
+ load_note(Directive, File, Note) :-
343
+ load_directive(Directive, Specs),
344
+ member(Spec, Specs), nonvar(Spec),
345
+ ( alias_spec(Spec, _), \+ alias_target(Spec, _)
346
+ -> alias_note(Spec, Note)
347
+ ; consult_directive(Directive), project_file(Spec, File, Path), \+ module_file(Path),
348
+ absolute_file_name(Path, Abs), \+ plan_file(Abs)
349
+ -> shown_path(Abs, Shown),
350
+ format(string(Note), "add ~w to files: so Kibi reads the file it loads", [Shown])
351
+ ),
352
+ !.
353
+
354
+ load_refusal(File, Line, Bindings, Goal) :-
355
+ ( ( Goal = (:- Directive) -> true ; Directive = Goal ),
356
+ load_note(Directive, File, Note)
357
+ -> miss_note(File, Line, Bindings, Goal, Note)
358
+ ; miss(File, Line, Bindings, Goal)
359
+ ).
360
+
361
+ % A load of Alias(Path) that names a project file Kibi cannot find.
362
+ unresolved_alias(Spec, File) :-
363
+ alias_spec(Spec, _), \+ project_file(Spec, File, _).
364
+
365
+ % A file whose first term is a module header.
366
+ module_file(Path) :-
367
+ catch(setup_call_cleanup(
368
+ open(Path, read, Stream),
369
+ read_term(Stream, First, [syntax_errors(quiet)]),
370
+ close(Stream)),
371
+ _, fail),
372
+ nonvar(First), First = (:- module(_, _)).
373
+
374
+ % A relative file spec as written: helpers, 'helpers.pl' or sub/helpers.
375
+ path_spec(Spec, Atom) :- ( atom(Spec) ; string(Spec) ), !, atom_string(Atom, Spec).
376
+ path_spec(Dir/Name, Atom) :-
377
+ path_spec(Dir, D), path_spec(Name, N),
378
+ atomic_list_concat([D, N], /, Atom).
379
+
380
+ % The load directives and the specs they name.
381
+ load_directive(use_module(S), [S]).
382
+ load_directive(use_module(S, _), [S]).
383
+ load_directive(ensure_loaded(S), [S]).
384
+ load_directive(consult(S), Specs) :- spec_list(S, Specs).
385
+ load_directive(reexport(S), [S]).
386
+ load_directive(reexport(S, _), [S]).
387
+ load_directive(load_files(S), Specs) :- spec_list(S, Specs).
388
+ load_directive(load_files(S, _), Specs) :- spec_list(S, Specs).
389
+ load_directive(include(S), [S]).
390
+ load_directive(use_foreign_library(S), [S]).
391
+ load_directive(use_foreign_library(S, _), [S]).
392
+ load_directive(autoload(S), [S]).
393
+ load_directive(autoload(S, _), [S]).
394
+ load_directive(List, List) :- is_list(List), List \== [].
395
+
396
+ spec_list(S, S) :- is_list(S), !.
397
+ spec_list(S, [S]).
398
+
399
+ conditional(if(_)).
400
+ conditional(elif(_)).
401
+ conditional(else).
402
+ conditional(endif).
403
+
404
+ % ------------------------------------------------------------------------
405
+ % The references reader.
406
+
407
+ run(Plan, _{ok: true, members: Members, unresolved: Unresolved}) :-
408
+ Plan.mode == "references", !,
409
+ retractall(member_found(_)), retractall(unresolved_found(_, _, _)),
410
+ retractall(local_meta(_, _)), retractall(local_pred(_, _)),
411
+ retractall(plan_file(_)), retractall(expands_checked(_, _)),
412
+ retractall(file_module(_, _)), retractall(loaded_meta(_, _)),
413
+ retractall(loaded_export(_)), retractall(loaded_read(_)),
414
+ retractall(search_dir(_, _)), retractall(search_via(_, _, _)), retractall(search_unread(_, _, _)),
415
+ retractall(in_message_rule),
416
+ forall(member(File, Plan.files), ( absolute_file_name(File, Abs), assertz(plan_file(Abs)) )),
417
+ maplist(plan_terms, Plan.files, Files),
418
+ forall(member(_-Terms, Files), collect_local(Terms)),
419
+ forall(member(File-Terms, Files), collect_loaded(File, Terms)),
420
+ forall(member(File-Terms, Files), expansion_misses(File, Terms)),
421
+ Extract = Plan.extract,
422
+ ( get_dict(callee, Plan, Callee) -> true ; Callee = "" ),
423
+ forall(member(File-Terms, Files),
424
+ forall(member(term(Term, Line, Bindings), Terms),
425
+ references(Extract, Callee, File, Line, Bindings, Term))),
426
+ findall(Row, unresolved_row(Row), Unresolved),
427
+ findall(M, member_found(M), Members).
428
+ run(Plan, _{ok: true, members: Members, sequence: Sequence}) :-
429
+ Plan.mode == "terms", !,
430
+ file_terms(Plan.file, Terms),
431
+ functor_spec(Plan.functor, Name, Arity),
432
+ ( get_dict(arg, Plan, Index) -> true ; Index = default ),
433
+ argument_index(Index, Plan.functor, Arity, At),
434
+ findall(Value-Line,
435
+ ( member(term(Term, Line, _), Terms),
436
+ Term \= (:- _), Term \= (_ :- _), Term \= (_ --> _),
437
+ functor(Term, Name, Arity),
438
+ term_value(At, Term, Value) ),
439
+ Rows),
440
+ findall(Text,
441
+ ( member(Value-Line, Rows),
442
+ term_members(Value, Line, Plan.file, Text) ),
443
+ Sequence),
444
+ sort(Sequence, Members).
445
+ run(Plan, _) :- refuse("unknown plan mode ~w", [Plan.mode]).
446
+
447
+ unresolved_row(_{file: File, line: Line, text: Text}) :- unresolved_found(File, Line, Text).
448
+
449
+ % Every file is read or the read fails: none is left out.
450
+ plan_terms(File, File-Terms) :-
451
+ file_terms(File, Terms, Module),
452
+ assertz(file_module(File, Module)).
453
+
454
+ % The meta_predicate declarations and exports of the project modules the
455
+ % files load (and those they reexport), so a goal handed to a project
456
+ % meta-predicate is read as one.
457
+ collect_loaded(File, Terms) :-
458
+ forall(( member(term((:- Directive), _, _), Terms),
459
+ load_directive(Directive, Specs), member(Spec, Specs),
460
+ project_file(Spec, File, Path) ),
461
+ read_loaded(Path)).
462
+
463
+ read_loaded(Path) :-
464
+ absolute_file_name(Path, Abs),
465
+ ( ( plan_file(Abs) ; loaded_read(Abs) )
466
+ -> true
467
+ ; assertz(loaded_read(Abs)),
468
+ atom_string(Abs, File),
469
+ ( catch(file_terms(File, Terms), _, fail)
470
+ -> forall(member(term(Term, _, _), Terms), loaded_term(Term)),
471
+ forall(( member(term((:- Directive), _, _), Terms),
472
+ reexport_directive(Directive, Specs), member(Spec, Specs),
473
+ project_file(Spec, File, Next) ),
474
+ read_loaded(Next))
475
+ ; true
476
+ )
477
+ ).
478
+
479
+ reexport_directive(reexport(S), [S]).
480
+ reexport_directive(reexport(S, _), [S]).
481
+
482
+ loaded_term((:- meta_predicate(Specs))) :- !,
483
+ forall(comma_member(Spec, Specs),
484
+ ( ( Spec = _:Head -> true ; Head = Spec ),
485
+ ( callable(Head) -> functor(Head, N, A), assertz(loaded_meta(N/A, Head)) ; true )
486
+ )).
487
+ loaded_term((:- module(_, Exports))) :- is_list(Exports), !,
488
+ forall(( member(E, Exports), nonvar(E), ( E = N/A ; E = N//A0, A is A0 + 2 ) ),
489
+ assertz(loaded_export(N/A))).
490
+ loaded_term(_).
491
+
492
+ % Predicates the files define, and their meta_predicate declarations.
493
+ collect_local(Terms) :-
494
+ forall(member(term(Term, _, _), Terms), collect_local_term(Term)).
495
+
496
+ collect_local_term((:- meta_predicate(Specs))) :- !,
497
+ forall(comma_member(Spec, Specs),
498
+ ( ( Spec = _:Head -> true ; Head = Spec ),
499
+ ( callable(Head) -> functor(Head, N, A), assertz(local_meta(N/A, Head)) ; true )
500
+ )).
501
+ collect_local_term((:- _)) :- !.
502
+ collect_local_term((Head --> _)) :- !,
503
+ ( callable(Head) -> head_name(Head, N, A0), A is A0 + 2, assertz(local_pred(N, A)) ; true ).
504
+ collect_local_term((Head :- _)) :- !,
505
+ ( callable(Head) -> head_name(Head, N, A), assertz(local_pred(N, A)) ; true ).
506
+ collect_local_term(Head) :-
507
+ ( callable(Head) -> head_name(Head, N, A), assertz(local_pred(N, A)) ; true ).
508
+
509
+ % term_expansion/2,4 and goal_expansion/2,4 rewrite what the file defines
510
+ % into clauses Kibi never sees, so a file that defines them, or that loads a
511
+ % project file defining them, cannot be read statically.
512
+ expansion_misses(File, Terms) :-
513
+ forall(( member(term(Term, Line, Bindings), Terms), expansion_clause(Term) ),
514
+ miss(File, Line, Bindings, Term)),
515
+ forall(( member(term((:- Directive), Line, Bindings), Terms),
516
+ load_directive(Directive, Specs), member(Spec, Specs),
517
+ project_file(Spec, File, Path), expands(Path, []) ),
518
+ miss(File, Line, Bindings, (:- Directive))).
519
+
520
+ expansion_clause(Term) :-
521
+ nonvar(Term), Term \= (:- _), Term \= (_ --> _),
522
+ ( Term = (Head :- _) -> true ; Head = Term ),
523
+ expansion_head(Head).
524
+
525
+ expansion_head(Head) :- nonvar(Head), Head = _:Inner, !, expansion_head(Inner).
526
+ expansion_head(Head) :-
527
+ callable(Head), functor(Head, Name, Arity),
528
+ memberchk(Name/Arity, [term_expansion/2, term_expansion/4, goal_expansion/2, goal_expansion/4]).
529
+
530
+ % A project file defines an expansion, or loads one that does; a file Kibi
531
+ % cannot read counts as one that does.
532
+ expands(Path, Seen) :-
533
+ absolute_file_name(Path, Abs),
534
+ ( memberchk(Abs, Seen) -> fail
535
+ ; expands_checked(Abs, Answer) -> Answer == true
536
+ ; atom_string(Path, File),
537
+ ( catch(file_terms(File, Terms), _, fail)
538
+ -> ( member(term(Term, _, _), Terms), expansion_clause(Term) -> Answer = true
539
+ ; member(term((:- Directive), _, _), Terms), load_directive(Directive, Specs),
540
+ member(Spec, Specs), project_file(Spec, File, Next), expands(Next, [Abs|Seen])
541
+ -> Answer = true
542
+ ; Answer = false
543
+ )
544
+ ; Answer = true
545
+ ),
546
+ assertz(expands_checked(Abs, Answer)),
547
+ Answer == true
548
+ ).
549
+
550
+ % An included file is part of the including module: it is read only when it
551
+ % is one of the files read. So is a file without a module header that
552
+ % consult/1, :- [File], ensure_loaded/1 or load_files/1,2 loads: its clauses
553
+ % join the loading module. A load through an alias Kibi cannot resolve could
554
+ % bring any meta-predicate or expansion, so it fails the read too.
555
+ load_miss(include(Spec), File) :- !,
556
+ \+ ( project_file(Spec, File, Path), absolute_file_name(Path, Abs), plan_file(Abs) ).
557
+ load_miss(Directive, File) :-
558
+ load_directive(Directive, Specs),
559
+ member(Spec, Specs),
560
+ ( consult_directive(Directive) -> consult_miss(Spec, File) ; unresolved_alias(Spec, File) ),
561
+ !.
562
+
563
+ consult_directive(consult(_)).
564
+ consult_directive(ensure_loaded(_)).
565
+ consult_directive(load_files(_)).
566
+ consult_directive(load_files(_, _)).
567
+ consult_directive(List) :- is_list(List).
568
+
569
+ consult_miss(Spec, _) :- var(Spec), !.
570
+ consult_miss(Spec, _) :- compound(Spec), functor(Spec, Alias, 1), system_alias(Alias), !, fail.
571
+ consult_miss(Spec, File) :-
572
+ ( project_file(Spec, File, Path)
573
+ -> \+ module_file(Path),
574
+ absolute_file_name(Path, Abs), \+ plan_file(Abs)
575
+ ; true
576
+ ).
577
+
578
+ head_name(_:Head, N, A) :- !, head_name(Head, N, A).
579
+ head_name((Head, _), N, A) :- !, head_name(Head, N, A).
580
+ head_name(Head, N, A) :- functor(Head, N, A).
581
+
582
+ comma_member(X, (A, B)) :- !, ( comma_member(X, A) ; comma_member(X, B) ).
583
+ comma_member(X, X).
584
+
585
+ found(Member) :- ( member_found(Member) -> true ; assertz(member_found(Member)) ).
586
+
587
+ miss(File, Line, Bindings, Goal) :-
588
+ goal_text(Goal, Bindings, Text),
589
+ record_miss(File, Line, Text).
590
+
591
+ miss_note(File, Line, Bindings, Goal, Note) :-
592
+ goal_text(Goal, Bindings, Text0),
593
+ format(string(Text), "~s (~s)", [Text0, Note]),
594
+ record_miss(File, Line, Text).
595
+
596
+ goal_text(Goal, Bindings, Text) :-
597
+ format(string(Text0), "~W", [Goal, [quoted(true), variable_names(Bindings), max_depth(6)]]),
598
+ sub_string_max(Text0, 80, Text).
599
+
600
+ record_miss(File, Line, Text) :-
601
+ ( unresolved_found(File, Line, Text) -> true ; assertz(unresolved_found(File, Line, Text)) ).
602
+
603
+ sub_string_max(Text, Max, Out) :-
604
+ string_length(Text, Length),
605
+ ( Length =< Max -> Out = Text ; sub_string(Text, 0, Max, _, Out) ).
606
+
607
+ % references(+Extract, +Callee, +File, +Line, +Bindings, +Term)
608
+ references(Extract, Callee, File, Line, Bindings, (:- Directive)) :- !,
609
+ ( conditional(Directive)
610
+ -> miss(File, Line, Bindings, (:- Directive))
611
+ ; load_miss(Directive, File)
612
+ -> load_refusal(File, Line, Bindings, (:- Directive))
613
+ ; load_directive(Directive, _)
614
+ -> ( Extract == "imports"
615
+ -> directive_imports(Directive, File, Line, Bindings)
616
+ ; Extract == "calls", \+ is_list(Directive)
617
+ -> extract("calls", Callee, File, Line, Bindings, none, Directive)
618
+ ; true
619
+ )
620
+ ; directive_goal(Directive)
621
+ -> true
622
+ ; body(Extract, Callee, File, Line, Bindings, none, Directive)
623
+ ).
624
+ references(Extract, Callee, File, Line, Bindings, (Head --> Body)) :- !,
625
+ ( message_rule(Head)
626
+ -> setup_call_cleanup(assertz(in_message_rule),
627
+ dcg_body(Extract, Callee, File, Line, Bindings, none, Body),
628
+ retractall(in_message_rule))
629
+ ; dcg_body(Extract, Callee, File, Line, Bindings, none, Body)
630
+ ).
631
+ references(Extract, Callee, File, Line, Bindings, (_ :- Body)) :- !,
632
+ body(Extract, Callee, File, Line, Bindings, none, Body).
633
+ references(_, _, _, _, _, _).
634
+
635
+ % Declarations that are not goals.
636
+ directive_goal(module(_, _)).
637
+ directive_goal(meta_predicate(_)).
638
+ directive_goal(dynamic(_)).
639
+ directive_goal(discontiguous(_)).
640
+ directive_goal(multifile(_)).
641
+ directive_goal(module_transparent(_)).
642
+ directive_goal(table(_)).
643
+ directive_goal(op(_, _, _)).
644
+ directive_goal(set_prolog_flag(_, _)).
645
+ directive_goal(license(_)).
646
+ directive_goal(Load) :- load_directive(Load, _).
647
+
648
+ directive_imports(Directive, File, Line, Bindings) :-
649
+ load_directive(Directive, Specs), !,
650
+ forall(member(Spec, Specs), import_spec(Spec, File, Line, Bindings, Directive)).
651
+ directive_imports(_, _, _, _).
652
+
653
+ import_spec(Spec, File, Line, Bindings, Directive) :-
654
+ ( ground(Spec)
655
+ -> normal_spec(Spec, Normal), text(Normal, Text), found(Text)
656
+ ; miss(File, Line, Bindings, (:- Directive))
657
+ ).
658
+
659
+ % library("utf8") and library('http/json') are written as library(utf8) and
660
+ % library(http/json).
661
+ normal_spec(Spec, Normal) :-
662
+ compound(Spec), Spec =.. [Alias, Arg], atom(Alias), \+ memberchk(Alias, [/, :, -]), !,
663
+ normal_path(Arg, Path), Normal =.. [Alias, Path].
664
+ normal_spec(Spec, Spec).
665
+
666
+ normal_path(Text, Path) :-
667
+ ( string(Text) ; atom(Text) ), !,
668
+ atom_string(Atom, Text),
669
+ atomic_list_concat(Parts, /, Atom),
670
+ ( Parts = [First|Rest], First \== '', \+ memberchk('', Rest)
671
+ -> foldl([P, A0, A1]>>(A1 = A0/P), Rest, First, Path)
672
+ ; Path = Atom
673
+ ).
674
+ normal_path(Dir/Name, D/N) :- !, normal_path(Dir, D), normal_path(Name, N).
675
+ normal_path(Path, Path).
676
+
677
+ % A grammar rule body: {Goal} is a goal, a non-terminal Name//N is the call
678
+ % Name/(N+2), and terminals call nothing, except a list element that is a
679
+ % message line whose format has ~@ (a message rule's lines are printed, and
680
+ % ~@ calls its argument). In the rules of the message hooks
681
+ % (prolog:message//1 and the rest) every element is read as a message line.
682
+ dcg_body(_, _, F, L, B, _, Body) :- var(Body), !, miss(F, L, B, phrase(Body)).
683
+ dcg_body(E, C, F, L, B, K, (X, Y)) :- !, dcg_body(E, C, F, L, B, K, X), dcg_body(E, C, F, L, B, K, Y).
684
+ dcg_body(E, C, F, L, B, K, (X ; Y)) :- !, dcg_body(E, C, F, L, B, K, X), dcg_body(E, C, F, L, B, K, Y).
685
+ dcg_body(E, C, F, L, B, K, (X | Y)) :- !, dcg_body(E, C, F, L, B, K, X), dcg_body(E, C, F, L, B, K, Y).
686
+ dcg_body(E, C, F, L, B, K, (X -> Y)) :- !, dcg_body(E, C, F, L, B, K, X), dcg_body(E, C, F, L, B, K, Y).
687
+ dcg_body(E, C, F, L, B, K, \+ X) :- !, dcg_body(E, C, F, L, B, K, X).
688
+ dcg_body(E, C, F, L, B, K, {Goal}) :- !, body(E, C, F, L, B, K, Goal).
689
+ dcg_body(_, _, _, _, _, _, !) :- !.
690
+ dcg_body(E, C, F, L, B, K, List) :- is_list(List), !,
691
+ forall(member(Item, List), dcg_terminal(E, C, F, L, B, K, List, Item)).
692
+ dcg_body(_, _, _, _, _, _, Text) :- string(Text), !.
693
+ dcg_body(E, C, F, L, B, K, call(Closure)) :- !, closure(E, C, F, L, B, K, 2, call(Closure), Closure).
694
+ dcg_body(E, C, F, L, B, K, Call) :-
695
+ Call =.. [call, Closure|Args], !,
696
+ length(Args, N), Extra is N + 2,
697
+ closure(E, C, F, L, B, K, Extra, Call, Closure).
698
+ dcg_body(E, C, F, L, B, K, Module:NonTerminal) :- !,
699
+ ( atom(Module), callable(NonTerminal)
700
+ -> extend(NonTerminal, 2, Called), qualified(E, C, F, L, B, K, Module, Called)
701
+ ; miss(F, L, B, Module:NonTerminal)
702
+ ).
703
+ dcg_body(E, C, F, L, B, K, NonTerminal) :-
704
+ callable(NonTerminal), !,
705
+ extend(NonTerminal, 2, Called),
706
+ body(E, C, F, L, B, K, Called).
707
+ dcg_body(_, _, F, L, B, _, Body) :- miss(F, L, B, Body).
708
+
709
+ dcg_terminal(E, C, F, L, B, K, List, Item) :-
710
+ ( in_message_rule
711
+ -> message_line(E, C, F, L, B, K, List, Item)
712
+ ; nonvar(Item), line_format(Item, Format), format_codes(Format, Codes),
713
+ phrase(format_directives(Uses), Codes), memberchk(goal, Uses)
714
+ -> message_line(E, C, F, L, B, K, List, Item)
715
+ ; true
716
+ ).
717
+
718
+ message_rule(Head) :-
719
+ nonvar(Head), Head = prolog:Inner, callable(Inner),
720
+ functor(Inner, Name, Arity),
721
+ memberchk(Name, [message, error_message, message_context, message_location, deprecated]),
722
+ memberchk(Arity, [1, 2]).
723
+
724
+ % The format of a message line element that is formatted with arguments.
725
+ line_format(Format-_, Format).
726
+ line_format(ansi(_, Format, _), Format).
727
+ line_format(ansi(_, Format, _, _), Format).
728
+ line_format(prefix(Inner), Format) :- nonvar(Inner), line_format(Inner, Format).
729
+ line_format(url(_, Label), Format) :- nonvar(Label), line_format(Label, Format).
730
+
731
+ % The message lines print_message_lines/3 prints: each element is read as a
732
+ % format call; a list Kibi cannot see to its end fails the read.
733
+ message_lines(_, _, F, L, B, _, Goal, Lines) :- var(Lines), !, miss(F, L, B, Goal).
734
+ message_lines(_, _, _, _, _, _, _, []) :- !.
735
+ message_lines(E, C, F, L, B, K, Goal, [Line|Lines]) :- !,
736
+ message_line(E, C, F, L, B, K, Goal, Line),
737
+ message_lines(E, C, F, L, B, K, Goal, Lines).
738
+ message_lines(_, _, F, L, B, _, Goal, _) :- miss(F, L, B, Goal).
739
+
740
+ % One message line element, as SWI-Prolog prints it: Format-Args,
741
+ % ansi(_, Format, Args), prefix(...) and url(_, Label) are formatted, nl,
742
+ % flush, url(Location), begin/2 and end/1 print no text of the program's, and
743
+ % any other element is a format without arguments.
744
+ message_line(_, _, F, L, B, _, Goal, Line) :- var(Line), !, miss(F, L, B, Goal).
745
+ message_line(E, C, F, L, B, K, Goal, Format-Args) :- !,
746
+ format_goals(E, C, F, L, B, K, Goal, Format, Args).
747
+ message_line(E, C, F, L, B, K, Goal, ansi(_, Format, Args)) :- !,
748
+ format_goals(E, C, F, L, B, K, Goal, Format, Args).
749
+ message_line(E, C, F, L, B, K, Goal, ansi(_, Format, Args, _)) :- !,
750
+ format_goals(E, C, F, L, B, K, Goal, Format, Args).
751
+ message_line(E, C, F, L, B, K, Goal, prefix(Inner)) :- !,
752
+ message_line(E, C, F, L, B, K, Goal, Inner).
753
+ message_line(E, C, F, L, B, K, Goal, url(_, Label)) :- !,
754
+ message_line(E, C, F, L, B, K, Goal, Label).
755
+ message_line(_, _, _, _, _, _, _, Line) :- message_data(Line), !.
756
+ message_line(E, C, F, L, B, K, Goal, Format) :-
757
+ format_goals(E, C, F, L, B, K, Goal, Format, []).
758
+
759
+ message_data(url(_)). message_data(begin(_, _)). message_data(end(_)).
760
+ message_data(nl). message_data(flush). message_data(full_stop). message_data(at_same_line).
761
+
762
+ % body(+Extract, +Callee, +File, +Line, +Bindings, +Catcher, +Goal)
763
+ body(_, _, File, Line, Bindings, _, Goal) :- var(Goal), !, miss(File, Line, Bindings, call(Goal)).
764
+ body(E, C, F, L, B, K, (A, Z)) :- !, body(E, C, F, L, B, K, A), body(E, C, F, L, B, K, Z).
765
+ body(E, C, F, L, B, K, (A ; Z)) :- !, body(E, C, F, L, B, K, A), body(E, C, F, L, B, K, Z).
766
+ body(E, C, F, L, B, K, (A -> Z)) :- !, body(E, C, F, L, B, K, A), body(E, C, F, L, B, K, Z).
767
+ body(E, C, F, L, B, K, (A *-> Z)) :- !, body(E, C, F, L, B, K, A), body(E, C, F, L, B, K, Z).
768
+ body(E, C, F, L, B, K, \+ A) :- !, body(E, C, F, L, B, K, A).
769
+ body(E, C, F, L, B, K, Module:Goal) :- !,
770
+ ( atom(Module), callable(Goal)
771
+ -> qualified(E, C, F, L, B, K, Module, Goal)
772
+ ; miss(F, L, B, Module:Goal)
773
+ ).
774
+ body(E, C, F, L, B, K, apply(Closure, Args)) :- !,
775
+ record_call(E, C, F, L, B, K, apply(Closure, Args)),
776
+ ( is_list(Args), apply_goal(Closure, Args, Goal)
777
+ -> body(E, C, F, L, B, K, Goal)
778
+ ; miss(F, L, B, apply(Closure, Args))
779
+ ).
780
+ body(E, C, F, L, B, K, catch(Goal, Catcher, Recovery)) :- !,
781
+ record_call(E, C, F, L, B, K, catch(Goal, Catcher, Recovery)),
782
+ body(E, C, F, L, B, K, Goal),
783
+ body(E, C, F, L, B, Catcher, Recovery).
784
+ body(E, C, F, L, B, K, Goal) :-
785
+ callable(Goal), !,
786
+ record_call(E, C, F, L, B, K, Goal),
787
+ meta_arguments(E, C, F, L, B, K, Goal).
788
+ body(_, _, F, L, B, _, Goal) :- miss(F, L, B, Goal).
789
+
790
+ qualified(E, C, F, L, B, K, Module, Goal) :-
791
+ functor(Goal, Name, Arity),
792
+ ( E == "calls" -> format(string(M), "~w:~w/~w", [Module, Name, Arity]), found(M)
793
+ ; record_call(E, C, F, L, B, K, Goal)
794
+ ),
795
+ meta_arguments(E, C, F, L, B, K, Goal).
796
+
797
+ % The arguments a meta-predicate calls, walked as goals (0) or closures (N).
798
+ meta_arguments(E, C, F, L, B, K, Goal) :-
799
+ Goal = print_message(_, Message), nonvar(Message), Message = format(Format, Args), !,
800
+ format_goals(E, C, F, L, B, K, Goal, Format, Args).
801
+ meta_arguments(E, C, F, L, B, K, Goal) :-
802
+ Goal = print_message_lines(_, _, Lines), \+ local_pred(print_message_lines, 3), !,
803
+ message_lines(E, C, F, L, B, K, Goal, Lines).
804
+ meta_arguments(E, C, F, L, B, K, Goal) :-
805
+ format_call(Goal, Format, Args), !,
806
+ format_goals(E, C, F, L, B, K, Goal, Format, Args).
807
+ meta_arguments(E, C, F, L, B, K, Goal) :-
808
+ meta_spec(F, Goal, Spec), !,
809
+ Goal =.. [_|Args], Spec =.. [_|Kinds],
810
+ forall(nth1(I, Kinds, Kind), meta_argument(E, C, F, L, B, K, Kind, Goal, I, Args)).
811
+ meta_arguments(_, _, _, _, _, _, _).
812
+
813
+ % Library predicates that format without declaring a meta argument.
814
+ format_call(ansi_format(_, Format, Args), Format, Args) :- \+ local_pred(ansi_format, 3).
815
+ format_call(sformat(_, Format, Args), Format, Args) :- \+ local_pred(sformat, 3).
816
+ format_call(sformat(_, Format), Format, []) :- \+ local_pred(sformat, 2).
817
+
818
+ meta_argument(E, C, F, L, B, K, Kind, Goal, I, Args) :-
819
+ nth1(I, Args, Arg),
820
+ ( Kind == 0
821
+ -> body(E, C, F, L, B, K, Arg)
822
+ ; Kind == ^
823
+ -> strip_carets(Arg, Inner), body(E, C, F, L, B, K, Inner)
824
+ ; Kind == //
825
+ -> grammar_argument(E, C, F, L, B, K, Goal, Arg)
826
+ ; integer(Kind)
827
+ -> closure(E, C, F, L, B, K, Kind, Goal, Arg)
828
+ ; Kind == (:)
829
+ -> colon_argument(E, C, F, L, B, K, Goal, I, Args, Arg)
830
+ ; true
831
+ ).
832
+
833
+ % A module-sensitive (:) argument: the format arguments of format/2,3 and
834
+ % debug/3 (a ~@ directive calls its argument), the goal lists of
835
+ % first_solution/3 and concurrent/3, and a lambda's body are read as goals;
836
+ % the clause, predicate, file and operator specs of the declarations and
837
+ % database predicates are data. Any other (:) argument that is not [] can
838
+ % hold a goal Kibi does not know how to find, and fails the read.
839
+ colon_argument(E, C, F, L, B, K, Goal, I, Args, Arg) :-
840
+ functor(Goal, Name, Arity),
841
+ ( format_argument(Name/Arity, I, At)
842
+ -> nth1(At, Args, Format), format_goals(E, C, F, L, B, K, Goal, Format, Arg)
843
+ ; goal_list_argument(Name/Arity, I)
844
+ -> ( is_list(Arg) -> forall(member(G, Arg), body(E, C, F, L, B, K, G)) ; miss(F, L, B, Goal) )
845
+ ; Name == (>>), I =:= 2
846
+ -> lambda_body(Arg, Body), body(E, C, F, L, B, K, Body)
847
+ ; colon_data(Name)
848
+ -> true
849
+ ; Arg == []
850
+ -> true
851
+ ; miss(F, L, B, Goal)
852
+ ).
853
+
854
+ format_argument(format/2, 2, 1).
855
+ format_argument(format/3, 3, 2).
856
+ format_argument(debug/3, 3, 2).
857
+ format_argument(pengine_format/2, 2, 1).
858
+
859
+ goal_list_argument(first_solution/3, 2).
860
+ goal_list_argument(concurrent/3, 2).
861
+
862
+ colon_data(Name) :-
863
+ memberchk(Name, [ assert, asserta, assertz, retract, retractall, clause, abolish,
864
+ incr_assert, incr_asserta, incr_assertz, incr_retract, incr_retractall,
865
+ compile_predicates, current_predicate, predicate_property, det,
866
+ discontiguous, dynamic, multifile, module_transparent, non_terminal,
867
+ public, table, untable, thread_local, volatile, noprofile, listing,
868
+ spy, nospy, trace, tracing, lock_predicate, unlock_predicate,
869
+ abolish_table_subgoals, abolish_table_call, abolish_table_pred,
870
+ current_table, rule, source_file, consult, ensure_loaded, load_files,
871
+ use_module, reexport, autoload, require, use_foreign_library,
872
+ load_foreign_library, qcompile, compile, reconsult, open_resource,
873
+ current_resource, op, current_op, push_op, push_operators ]).
874
+
875
+ % The goals a format string's ~@ directives call: each directive takes its
876
+ % arguments in order (~W two, ~* one more, ~n, ~t, ~|, ~+ and ~~ none). A
877
+ % format string Kibi cannot read fails the read; without ~@ no argument is a
878
+ % goal. Arguments that are not a list are one argument, as format/2 takes them.
879
+ format_goals(E, C, F, L, B, K, Goal, Format, Args0) :-
880
+ ( format_codes(Format, Codes), phrase(format_directives(Uses), Codes)
881
+ -> ( \+ memberchk(goal, Uses)
882
+ -> true
883
+ ; strip_args(Args0, Args), format_arguments(Args, List)
884
+ -> format_walk(E, C, F, L, B, K, Goal, Uses, List)
885
+ ; miss(F, L, B, Goal)
886
+ )
887
+ ; strip_args(Args0, Args), format_arguments(Args, List), maplist(plain_value, List)
888
+ -> true
889
+ ; miss(F, L, B, Goal)
890
+ ).
891
+
892
+ % A format argument that cannot be a goal, whatever the format string is.
893
+ plain_value(V) :- nonvar(V), ( number(V) ; string(V) ), !.
894
+
895
+ format_codes(Format, Codes) :-
896
+ nonvar(Format),
897
+ ( is_list(Format) -> catch(text_to_string(Format, S), _, fail) ; ( atom(Format) ; string(Format) ), S = Format ),
898
+ string_codes(S, Codes).
899
+
900
+ strip_args(Args, Args) :- var(Args), !.
901
+ strip_args(M:Args, Inner) :- atom(M), !, strip_args(Args, Inner).
902
+ strip_args(Args, Args).
903
+
904
+ format_arguments(Args, _) :- var(Args), !, fail.
905
+ format_arguments(Args, Args) :- is_list(Args), !.
906
+ format_arguments(Args, _) :- Args = [_|_], !, fail.
907
+ format_arguments(Args, [Args]).
908
+
909
+ format_walk(_, _, _, _, _, _, _, [], _) :- !.
910
+ format_walk(E, C, F, L, B, K, Goal, [Use|Uses], [Arg|Args]) :- !,
911
+ ( Use == goal -> body(E, C, F, L, B, K, Arg) ; true ),
912
+ format_walk(E, C, F, L, B, K, Goal, Uses, Args).
913
+ format_walk(_, _, F, L, B, _, Goal, _, _) :- miss(F, L, B, Goal).
914
+
915
+ format_directives(Uses) --> [0'~], !, format_column(Star), format_colon, [D],
916
+ { format_directive(D, Own), append(Star, Own, These) },
917
+ format_directives(Rest), { append(These, Rest, Uses) }.
918
+ format_directives(Uses) --> [_], !, format_directives(Uses).
919
+ format_directives([]) --> [].
920
+
921
+ format_column([arg]) --> [0'*], !.
922
+ format_column([]) --> [96], !, [_].
923
+ format_column([]) --> [D], { code_type(D, digit) }, !, format_digits.
924
+ format_column([]) --> [].
925
+ format_digits --> [D], { code_type(D, digit) }, !, format_digits.
926
+ format_digits --> [].
927
+ format_colon --> [0':], !.
928
+ format_colon --> [].
929
+
930
+ format_directive(0'@, [goal]) :- !.
931
+ format_directive(0'W, [arg, arg]) :- !.
932
+ format_directive(D, []) :- memberchk(D, [0'~, 0'n, 0't, 0'|, 0'+]), !.
933
+ format_directive(D, [arg]) :- memberchk(D, [0'a, 0'c, 0'd, 0'D, 0'e, 0'f, 0'g, 0'h, 0'i, 0'I, 0'k, 0'p, 0'q, 0'r, 0'R, 0's, 0'w]).
934
+
935
+ % A grammar body handed to phrase/2,3 or call_dcg/3: read as a rule body,
936
+ % so {Goal} is the goal it calls.
937
+ grammar_argument(_, _, F, L, B, _, Goal, Arg) :- var(Arg), !, miss(F, L, B, Goal).
938
+ grammar_argument(E, C, F, L, B, K, Goal, Arg) :- Arg = _>>_, !, closure(E, C, F, L, B, K, 2, Goal, Arg).
939
+ grammar_argument(E, C, F, L, B, K, _, Arg) :- dcg_body(E, C, F, L, B, K, Arg).
940
+
941
+ strip_carets(V, V) :- var(V), !.
942
+ strip_carets(_^G, I) :- !, strip_carets(G, I).
943
+ strip_carets(G, G).
944
+
945
+ % A closure called with Extra more arguments: a lambda's body is walked, a
946
+ % named closure is the call it makes once the arguments are added.
947
+ closure(_, _, F, L, B, _, _, Goal, Closure) :- var(Closure), !, miss(F, L, B, Goal).
948
+ closure(E, C, F, L, B, K, _, _, Params>>Lambda) :- !,
949
+ ( E == "imports", autoloaded(F, Params>>Lambda, Spec) -> text(Spec, Text), found(Text) ; true ),
950
+ lambda_body(Lambda, Body), body(E, C, F, L, B, K, Body).
951
+ closure(E, C, F, L, B, K, Extra, Goal, Module:Closure) :- !,
952
+ ( atom(Module), callable(Closure)
953
+ -> extend(Closure, Extra, Called), qualified(E, C, F, L, B, K, Module, Called)
954
+ ; miss(F, L, B, Goal)
955
+ ).
956
+ closure(E, C, F, L, B, K, Extra, _, Closure) :-
957
+ callable(Closure), !,
958
+ extend(Closure, Extra, Called),
959
+ body(E, C, F, L, B, K, Called).
960
+ closure(_, _, F, L, B, _, _, Goal, _) :- miss(F, L, B, Goal).
961
+
962
+ % apply(Closure, Args) calls Closure with Args added.
963
+ apply_goal(Closure, Args, Module:Goal) :-
964
+ nonvar(Closure), Closure = Module:Inner, !,
965
+ apply_goal(Inner, Args, Goal).
966
+ apply_goal(Closure, Args, Goal) :-
967
+ callable(Closure),
968
+ Closure =.. List, append(List, Args, Full), Goal =.. Full.
969
+
970
+ lambda_body(_/Body, Body) :- !.
971
+ lambda_body(Body, Body).
972
+
973
+ extend(Closure, Extra, Called) :-
974
+ Closure =.. List,
975
+ length(More, Extra),
976
+ append(List, More, Full),
977
+ Called =.. Full.
978
+
979
+ % A predicate's meta_predicate declaration: the files' own, then those of
980
+ % the project modules they load, then SWI-Prolog's (autoloadable ones too)
981
+ % and those of the libraries the file loads.
982
+ meta_spec(File, Goal, Spec) :-
983
+ functor(Goal, Name, Arity),
984
+ ( local_meta(Name/Arity, Spec)
985
+ -> true
986
+ ; local_pred(Name, Arity)
987
+ -> fail
988
+ ; loaded_meta(Name/Arity, Spec)
989
+ -> true
990
+ ; catch(predicate_property(kibi_source_reader:Goal, meta_predicate(Spec)), _, fail)
991
+ -> true
992
+ ; file_module(File, Module), current_predicate(Module:Name/Arity),
993
+ catch(predicate_property(Module:Goal, meta_predicate(Spec)), _, fail)
994
+ ).
995
+
996
+ % A clause asserted from a literal term is read like one in the file: its
997
+ % body is walked. One asserted from data, or one that defines an expansion,
998
+ % is not known statically and is refused.
999
+ data_goal(assert(_)). data_goal(asserta(_)). data_goal(assertz(_)).
1000
+ data_goal(assert(_, _)). data_goal(asserta(_, _)). data_goal(assertz(_, _)).
1001
+ data_goal(compile_aux_clauses(_)).
1002
+
1003
+ record_call(E, C, F, L, B, K, Goal) :-
1004
+ ( load_directive(Goal, _), load_miss(Goal, F)
1005
+ -> load_refusal(F, L, B, Goal)
1006
+ ; true
1007
+ ),
1008
+ ( data_goal(Goal)
1009
+ -> arg(1, Goal, Arg),
1010
+ ( Goal = compile_aux_clauses(_), is_list(Arg) -> Clauses = Arg ; Clauses = [Arg] ),
1011
+ forall(member(Clause, Clauses), asserted(E, C, F, L, B, Goal, Clause))
1012
+ ; true
1013
+ ),
1014
+ extract(E, C, F, L, B, K, Goal).
1015
+
1016
+ asserted(E, C, F, L, B, Goal, Clause) :-
1017
+ ( static_clause(Clause, Head, Body), \+ expansion_head(Head)
1018
+ -> ( Body == true -> true ; body(E, C, F, L, B, none, Body) )
1019
+ ; miss(F, L, B, Goal)
1020
+ ).
1021
+
1022
+ static_clause(Clause, _, _) :- var(Clause), !, fail.
1023
+ static_clause(Module:Clause, Head, Body) :- !, atom(Module), static_clause(Clause, Head, Body).
1024
+ static_clause((Head :- Body), Head, Body) :- !, callable(Head), nonvar(Body).
1025
+ static_clause(Head, Head, true) :- callable(Head).
1026
+
1027
+ control(call/_).
1028
+ control(','/2). control(';'/2). control('->'/2). control('*->'/2). control('\\+'/1).
1029
+
1030
+ extract("calls", _, _, _, _, _, Goal) :- !,
1031
+ functor(Goal, Name, Arity),
1032
+ ( control(Name/Arity) -> true
1033
+ ; format(string(M), "~w/~w", [Name, Arity]), found(M)
1034
+ ).
1035
+ extract("env", _, F, L, B, _, Goal) :-
1036
+ Goal = getenv(Name, _), !,
1037
+ ( atomic(Name) -> text(Name, T), found(T) ; miss(F, L, B, Goal) ).
1038
+ extract("string_args", Callee, F, L, B, _, Goal) :-
1039
+ callee_matches(Callee, Goal), !,
1040
+ arg(1, Goal, First),
1041
+ ( ( atom(First) ; string(First) ) -> text(First, T), found(T) ; miss(F, L, B, Goal) ).
1042
+ extract("thrown", _, F, L, B, Catcher, Goal) :-
1043
+ Goal = throw(Ball), !,
1044
+ ( Ball == Catcher, Catcher \== none
1045
+ -> true
1046
+ ; var(Ball)
1047
+ -> miss(F, L, B, Goal)
1048
+ ; thrown_name(Ball, Name)
1049
+ -> found(Name)
1050
+ ; miss(F, L, B, Goal)
1051
+ ).
1052
+ extract("thrown", _, _, _, _, _, Goal) :-
1053
+ Goal = must_be(_, _), !,
1054
+ % library(error)'s must_be/2 throws these when the value is not of the type.
1055
+ forall(member(Error, ["type_error", "domain_error", "instantiation_error", "existence_error"]), found(Error)).
1056
+ extract("thrown", _, _, _, _, _, Goal) :-
1057
+ functor(Goal, Name, Arity),
1058
+ error_helper(Name/Arity), !,
1059
+ atom_string(Name, Text), found(Text).
1060
+ extract("imports", _, F, L, B, _, Goal) :-
1061
+ load_directive(Goal, _), \+ is_list(Goal), !,
1062
+ directive_imports(Goal, F, L, B).
1063
+ extract("imports", _, F, _, _, _, Goal) :-
1064
+ autoloaded(F, Goal, Spec), !,
1065
+ text(Spec, Text), found(Text).
1066
+ extract(_, _, _, _, _, _, _).
1067
+
1068
+ % The library SWI-Prolog autoloads for a call the file makes: one it does
1069
+ % not define, that no library or project module it loads gives it, and that
1070
+ % is not built in.
1071
+ autoloaded(File, Goal, Spec) :-
1072
+ callable(Goal), functor(Goal, Name, Arity),
1073
+ \+ control(Name/Arity),
1074
+ \+ local_pred(Name, Arity),
1075
+ \+ loaded_export(Name/Arity),
1076
+ file_module(File, Module),
1077
+ \+ current_predicate(Module:Name/Arity),
1078
+ catch('$find_library'(_, Name, Arity, _, Library), _, fail),
1079
+ file_name_on_path(Library, Spec).
1080
+
1081
+ thrown_name(error(Formal, _), Name) :- !, nonvar(Formal), functor(Formal, N, _), atom_string(N, Name).
1082
+ thrown_name(Ball, Name) :- functor(Ball, N, _), atom_string(N, Name).
1083
+
1084
+ error_helper(type_error/2). error_helper(domain_error/2). error_helper(existence_error/2).
1085
+ error_helper(existence_error/3). error_helper(permission_error/3). error_helper(instantiation_error/1).
1086
+ error_helper(uninstantiation_error/1). error_helper(representation_error/1).
1087
+ error_helper(resource_error/1). error_helper(syntax_error/1).
1088
+
1089
+ callee_matches(Callee, Goal) :-
1090
+ functor(Goal, Name, Arity),
1091
+ Arity >= 1,
1092
+ ( atom_string(Name, Callee)
1093
+ -> true
1094
+ ; format(string(Indicator), "~w/~w", [Name, Arity]), Indicator == Callee
1095
+ ).
1096
+
1097
+ % ------------------------------------------------------------------------
1098
+ % The terms reader.
1099
+
1100
+ functor_spec(Text, Name, Arity) :-
1101
+ catch(term_string(Term, Text), _, fail),
1102
+ Term = Name/Arity, atom(Name), integer(Arity), Arity >= 0, !.
1103
+ functor_spec(Text, _, _) :- refuse("functor ~s is not Name/Arity", [Text]).
1104
+
1105
+ argument_index(default, _, 0, none) :- !.
1106
+ argument_index(default, _, 1, 1) :- !.
1107
+ argument_index(default, Text, Arity, _) :- !,
1108
+ refuse("~s has ~w arguments; set arg to the one that holds each member", [Text, Arity]).
1109
+ argument_index(Index, Text, Arity, Index) :-
1110
+ ( integer(Index), Index >= 1, Index =< Arity -> true
1111
+ ; refuse("arg ~w is not an argument of ~s", [Index, Text])
1112
+ ).
1113
+
1114
+ term_value(none, Term, Name) :- !, functor(Term, Name, _).
1115
+ term_value(Index, Term, Value) :- arg(Index, Term, Value).
1116
+
1117
+ term_members(Value, Line, File, _) :-
1118
+ \+ ground(Value), !,
1119
+ refuse("~s:~w holds a value with a variable, which is not a member", [File, Line]).
1120
+ term_members(Value, _, _, Text) :-
1121
+ is_list(Value), !, member(Item, Value), text(Item, Text).
1122
+ term_members(Value, _, _, Text) :- text(Value, Text).
1123
+ `;