kibi-cli 2.38.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.
- package/dist/cli-json-command.d.ts.map +1 -1
- package/dist/cli-json-command.js +4 -0
- package/dist/cli-register-foundation.js +2 -2
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +33 -9
- package/dist/commands/coverage.d.ts +1 -0
- package/dist/commands/coverage.d.ts.map +1 -1
- package/dist/commands/coverage.js +40 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +29 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -0
- package/dist/commands/migrate-plan-record.d.ts +18 -0
- package/dist/commands/migrate-plan-record.d.ts.map +1 -0
- package/dist/commands/migrate-plan-record.js +112 -0
- package/dist/commands/migrate.d.ts +11 -2
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +60 -8
- package/dist/commands/prove.d.ts.map +1 -1
- package/dist/commands/prove.js +18 -3
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +17 -4
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +6 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +8 -0
- package/dist/commands/telemetry.d.ts.map +1 -1
- package/dist/commands/telemetry.js +12 -2
- package/dist/extractors/markdown.d.ts +6 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +21 -0
- package/dist/extractors/symbol-title-match.d.ts +31 -0
- package/dist/extractors/symbol-title-match.d.ts.map +1 -0
- package/dist/extractors/symbol-title-match.js +133 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +81 -12
- package/dist/operations/bootstrap/discovery-evidence.d.ts.map +1 -1
- package/dist/operations/bootstrap/discovery-evidence.js +44 -2
- package/dist/operations/bootstrap/discovery.d.ts.map +1 -1
- package/dist/operations/bootstrap/discovery.js +1 -0
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +12 -1
- package/dist/operations/bootstrap/plugin-offers.d.ts +7 -1
- package/dist/operations/bootstrap/plugin-offers.d.ts.map +1 -1
- package/dist/operations/bootstrap/plugin-offers.js +73 -3
- package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
- package/dist/operations/modeling/logic-modeling.js +2 -1
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +2 -1
- package/dist/operations/mutation/symbol-refresh.d.ts +2 -0
- package/dist/operations/mutation/symbol-refresh.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-refresh.js +6 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +7 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +2 -1
- package/dist/operations/proof/claim-bindings.d.ts +11 -0
- package/dist/operations/proof/claim-bindings.d.ts.map +1 -1
- package/dist/operations/proof/claim-bindings.js +144 -1
- package/dist/operations/proof/code-certificates.d.ts +10 -3
- package/dist/operations/proof/code-certificates.d.ts.map +1 -1
- package/dist/operations/proof/code-certificates.js +65 -13
- package/dist/operations/proof/code-languages.d.ts +104 -0
- package/dist/operations/proof/code-languages.d.ts.map +1 -0
- package/dist/operations/proof/code-languages.js +119 -0
- package/dist/operations/proof/code-models.d.ts +2 -0
- package/dist/operations/proof/code-models.d.ts.map +1 -1
- package/dist/operations/proof/code-models.js +11 -5
- package/dist/operations/proof/code-runner.d.ts +12 -9
- package/dist/operations/proof/code-runner.d.ts.map +1 -1
- package/dist/operations/proof/code-runner.js +81 -38
- package/dist/operations/proof/code-scope.d.ts +8 -0
- package/dist/operations/proof/code-scope.d.ts.map +1 -1
- package/dist/operations/proof/code-scope.js +30 -1
- package/dist/operations/proof/explain.d.ts +76 -0
- package/dist/operations/proof/explain.d.ts.map +1 -1
- package/dist/operations/proof/explain.js +228 -0
- package/dist/operations/proof/ingest-proof.d.ts +2 -0
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +1 -0
- package/dist/operations/proof/java-package-shadow.d.ts +14 -0
- package/dist/operations/proof/java-package-shadow.d.ts.map +1 -0
- package/dist/operations/proof/java-package-shadow.js +389 -0
- package/dist/operations/proof/prolog-evaluator.d.ts +14 -0
- package/dist/operations/proof/prolog-evaluator.d.ts.map +1 -0
- package/dist/operations/proof/prolog-evaluator.js +247 -0
- package/dist/operations/proof/prolog-source-reader.d.ts +20 -0
- package/dist/operations/proof/prolog-source-reader.d.ts.map +1 -0
- package/dist/operations/proof/prolog-source-reader.js +1123 -0
- package/dist/operations/proof/source-code-readers.d.ts +6 -0
- package/dist/operations/proof/source-code-readers.d.ts.map +1 -1
- package/dist/operations/proof/source-code-readers.js +879 -75
- package/dist/operations/semantic-advisor/analyze-prose.d.ts +9 -1
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +21 -0
- package/dist/operations/semantic-advisor/case-preview.d.ts +24 -0
- package/dist/operations/semantic-advisor/case-preview.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/case-preview.js +216 -0
- package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/shared.js +3 -0
- package/dist/operations/semantic-advisor/types.d.ts +23 -0
- package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
- package/dist/plugins/approved-source-analyzers.generated.d.ts +15 -34
- package/dist/plugins/approved-source-analyzers.generated.d.ts.map +1 -1
- package/dist/plugins/approved-source-analyzers.generated.js +15 -34
- package/dist/plugins/plugin-install-check.d.ts +21 -0
- package/dist/plugins/plugin-install-check.d.ts.map +1 -0
- package/dist/plugins/plugin-install-check.js +62 -0
- package/dist/plugins/proof-reference-extractors.d.ts +20 -0
- package/dist/plugins/proof-reference-extractors.d.ts.map +1 -0
- package/dist/plugins/proof-reference-extractors.js +104 -0
- package/dist/plugins/reference-language-suggestion.d.ts +51 -0
- package/dist/plugins/reference-language-suggestion.d.ts.map +1 -0
- package/dist/plugins/reference-language-suggestion.js +277 -0
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +3 -1
- package/dist/plugins/source-analysis-service.d.ts.map +1 -1
- package/dist/plugins/source-analysis-service.js +4 -0
- package/dist/plugins/source-change-analysis.d.ts +26 -1
- package/dist/plugins/source-change-analysis.d.ts.map +1 -1
- package/dist/plugins/source-change-analysis.js +77 -5
- package/dist/plugins/source-classification.d.ts +2 -2
- package/dist/plugins/source-classification.d.ts.map +1 -1
- package/dist/plugins/source-classification.js +45 -1
- package/dist/public/operations/catalog.d.ts +62 -7
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +3 -0
- package/dist/public/operations/evidence-floor.d.ts +8 -0
- package/dist/public/operations/evidence-floor.d.ts.map +1 -1
- package/dist/public/operations/evidence-floor.js +19 -1
- package/dist/public/operations/repair-plan.d.ts +46 -0
- package/dist/public/operations/repair-plan.d.ts.map +1 -1
- package/dist/public/operations/repair-plan.js +110 -1
- package/dist/public/operations/schema6-check-actions.js +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.js +3 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/mutation-schemas.d.ts +61 -6
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/mutation-schemas.js +30 -0
- package/dist/public/operations/specs/reporting.d.ts +8 -0
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +26 -3
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +2 -1
- package/dist/public/operations/specs/upsert.d.ts +61 -6
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/operations/symbol-coordinates-check.d.ts +11 -0
- package/dist/public/operations/symbol-coordinates-check.d.ts.map +1 -0
- package/dist/public/operations/symbol-coordinates-check.js +66 -0
- package/dist/public/proof-protocol.d.ts +31 -6
- package/dist/public/proof-protocol.d.ts.map +1 -1
- package/dist/public/proof-protocol.js +45 -15
- package/dist/public/proof-sources.d.ts +4 -2
- package/dist/public/proof-sources.d.ts.map +1 -1
- package/dist/public/proof-sources.js +43 -4
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +59 -0
- package/dist/public/skills/kibi-commit-review/SKILL.md +6 -2
- package/dist/public/skills/kibi-decisions/SKILL.md +7 -2
- package/dist/public/skills/kibi-decisions/resources/signoff-views.md +3 -1
- package/dist/public/skills/kibi-usage/SKILL.md +14 -3
- package/dist/public/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/dist/public/skills/kibi-usage/resources/proof.md +44 -3
- package/dist/schemas/entity.schema.json +126 -6
- package/dist/search-answer.d.ts +3 -2
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +56 -3
- package/dist/traceability/analysis-gate.d.ts +3 -1
- package/dist/traceability/analysis-gate.d.ts.map +1 -1
- package/dist/traceability/analysis-gate.js +9 -1
- package/dist/traceability/impact-knowledge.d.ts.map +1 -1
- package/dist/traceability/impact-knowledge.js +2 -0
- package/dist/traceability/staged-file-coverage.d.ts.map +1 -1
- package/dist/traceability/staged-file-coverage.js +7 -2
- package/dist/traceability/symbol-extract.d.ts +17 -0
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +69 -15
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +11 -1
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +5 -7
- package/dist/types/entities.d.ts +12 -0
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/usage-telemetry.d.ts +9 -0
- package/dist/usage-telemetry.d.ts.map +1 -1
- package/dist/usage-telemetry.js +50 -4
- package/dist/utils/first-sentence.d.ts +9 -0
- package/dist/utils/first-sentence.d.ts.map +1 -0
- package/dist/utils/first-sentence.js +52 -0
- package/dist/utils/rule-registry.generated.d.ts +11 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +13 -0
- package/package.json +5 -5
- package/schema/entities.pl +12 -0
- package/src/public/operations/check-executor.ts +8 -0
- package/src/public/operations/evidence-floor.ts +29 -1
- package/src/public/operations/repair-plan.ts +152 -1
- package/src/public/operations/schema6-check-actions.ts +1 -1
- package/src/public/operations/specs/check-rules.generated.ts +3 -1
- package/src/public/operations/specs/mutation-schemas.ts +34 -0
- package/src/public/operations/specs/reporting.ts +38 -3
- package/src/public/operations/specs/semantic.ts +2 -1
- package/src/public/operations/symbol-coordinates-check.ts +79 -0
- package/src/public/proof-protocol-types.d.ts +22 -5
- package/src/public/proof-protocol.ts +78 -19
- package/src/public/proof-sources.ts +51 -3
- package/src/public/schemas/entity.ts +65 -0
- package/src/public/skills/kibi-commit-review/SKILL.md +6 -2
- package/src/public/skills/kibi-decisions/SKILL.md +7 -2
- package/src/public/skills/kibi-decisions/resources/signoff-views.md +3 -1
- package/src/public/skills/kibi-usage/SKILL.md +14 -3
- package/src/public/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/src/public/skills/kibi-usage/resources/proof.md +44 -3
- package/src/schemas/entity.schema.json +126 -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
|
+
`;
|