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,389 @@
|
|
|
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
|
+
* Names another file of the project may bind instead of the standard one.
|
|
20
|
+
* The tree-sitter reference reader reads one file at a time, so it marks:
|
|
21
|
+
*
|
|
22
|
+
* - a bare Java `System` or `Runtime` ("System may be a class of package
|
|
23
|
+
* p: ..."): a top-level type of that name in another file of the package
|
|
24
|
+
* would be used instead of java.lang's;
|
|
25
|
+
* - a bare Rust standard macro ("println may be a macro_rules! macro of the
|
|
26
|
+
* crate: ..."): a `macro_rules! println` elsewhere in the crate, in
|
|
27
|
+
* textual scope through `#[macro_use]`, would be expanded instead;
|
|
28
|
+
* - a C# System type spelled bare ("Environment may be a type of the
|
|
29
|
+
* project: ..."): a type of that name in the file's namespace, an
|
|
30
|
+
* enclosing one or the global namespace, declared in any file of the
|
|
31
|
+
* project, or a `global using` alias of that name, would bind instead of
|
|
32
|
+
* System's;
|
|
33
|
+
* - a Go name the file uses but does not declare ("getenv may be declared in
|
|
34
|
+
* another file of the package: ..."), for env: another file of the package
|
|
35
|
+
* may hold a getter or a wrapper of one;
|
|
36
|
+
* - a Go name the file calls but does not declare ("command may be a
|
|
37
|
+
* function of another file of the package: ..."), for calls: another file
|
|
38
|
+
* of the package may bind it to a function value (`var command =
|
|
39
|
+
* exec.Command`), so the call is that function's;
|
|
40
|
+
* - a Go import ("config may be a package of the project: ..."), for env: a
|
|
41
|
+
* package of the project may wrap a getter (`config.Get("X")`, `var Get =
|
|
42
|
+
* os.Getenv`) or read the environment at init.
|
|
43
|
+
*
|
|
44
|
+
* The host drops such a row only after it has looked at those files and
|
|
45
|
+
* found no such declaration. When it finds one, or cannot read the files,
|
|
46
|
+
* the row stays and the read is refused.
|
|
47
|
+
*/
|
|
48
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
49
|
+
import path from "node:path";
|
|
50
|
+
const JAVA_ROW = /^(System|Runtime) may be a class of (?:package ([\w.]+)|the default package): /;
|
|
51
|
+
const RUST_ROW = /^(\w+) may be a macro_rules! macro of the crate: /;
|
|
52
|
+
const CSHARP_ROW = /^(\w+) may be a type of the project: /;
|
|
53
|
+
const GO_ROW = /^(\w+) may be declared in another file of the package: /;
|
|
54
|
+
const GO_IMPORT_ROW = /^\S+ may be a package of the project: /;
|
|
55
|
+
const GO_CALL_ROW = /^(\w+) may be a function of another file of the package: /;
|
|
56
|
+
/**
|
|
57
|
+
* What in Go source may read the environment: a getter of os, syscall or
|
|
58
|
+
* x/sys (also as a value, or through an alias or a dot import), cgo, and a
|
|
59
|
+
* `//go:linkname`, which binds a local name to any function.
|
|
60
|
+
*/
|
|
61
|
+
const GO_ENV_SOURCE = /\b(?:Getenv|LookupEnv|Environ|ExpandEnv|Expand)\b|"C"|go:linkname/;
|
|
62
|
+
/** Java source with comments, string, char and text-block contents removed. */
|
|
63
|
+
export function javaCode(text) {
|
|
64
|
+
let out = "";
|
|
65
|
+
let index = 0;
|
|
66
|
+
while (index < text.length) {
|
|
67
|
+
const rest = text.slice(index, index + 3);
|
|
68
|
+
if (rest.startsWith("//")) {
|
|
69
|
+
const end = text.indexOf("\n", index);
|
|
70
|
+
index = end === -1 ? text.length : end;
|
|
71
|
+
out += " ";
|
|
72
|
+
}
|
|
73
|
+
else if (rest.startsWith("/*")) {
|
|
74
|
+
const end = text.indexOf("*/", index + 2);
|
|
75
|
+
index = end === -1 ? text.length : end + 2;
|
|
76
|
+
out += " ";
|
|
77
|
+
}
|
|
78
|
+
else if (rest === '"""') {
|
|
79
|
+
const end = text.indexOf('"""', index + 3);
|
|
80
|
+
index = end === -1 ? text.length : end + 3;
|
|
81
|
+
out += '""';
|
|
82
|
+
}
|
|
83
|
+
else if (rest[0] === '"' || rest[0] === "'") {
|
|
84
|
+
const quote = rest[0];
|
|
85
|
+
index += 1;
|
|
86
|
+
while (index < text.length && text[index] !== quote && text[index] !== "\n")
|
|
87
|
+
index += text[index] === "\\" ? 2 : 1;
|
|
88
|
+
index += 1;
|
|
89
|
+
out += `${quote}${quote}`;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
out += text[index];
|
|
93
|
+
index += 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Java source with its Unicode escapes decoded (`q` is `q`), as the
|
|
100
|
+
* compiler reads it before anything else; a backslash escaped by another
|
|
101
|
+
* starts no escape.
|
|
102
|
+
*/
|
|
103
|
+
function javaUnicode(text) {
|
|
104
|
+
return text.replace(/(\\+)u+([0-9a-fA-F]{4})/g, (escape, slashes, hex) => slashes.length % 2 === 1 ? `${slashes.slice(1)}${String.fromCharCode(Number.parseInt(hex, 16))}` : escape);
|
|
105
|
+
}
|
|
106
|
+
/** The package a Java file declares, "" for the default package. */
|
|
107
|
+
function javaPackage(text) {
|
|
108
|
+
const declared = /\bpackage\s+([\w$.\s]+?)\s*;/.exec(javaCode(javaUnicode(text)));
|
|
109
|
+
return declared === null ? "" : declared[1].replace(/\s+/g, "");
|
|
110
|
+
}
|
|
111
|
+
/** Whether Java source declares a type of that name (any nesting). */
|
|
112
|
+
function javaDeclares(text, name) {
|
|
113
|
+
// A Unicode escape may spell any identifier: the file is not ruled out.
|
|
114
|
+
if (text.includes("\\u"))
|
|
115
|
+
return true;
|
|
116
|
+
return new RegExp(`(?:^|[^\\w$.])(?:class|interface|enum|record)\\s+${name}(?![\\w$])`).test(javaCode(text));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Source files with an extension under a directory, skipping dot folders,
|
|
120
|
+
* node_modules and the named build folders.
|
|
121
|
+
*/
|
|
122
|
+
function sourceFiles(directory, extension, skipped = []) {
|
|
123
|
+
const files = [];
|
|
124
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
125
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || skipped.includes(entry.name))
|
|
126
|
+
continue;
|
|
127
|
+
const full = path.join(directory, entry.name);
|
|
128
|
+
if (entry.isDirectory())
|
|
129
|
+
files.push(...sourceFiles(full, extension, skipped));
|
|
130
|
+
else if (entry.name.endsWith(extension))
|
|
131
|
+
files.push(full);
|
|
132
|
+
}
|
|
133
|
+
return files;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Whether C# source may declare a type, namespace or `global using` alias of
|
|
137
|
+
* that name. Comments and strings are not stripped (a mention there keeps
|
|
138
|
+
* the row too), and an identifier spelled with a Unicode escape may be any
|
|
139
|
+
* name.
|
|
140
|
+
*/
|
|
141
|
+
function csharpDeclares(text, name) {
|
|
142
|
+
const id = `@?${name}(?![\\w\\\\])`;
|
|
143
|
+
return (new RegExp(`\\b(?:class|struct|interface|enum|record)\\s+(?:(?:class|struct)\\s+)?${id}`).test(text) ||
|
|
144
|
+
new RegExp(`\\bdelegate\\s+[^;{}()]*?[\\s>\\]?]${id}\\s*[<(]`).test(text) ||
|
|
145
|
+
new RegExp(`\\bnamespace\\s+(?:[\\w@]+\\s*\\.\\s*)*${id}`).test(text) ||
|
|
146
|
+
new RegExp(`\\bglobal\\s+using\\s+${id}\\s*=`).test(text) ||
|
|
147
|
+
/\b(?:class|struct|interface|enum|record|delegate)\b[^;{}]*?\\[uU]/.test(text));
|
|
148
|
+
}
|
|
149
|
+
/** Go string literals, interpreted and raw: every import path a file may spell. */
|
|
150
|
+
function goStrings(text) {
|
|
151
|
+
return [...text.matchAll(/"((?:[^"\\\n]|\\.)*)"|`([^`]*)`/g)].map((match) => match[1] ?? match[2] ?? "");
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The project's Go modules: each go.mod's module path at its folder (which
|
|
155
|
+
* covers the modules of a go.work), and each replace with a local folder.
|
|
156
|
+
*/
|
|
157
|
+
function goModules(workspaceRoot) {
|
|
158
|
+
const modules = [];
|
|
159
|
+
for (const file of sourceFiles(workspaceRoot, "go.mod", ["vendor"])) {
|
|
160
|
+
if (path.basename(file) !== "go.mod")
|
|
161
|
+
continue;
|
|
162
|
+
const dir = path.dirname(file);
|
|
163
|
+
const text = readFileSync(file, "utf8").replace(/\/\/[^\n]*/g, "");
|
|
164
|
+
const name = /^\s*module\s+"?([^\s"]+)"?/m.exec(text);
|
|
165
|
+
if (name !== null)
|
|
166
|
+
modules.push({ path: name[1], dir });
|
|
167
|
+
// `replace a v1 => ../a`, alone or in a `replace ( ... )` block.
|
|
168
|
+
for (const replace of text.matchAll(/^\s*(?:replace\s+)?"?([^\s"(]+)"?(?:\s+\S+)?\s*=>\s*"?((?:\.\.?|\/)[^\s"]*)"?/gm))
|
|
169
|
+
modules.push({ path: replace[1], dir: path.resolve(dir, replace[2]) });
|
|
170
|
+
}
|
|
171
|
+
return modules;
|
|
172
|
+
}
|
|
173
|
+
/** Whether a go.mod at or above `directory`, within the workspace, puts it in module mode. */
|
|
174
|
+
function goModuleMode(workspaceRoot, directory) {
|
|
175
|
+
for (let current = directory;; current = path.dirname(current)) {
|
|
176
|
+
try {
|
|
177
|
+
if (statSync(path.join(current, "go.mod")).isFile())
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// no go.mod here
|
|
182
|
+
}
|
|
183
|
+
if (path.relative(workspaceRoot, current) === "" || path.dirname(current) === current)
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Which Go packages are the project's, and whether one can read the
|
|
189
|
+
* environment. Folder scans and go.mod files are read once per filter.
|
|
190
|
+
*/
|
|
191
|
+
function createGoReach(workspaceRoot) {
|
|
192
|
+
let modules;
|
|
193
|
+
const reached = new Map();
|
|
194
|
+
/**
|
|
195
|
+
* The folder an import path names when it is the project's: relative,
|
|
196
|
+
* under a module path or local replace of the project (the longest
|
|
197
|
+
* wins), or, without a go.mod (GOPATH mode), that path under one of the
|
|
198
|
+
* importer's enclosing folders. Null for another package (the standard
|
|
199
|
+
* library's, a third-party one); "" when the project's go.mod files
|
|
200
|
+
* cannot be read.
|
|
201
|
+
*/
|
|
202
|
+
const resolve = (spelled, from) => {
|
|
203
|
+
if (/^\.\.?(?:\/|$)/.test(spelled))
|
|
204
|
+
return path.resolve(from, spelled);
|
|
205
|
+
if (goModuleMode(workspaceRoot, from)) {
|
|
206
|
+
if (modules === undefined) {
|
|
207
|
+
try {
|
|
208
|
+
modules = goModules(workspaceRoot);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
modules = null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (modules === null)
|
|
215
|
+
return "";
|
|
216
|
+
let best = null;
|
|
217
|
+
for (const module of modules)
|
|
218
|
+
if ((spelled === module.path || spelled.startsWith(`${module.path}/`)) &&
|
|
219
|
+
(best === null || module.path.length > best.path.length))
|
|
220
|
+
best = module;
|
|
221
|
+
return best === null ? null : path.join(best.dir, spelled.slice(best.path.length));
|
|
222
|
+
}
|
|
223
|
+
for (let current = from;; current = path.dirname(current)) {
|
|
224
|
+
const candidate = path.join(current, spelled);
|
|
225
|
+
try {
|
|
226
|
+
if (statSync(candidate).isDirectory())
|
|
227
|
+
return candidate;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// not here
|
|
231
|
+
}
|
|
232
|
+
if (path.relative(workspaceRoot, current) === "" || path.dirname(current) === current)
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Whether a package of the project can read the environment: one of its
|
|
238
|
+
* files (not its tests, which an importer does not compile) spells a
|
|
239
|
+
* getter, cgo or a linkname, or imports a package of the project that
|
|
240
|
+
* can. A folder that cannot be read, or holds no Go file, can.
|
|
241
|
+
*/
|
|
242
|
+
const reaches = (dir) => {
|
|
243
|
+
const known = reached.get(dir);
|
|
244
|
+
if (known !== undefined)
|
|
245
|
+
return known;
|
|
246
|
+
// An import cycle reads nothing by itself.
|
|
247
|
+
reached.set(dir, false);
|
|
248
|
+
let result;
|
|
249
|
+
try {
|
|
250
|
+
const texts = readdirSync(dir)
|
|
251
|
+
.filter((name) => name.endsWith(".go") && !name.endsWith("_test.go"))
|
|
252
|
+
.map((name) => readFileSync(path.join(dir, name), "utf8"));
|
|
253
|
+
result = texts.some((text) => GO_ENV_SOURCE.test(text) || importsReach(text, dir));
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
// No such folder: no package of the project, so the path is not an
|
|
257
|
+
// import of it (an import of it would not build), only a string.
|
|
258
|
+
const code = error.code;
|
|
259
|
+
result = code !== "ENOENT" && code !== "ENOTDIR";
|
|
260
|
+
}
|
|
261
|
+
reached.set(dir, result);
|
|
262
|
+
return result;
|
|
263
|
+
};
|
|
264
|
+
/** Whether Go source imports a package of the project that can read the environment. */
|
|
265
|
+
const importsReach = (text, from) => goStrings(text).some((spelled) => {
|
|
266
|
+
const target = resolve(spelled, from);
|
|
267
|
+
return target === "" || (target !== null && target !== from && reaches(target));
|
|
268
|
+
});
|
|
269
|
+
return { importsReach };
|
|
270
|
+
}
|
|
271
|
+
/** The nearest folder holding Cargo.toml at or above `directory`, within the workspace. */
|
|
272
|
+
function crateRoot(workspaceRoot, directory) {
|
|
273
|
+
for (let current = directory;; current = path.dirname(current)) {
|
|
274
|
+
try {
|
|
275
|
+
if (statSync(path.join(current, "Cargo.toml")).isFile())
|
|
276
|
+
return current;
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
// no manifest here
|
|
280
|
+
}
|
|
281
|
+
if (path.relative(workspaceRoot, current) === "" || path.dirname(current) === current)
|
|
282
|
+
return workspaceRoot;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Whether Rust source may define a macro of that name for the crate: a
|
|
287
|
+
* `macro_rules!` of it (comments allowed between the tokens), a
|
|
288
|
+
* `macro_rules!` whose name is a macro variable, or `#[macro_use] extern
|
|
289
|
+
* crate`, which brings another crate's macros in.
|
|
290
|
+
*/
|
|
291
|
+
function rustDefines(text, name) {
|
|
292
|
+
const gap = String.raw `(?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*)*`;
|
|
293
|
+
return (new RegExp(`macro_rules${gap}!${gap}(?:r#)?${name}(?!\\w)`).test(text) ||
|
|
294
|
+
new RegExp(`macro_rules${gap}!${gap}\\$`).test(text) ||
|
|
295
|
+
new RegExp(`#${gap}\\[${gap}macro_use${gap}\\]${gap}(?:pub${gap})?extern${gap}crate\\b`).test(text));
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* A filter for one read: keeps every row, except a marked row whose
|
|
299
|
+
* declaration the project's files rule out. Directory scans are shared by
|
|
300
|
+
* the files of the read.
|
|
301
|
+
*/
|
|
302
|
+
export function createShadowRowFilter(workspaceRoot) {
|
|
303
|
+
const sources = new Map();
|
|
304
|
+
const read = (key, list) => {
|
|
305
|
+
if (!sources.has(key)) {
|
|
306
|
+
try {
|
|
307
|
+
sources.set(key, list().map((file) => readFileSync(file, "utf8")));
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
sources.set(key, null);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return sources.get(key) ?? null;
|
|
314
|
+
};
|
|
315
|
+
const go = createGoReach(workspaceRoot);
|
|
316
|
+
return (file, rows) => {
|
|
317
|
+
const directory = path.dirname(path.join(workspaceRoot, file));
|
|
318
|
+
return rows.filter((row) => {
|
|
319
|
+
const java = JAVA_ROW.exec(row.text);
|
|
320
|
+
if (java !== null) {
|
|
321
|
+
const texts = read(`java:${directory}`, () => readdirSync(directory)
|
|
322
|
+
.filter((name) => name.endsWith(".java"))
|
|
323
|
+
.map((name) => path.join(directory, name)));
|
|
324
|
+
const type = java[1];
|
|
325
|
+
if (texts === null || texts.some((text) => javaDeclares(text, type)))
|
|
326
|
+
return true;
|
|
327
|
+
// A package spans source roots (src/main/java beside generated or
|
|
328
|
+
// test sources), and a file's package is what it declares, not its
|
|
329
|
+
// folder: every .java file of the project declaring the package is
|
|
330
|
+
// read too.
|
|
331
|
+
const all = read("java:*", () => sourceFiles(workspaceRoot, ".java"));
|
|
332
|
+
const pkg = java[2] ?? "";
|
|
333
|
+
return all === null || all.some((text) => javaPackage(text) === pkg && javaDeclares(text, type));
|
|
334
|
+
}
|
|
335
|
+
const rust = RUST_ROW.exec(row.text);
|
|
336
|
+
if (rust !== null) {
|
|
337
|
+
const root = crateRoot(workspaceRoot, directory);
|
|
338
|
+
const texts = read(`rust:${root}`, () => sourceFiles(root, ".rs", ["target"]));
|
|
339
|
+
return texts === null || texts.some((text) => rustDefines(text, rust[1]));
|
|
340
|
+
}
|
|
341
|
+
if (GO_IMPORT_ROW.test(row.text)) {
|
|
342
|
+
// The row's text may be cut short, so the import is read from the
|
|
343
|
+
// file's line; every path spelled there is checked.
|
|
344
|
+
const texts = read(`file:${file}`, () => [path.join(workspaceRoot, file)]);
|
|
345
|
+
const line = texts?.[0]?.split("\n")[row.line - 1];
|
|
346
|
+
return line === undefined || goStrings(line).length === 0 || go.importsReach(line, directory);
|
|
347
|
+
}
|
|
348
|
+
const goCall = GO_CALL_ROW.exec(row.text);
|
|
349
|
+
if (goCall !== null) {
|
|
350
|
+
const own = path.basename(file);
|
|
351
|
+
const test = own.endsWith("_test.go");
|
|
352
|
+
const texts = read(`go:${file}`, () => readdirSync(directory)
|
|
353
|
+
.filter((name) => name.endsWith(".go") && name !== own && (test || !name.endsWith("_test.go")))
|
|
354
|
+
.map((name) => path.join(directory, name)));
|
|
355
|
+
// A function declared in another file is the package's own function;
|
|
356
|
+
// a variable of that name may hold any function value.
|
|
357
|
+
const name = goCall[1];
|
|
358
|
+
const variable = new RegExp(`\\bvar\\s+${name}\\b|\\b${name}\\s*(?:,\\s*\\w+\\s*)*(?::=|=(?!=))|^\\s*${name}\\s+[^\\s(=]`, "m");
|
|
359
|
+
return texts === null || texts.some((text) => variable.test(javaCode(text)));
|
|
360
|
+
}
|
|
361
|
+
const goName = GO_ROW.exec(row.text);
|
|
362
|
+
if (goName !== null) {
|
|
363
|
+
// The package's other files: the directory's .go files, with its
|
|
364
|
+
// test files only for a test file.
|
|
365
|
+
const own = path.basename(file);
|
|
366
|
+
const test = own.endsWith("_test.go");
|
|
367
|
+
const texts = read(`go:${file}`, () => readdirSync(directory)
|
|
368
|
+
.filter((name) => name.endsWith(".go") && name !== own && (test || !name.endsWith("_test.go")))
|
|
369
|
+
.map((name) => path.join(directory, name)));
|
|
370
|
+
// A getter may be reached through the name by any chain of the
|
|
371
|
+
// package's functions and values, so the row stays when any of
|
|
372
|
+
// those files can read the environment and any names it.
|
|
373
|
+
// A file that imports a package of the project that can read the
|
|
374
|
+
// environment can read it too.
|
|
375
|
+
const name = new RegExp(`\\b${goName[1]}\\b`);
|
|
376
|
+
return (texts === null ||
|
|
377
|
+
(texts.some((text) => GO_ENV_SOURCE.test(text) || go.importsReach(text, directory)) &&
|
|
378
|
+
texts.some((text) => name.test(text))));
|
|
379
|
+
}
|
|
380
|
+
const csharp = CSHARP_ROW.exec(row.text);
|
|
381
|
+
if (csharp !== null) {
|
|
382
|
+
// Generated sources under obj/ are compiled too, so they are read.
|
|
383
|
+
const texts = read("csharp", () => sourceFiles(workspaceRoot, ".cs"));
|
|
384
|
+
return texts === null || texts.some((text) => csharpDeclares(text, csharp[1]));
|
|
385
|
+
}
|
|
386
|
+
return true;
|
|
387
|
+
});
|
|
388
|
+
};
|
|
389
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SWI-Prolog evaluator behind the code proof kinds. Kibi runs it with the
|
|
3
|
+
* SWI-Prolog it resolves for its own engine (KIBI_SWIPL, the bundled runtime,
|
|
4
|
+
* then swipl on PATH). It loads the file a symbol names (a module, or a plain
|
|
5
|
+
* file into user) and reads a predicate named by its indicator: a domain is
|
|
6
|
+
* the distinct solutions of one argument (`arg`, or the only one), a value is
|
|
7
|
+
* the single number such a solution holds, a rule predicate is called once
|
|
8
|
+
* per row and decides it by success or failure, and a transition table lists
|
|
9
|
+
* every solution as an edge. The plan is the last command-line argument; the
|
|
10
|
+
* answer is the last line of output, in the JSON the JavaScript and Python
|
|
11
|
+
* evaluators print.
|
|
12
|
+
*/
|
|
13
|
+
export declare const PROLOG_EVALUATOR: string;
|
|
14
|
+
//# sourceMappingURL=prolog-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prolog-evaluator.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/prolog-evaluator.ts"],"names":[],"mappings":"AAkBA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB,QAwN5B,CAAC"}
|
|
@@ -0,0 +1,247 @@
|
|
|
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 evaluator behind the code proof kinds. Kibi runs it with the
|
|
20
|
+
* SWI-Prolog it resolves for its own engine (KIBI_SWIPL, the bundled runtime,
|
|
21
|
+
* then swipl on PATH). It loads the file a symbol names (a module, or a plain
|
|
22
|
+
* file into user) and reads a predicate named by its indicator: a domain is
|
|
23
|
+
* the distinct solutions of one argument (`arg`, or the only one), a value is
|
|
24
|
+
* the single number such a solution holds, a rule predicate is called once
|
|
25
|
+
* per row and decides it by success or failure, and a transition table lists
|
|
26
|
+
* every solution as an edge. The plan is the last command-line argument; the
|
|
27
|
+
* answer is the last line of output, in the JSON the JavaScript and Python
|
|
28
|
+
* evaluators print.
|
|
29
|
+
*/
|
|
30
|
+
// implements REQ-kibi-multilingual-code-proofs
|
|
31
|
+
export const PROLOG_EVALUATOR = String.raw `
|
|
32
|
+
:- module(kibi_proof_evaluator, []).
|
|
33
|
+
:- use_module(library(http/json)).
|
|
34
|
+
:- use_module(library(apply)).
|
|
35
|
+
:- use_module(library(lists)).
|
|
36
|
+
:- use_module(library(pairs)).
|
|
37
|
+
|
|
38
|
+
:- initialization(main, main).
|
|
39
|
+
|
|
40
|
+
:- dynamic loaded_module/2.
|
|
41
|
+
|
|
42
|
+
main :-
|
|
43
|
+
current_prolog_flag(argv, Argv),
|
|
44
|
+
last(Argv, PlanText),
|
|
45
|
+
atom_json_dict(PlanText, Plan, [value_string_as(string)]),
|
|
46
|
+
( catch(run(Plan, Out), Error, (failure_text(Error, Text), failed(Plan, Text, Out)))
|
|
47
|
+
-> true
|
|
48
|
+
; failed(Plan, "the evaluation failed", Out)
|
|
49
|
+
),
|
|
50
|
+
nl,
|
|
51
|
+
json_write_dict(current_output, Out, [width(0)]),
|
|
52
|
+
nl.
|
|
53
|
+
|
|
54
|
+
failed(Plan, Text, _{error: Text}) :- Plan.mode == "export", !.
|
|
55
|
+
failed(_, Text, _{ok: false, error: Text}).
|
|
56
|
+
|
|
57
|
+
failure_text(kibi(Text), Text) :- !.
|
|
58
|
+
failure_text(Error, Text) :-
|
|
59
|
+
catch('$messages':translate_message(Error, Lines, []), _, fail),
|
|
60
|
+
!,
|
|
61
|
+
with_output_to(string(Raw), print_message_lines(current_output, '', Lines)),
|
|
62
|
+
split_string(Raw, "", "\n ", [Text]).
|
|
63
|
+
failure_text(Error, Text) :- format(string(Text), "~q", [Error]).
|
|
64
|
+
|
|
65
|
+
refuse(Format, Args) :- format(string(Text), Format, Args), throw(kibi(Text)).
|
|
66
|
+
|
|
67
|
+
% The module a file defines, loaded once; user for a plain file.
|
|
68
|
+
module_of(File, Module) :- loaded_module(File, Module), !.
|
|
69
|
+
module_of(File, Module) :-
|
|
70
|
+
atom_string(Path, File),
|
|
71
|
+
load_files(user:Path, [if(not_loaded), silent(true)]),
|
|
72
|
+
absolute_file_name(Path, Absolute, [file_type(prolog), access(read)]),
|
|
73
|
+
( module_property(Defined, file(Absolute)) -> Module = Defined ; Module = user ),
|
|
74
|
+
assertz(loaded_module(File, Module)).
|
|
75
|
+
|
|
76
|
+
% A predicate indicator: Name/Arity, Name//Arity (a DCG rule) or Module:Indicator.
|
|
77
|
+
indicator(Text, Default, Module, Name, Arity) :-
|
|
78
|
+
catch(term_string(Term, Text), _, refuse("~s is not a predicate indicator Name/Arity", [Text])),
|
|
79
|
+
indicator_term(Term, Text, Default, Module, Name, Arity).
|
|
80
|
+
|
|
81
|
+
indicator_term(Module:Term, Text, _, Found, Name, Arity) :- atom(Module), !,
|
|
82
|
+
indicator_term(Term, Text, Module, Found, Name, Arity).
|
|
83
|
+
indicator_term(Name/Arity, _, Module, Module, Name, Arity) :- atom(Name), integer(Arity), Arity >= 0, !.
|
|
84
|
+
indicator_term(Name//Rule, _, Module, Module, Name, Arity) :- atom(Name), integer(Rule), Rule >= 0, !,
|
|
85
|
+
Arity is Rule + 2.
|
|
86
|
+
indicator_term(_, Text, _, _, _, _) :- refuse("~s is not a predicate indicator Name/Arity", [Text]).
|
|
87
|
+
|
|
88
|
+
% The goal behind a reference, after checking the predicate is defined.
|
|
89
|
+
predicate(Ref, Module, Head, Name, Arity) :-
|
|
90
|
+
module_of(Ref.file, Default),
|
|
91
|
+
indicator(Ref.export, Default, Module, Name, Arity),
|
|
92
|
+
functor(Head, Name, Arity),
|
|
93
|
+
( predicate_property(Module:Head, defined) -> true
|
|
94
|
+
; refuse("~s is not defined in ~s", [Ref.export, Ref.file])
|
|
95
|
+
).
|
|
96
|
+
|
|
97
|
+
% The argument a reference projects its solutions on: arg, or the only one.
|
|
98
|
+
projected(Ref, Arity, Index) :-
|
|
99
|
+
( get_dict(arg, Ref, Index)
|
|
100
|
+
-> ( integer(Index), Index >= 1, Index =< Arity -> true
|
|
101
|
+
; refuse("arg ~w is not an argument of ~s", [Index, Ref.export])
|
|
102
|
+
)
|
|
103
|
+
; Arity =:= 1
|
|
104
|
+
-> Index = 1
|
|
105
|
+
; Arity =:= 0
|
|
106
|
+
-> refuse("~s takes no argument to list", [Ref.export])
|
|
107
|
+
; refuse("~s has ~w arguments; set arg to the one that names each member", [Ref.export, Arity])
|
|
108
|
+
).
|
|
109
|
+
|
|
110
|
+
limit_solutions(100000).
|
|
111
|
+
|
|
112
|
+
% The distinct solutions of a reference's projected argument, in order.
|
|
113
|
+
values(Ref, Values) :-
|
|
114
|
+
predicate(Ref, Module, Head, _, Arity),
|
|
115
|
+
projected(Ref, Arity, Index),
|
|
116
|
+
arg(Index, Head, Value),
|
|
117
|
+
limit_solutions(Limit),
|
|
118
|
+
Over is Limit + 1,
|
|
119
|
+
findall(Value, limit(Over, Module:Head), All),
|
|
120
|
+
length(All, Count),
|
|
121
|
+
( Count > Limit -> refuse("~s has more than ~w solutions", [Ref.export, Limit]) ; true ),
|
|
122
|
+
( member(Open, All), \+ ground(Open)
|
|
123
|
+
-> refuse("a solution of ~s leaves argument ~w unbound", [Ref.export, Index])
|
|
124
|
+
; true
|
|
125
|
+
),
|
|
126
|
+
list_to_set(All, Values).
|
|
127
|
+
|
|
128
|
+
text(Value, Text) :- string(Value), !, Text = Value.
|
|
129
|
+
text(Value, Text) :- atom(Value), !, atom_string(Value, Text).
|
|
130
|
+
text(Value, Text) :- number(Value), !, number_string(Value, Text).
|
|
131
|
+
text(Value, Text) :- format(string(Text), "~q", [Value]).
|
|
132
|
+
|
|
133
|
+
run(Plan, Out) :- Plan.mode == "export", !, export(Plan, Out).
|
|
134
|
+
run(Plan, _{ok: true, model: Model}) :- Plan.mode == "table", !, table(Plan, Model).
|
|
135
|
+
run(Plan, _{ok: true, model: Model}) :- Plan.mode == "machine", !, machine(Plan, Model).
|
|
136
|
+
run(Plan, _{ok: true, model: Model}) :- Plan.mode == "pattern", !, pattern(Plan.pattern, Model).
|
|
137
|
+
run(Plan, _) :- refuse("unknown plan mode ~w", [Plan.mode]).
|
|
138
|
+
|
|
139
|
+
% A domain lists its members; a value is the single number a constant holds.
|
|
140
|
+
export(Plan, Out) :-
|
|
141
|
+
values(Plan.ref, Values),
|
|
142
|
+
( Plan.want == "scalar"
|
|
143
|
+
-> ( Values = [Number], number(Number)
|
|
144
|
+
-> Out = _{scalar: Number}
|
|
145
|
+
; Values = [Other]
|
|
146
|
+
-> refuse("~s holds ~q, not a number", [Plan.ref.export, Other])
|
|
147
|
+
; length(Values, Count),
|
|
148
|
+
refuse("~s has ~w solutions, not the single value of a constant", [Plan.ref.export, Count])
|
|
149
|
+
)
|
|
150
|
+
; length(Values, Count),
|
|
151
|
+
maplist(text, Values, Members),
|
|
152
|
+
Out = _{count: Count, members: Members}
|
|
153
|
+
).
|
|
154
|
+
|
|
155
|
+
domain(Ref, Values) :- get_dict(values, Ref, Texts), !, maplist(atom_string, Values, Texts).
|
|
156
|
+
domain(Ref, Values) :- values(Ref, Values).
|
|
157
|
+
|
|
158
|
+
table(Plan, _{domains: Domains, predicates: Predicates}) :-
|
|
159
|
+
dict_pairs(Plan.domains, _, DomainRefs),
|
|
160
|
+
findall(Type-Values, (member(Type-Ref, DomainRefs), domain(Ref, Values)), Typed),
|
|
161
|
+
findall(Type-Texts, (member(Type-Values, Typed), maplist(text, Values, Texts)), DomainTexts),
|
|
162
|
+
dict_pairs(Domains, _, DomainTexts),
|
|
163
|
+
dict_pairs(Plan.predicates, _, PredicateRefs),
|
|
164
|
+
foldl(predicate_table(Typed, Plan.limit), PredicateRefs, []-0, Reversed-_),
|
|
165
|
+
reverse(Reversed, Rows),
|
|
166
|
+
dict_pairs(Predicates, _, Rows).
|
|
167
|
+
|
|
168
|
+
predicate_table(Typed, Limit, Name-Ref, Done-Used, [Name-Rows|Done]-Total) :-
|
|
169
|
+
predicate(Ref, Module, Head, _, Arity),
|
|
170
|
+
length(Ref.arg_types, Types),
|
|
171
|
+
( Types =:= Arity -> true
|
|
172
|
+
; refuse("~s takes ~w arguments, but the predicate ~w has ~w argument types", [Ref.export, Arity, Name, Types])
|
|
173
|
+
),
|
|
174
|
+
findall(Values,
|
|
175
|
+
( member(TypeText, Ref.arg_types),
|
|
176
|
+
atom_string(Type, TypeText),
|
|
177
|
+
( memberchk(Type-Values, Typed) -> true
|
|
178
|
+
; refuse("no domain for argument type ~s of ~w", [TypeText, Name])
|
|
179
|
+
)
|
|
180
|
+
),
|
|
181
|
+
Lists),
|
|
182
|
+
foldl([List, P0, P]>>(length(List, L), P is P0 * L), Lists, 1, Count),
|
|
183
|
+
Total is Used + Count,
|
|
184
|
+
( Total > Limit -> refuse("the predicate tables need more than ~w calls", [Limit]) ; true ),
|
|
185
|
+
findall(Row, (maplist(member, Tuple, Lists), row(Module, Head, Tuple, Row)), Rows).
|
|
186
|
+
|
|
187
|
+
row(Module, Head, Tuple, Row) :-
|
|
188
|
+
copy_term(Head, Goal),
|
|
189
|
+
Goal =.. [_|Tuple],
|
|
190
|
+
maplist(text, Tuple, Args),
|
|
191
|
+
catch(( once(Module:Goal) -> Row = _{args: Args, value: true} ; Row = _{args: Args, value: false} ),
|
|
192
|
+
Error,
|
|
193
|
+
( failure_text(Error, Text), Row = _{args: Args, error: Text} )).
|
|
194
|
+
|
|
195
|
+
% A transition table: Name/2 facts (from, to), Name/3 facts (from, event,
|
|
196
|
+
% to), or with state and event domains a Name/3 next-state predicate Kibi
|
|
197
|
+
% calls on every state and event.
|
|
198
|
+
machine(Plan, Model) :-
|
|
199
|
+
Ref = Plan.transitions,
|
|
200
|
+
predicate(Ref, Module, Head, _, Arity),
|
|
201
|
+
( get_dict(domains, Plan, Domains)
|
|
202
|
+
-> ( Arity =:= 3 -> true ; refuse("~s must take a state, an event and the next state", [Ref.export]) ),
|
|
203
|
+
values(Domains.state, States0),
|
|
204
|
+
values(Domains.event, Events),
|
|
205
|
+
findall(From-To,
|
|
206
|
+
( member(From, States0), member(Event, Events),
|
|
207
|
+
Head =.. [_, From, Event, To],
|
|
208
|
+
catch(Module:Head, _, fail)
|
|
209
|
+
),
|
|
210
|
+
Edges)
|
|
211
|
+
; Arity =:= 2
|
|
212
|
+
-> Head =.. [_, From, To],
|
|
213
|
+
findall(From-To, Module:Head, Edges),
|
|
214
|
+
States0 = []
|
|
215
|
+
; Arity =:= 3
|
|
216
|
+
-> Head =.. [_, From, _, To],
|
|
217
|
+
findall(From-To, Module:Head, Edges),
|
|
218
|
+
States0 = []
|
|
219
|
+
; refuse("~s is not a transition table Name/2 or Name/3", [Ref.export])
|
|
220
|
+
),
|
|
221
|
+
( member(Edge, Edges), \+ ground(Edge) -> refuse("a transition of ~s leaves a state unbound", [Ref.export]) ; true ),
|
|
222
|
+
findall(Text, (member(State, States0), text(State, Text)), Listed),
|
|
223
|
+
findall([FromText, ToText], (member(F-T, Edges), text(F, FromText), text(T, ToText)), Pairs0),
|
|
224
|
+
sort(Pairs0, Pairs),
|
|
225
|
+
findall(S, (member([A, B], Pairs), member(S, [A, B])), Ends),
|
|
226
|
+
append(Listed, Ends, AllStates),
|
|
227
|
+
sort(AllStates, StateTexts),
|
|
228
|
+
Base = _{states: StateTexts, transitions: Pairs},
|
|
229
|
+
( get_dict(initial, Plan, Initial), Initial \== null
|
|
230
|
+
-> values(Initial, InitialValues),
|
|
231
|
+
maplist(text, InitialValues, InitialTexts0),
|
|
232
|
+
sort(InitialTexts0, InitialTexts),
|
|
233
|
+
Model = Base.put(initial, InitialTexts)
|
|
234
|
+
; Model = Base
|
|
235
|
+
).
|
|
236
|
+
|
|
237
|
+
% A pattern constant: the single atom or string a Name/1 fact holds.
|
|
238
|
+
pattern(Ref, _{type: "string", value: Text}) :-
|
|
239
|
+
values(Ref, Values),
|
|
240
|
+
( Values = [Value], (atom(Value) ; string(Value))
|
|
241
|
+
-> text(Value, Text)
|
|
242
|
+
; Values = [Other]
|
|
243
|
+
-> refuse("~s holds ~q, not an atom or a string", [Ref.export, Other])
|
|
244
|
+
; length(Values, Count),
|
|
245
|
+
refuse("~s has ~w solutions, not the single pattern of a constant", [Ref.export, Count])
|
|
246
|
+
).
|
|
247
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SWI-Prolog reader behind the `references` reader for `.pl` and `.plt`
|
|
3
|
+
* files and the `prolog` source reader. It reads terms with read_term/3 and
|
|
4
|
+
* runs none of the project's code: operators come from the file's own op/3
|
|
5
|
+
* directives and module export lists, those of project modules it loads
|
|
6
|
+
* (read from their module header) and those of the SWI-Prolog libraries it
|
|
7
|
+
* loads. `references` mode walks clause bodies and directives through
|
|
8
|
+
* control constructs and meta-predicate arguments (the file's own
|
|
9
|
+
* meta_predicate declarations and SWI-Prolog's), the goals ~@ calls in
|
|
10
|
+
* format strings and message lines, and the bodies of clauses
|
|
11
|
+
* asserted from literal terms, and reports every place it cannot read
|
|
12
|
+
* statically: a variable called as a goal or closure, a goal qualified by a
|
|
13
|
+
* variable module, a clause asserted from data, conditional compilation, a
|
|
14
|
+
* term_expansion or goal_expansion clause in the files or in a project file
|
|
15
|
+
* they load (expansion can produce any clause), and an include of a file
|
|
16
|
+
* outside the files read. `terms` mode lists one argument of every fact with
|
|
17
|
+
* a given functor, in file order.
|
|
18
|
+
*/
|
|
19
|
+
export declare const PROLOG_SOURCE_READER: string;
|
|
20
|
+
//# sourceMappingURL=prolog-source-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prolog-source-reader.d.ts","sourceRoot":"","sources":["../../../src/operations/proof/prolog-source-reader.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,oBAAoB,QA8jChC,CAAC"}
|