eyeprolog 1.4.2 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/examples/deck/README.md +1 -0
- package/examples/deck/introduction-to-eyeprolog.md +209 -0
- package/package.json +1 -1
- package/src/iso.js +45 -2
- package/test/conformance/ISO-COMPLIANCE.md +1 -1
- package/test/conformance/ISO-PROLOG-TEXT-EXECUTION-MATRIX.md +8 -4
- package/test/run-iso-strict.mjs +15 -0
- package/test/run-regression.mjs +25 -0
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ printf 'human(socrates).\nmortal(X) :- human(X).\n' |
|
|
|
65
65
|
- [Playground](https://eyereasoner.github.io/eyeprolog/playground) — run EyeProlog in a browser
|
|
66
66
|
- [Examples](examples) — runnable programs and checked output
|
|
67
67
|
- [Example decks](examples/deck/README.md) — explainable RDF/Prolog scenarios with reproducible roundtrips
|
|
68
|
+
- [Introduction to EyeProlog](examples/deck/introduction-to-eyeprolog.md) — short presentation deck for first-time audiences
|
|
68
69
|
- [Symbiotic Knowledge Graphs](examples/deck/symbiotic-knowledge-graphs.md) — RDF ↔ Prolog heatwave-response demo for human/AI/KG co-evolution
|
|
69
70
|
- [rdf-prolog-roundtrip](https://github.com/eyereasoner/rdf-prolog-roundtrip) — standalone RDF 1.2 ↔ ISO Prolog bridge used by the RDF examples
|
|
70
71
|
- [ISO conformance audit](test/conformance/ISO-COMPLIANCE.md) — supported Part 1 profile
|
package/examples/deck/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Wide-audience companion explanations for selected runnable examples.
|
|
4
4
|
|
|
5
|
+
- [Introduction to EyeProlog](introduction-to-eyeprolog.md) — a short slide deck introducing facts, rules, answers, proofs, ISO conformance, JavaScript embedding, and RDF roundtrips.
|
|
5
6
|
- [Cross-organization data sharing](cross-organization-data-sharing.md) — ODRL/DPV sharing decisions with permit, deny, review, safeguards, and obligations.
|
|
6
7
|
- [Explainable EV-depot configuration](explainable-ev-depot-configuration.md) — reversible configuration rules that select a charger, explain blockers, and derive required changes.
|
|
7
8
|
- [ODRL policy reasoning](odrl-policy-reasoning.md) — permissions, prohibitions, duties, conflicts, and defaults.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
marp: true
|
|
3
|
+
title: Introduction to EyeProlog
|
|
4
|
+
description: A short presentation introducing EyeProlog as a portable, inspectable ISO Prolog engine for answers, proofs, and RDF-backed reasoning.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Introduction to EyeProlog
|
|
8
|
+
|
|
9
|
+
Portable Prolog reasoning for answers, proofs, and knowledge graphs
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What is EyeProlog?
|
|
14
|
+
|
|
15
|
+
EyeProlog is a small ISO Prolog implementation for JavaScript.
|
|
16
|
+
|
|
17
|
+
It turns explicit facts and rules into:
|
|
18
|
+
|
|
19
|
+
- answers;
|
|
20
|
+
- variable bindings;
|
|
21
|
+
- checked example output;
|
|
22
|
+
- optional proof traces;
|
|
23
|
+
- embeddable reasoning in Node.js and browsers.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Why Prolog?
|
|
28
|
+
|
|
29
|
+
Prolog is a language for relations.
|
|
30
|
+
|
|
31
|
+
Instead of spelling out every control step, you describe what must hold:
|
|
32
|
+
|
|
33
|
+
```prolog
|
|
34
|
+
human(socrates).
|
|
35
|
+
mortal(X) :- human(X).
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then you ask questions:
|
|
39
|
+
|
|
40
|
+
```prolog
|
|
41
|
+
?- mortal(socrates).
|
|
42
|
+
true.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## The core idea
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
facts + rules + query
|
|
51
|
+
|
|
|
52
|
+
v
|
|
53
|
+
search by unification
|
|
54
|
+
|
|
|
55
|
+
v
|
|
56
|
+
answers with reasons
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The same rule can answer a yes/no question, enumerate solutions, or participate in a larger proof.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
Run EyeProlog without a global install:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npx --yes eyeprolog
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Try a list query:
|
|
72
|
+
|
|
73
|
+
```prolog
|
|
74
|
+
?- use_module(library(lists)).
|
|
75
|
+
true.
|
|
76
|
+
?- member(X, [prolog, logic]).
|
|
77
|
+
X = prolog
|
|
78
|
+
; X = logic.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Programs are ordinary text
|
|
84
|
+
|
|
85
|
+
A file can contain facts, rules, and goals:
|
|
86
|
+
|
|
87
|
+
```prolog
|
|
88
|
+
human(socrates).
|
|
89
|
+
mortal(X) :- human(X).
|
|
90
|
+
|
|
91
|
+
?- mortal(X).
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Run it:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
eyeprolog examples/socrates.pl
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Proofs are first-class
|
|
103
|
+
|
|
104
|
+
EyeProlog can show not only *what* was concluded, but *why*.
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
eyeprolog --proof examples/socrates.pl
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Proof output is useful for:
|
|
111
|
+
|
|
112
|
+
- debugging rules;
|
|
113
|
+
- explaining decisions;
|
|
114
|
+
- regression tests;
|
|
115
|
+
- audit trails.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## ISO first, extensions visible
|
|
120
|
+
|
|
121
|
+
EyeProlog starts from ISO/IEC 13211-1 Prolog.
|
|
122
|
+
|
|
123
|
+
That matters because facts, rules, terms, control, errors, streams, arithmetic, and meta-calls have an external reference point.
|
|
124
|
+
|
|
125
|
+
Normal mode adds practical libraries and embedding features. Strict ISO mode keeps the standardized core separate and testable.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Why JavaScript?
|
|
130
|
+
|
|
131
|
+
EyeProlog runs where modern applications already run:
|
|
132
|
+
|
|
133
|
+
- command-line tools;
|
|
134
|
+
- npm packages;
|
|
135
|
+
- browser playgrounds;
|
|
136
|
+
- web workers;
|
|
137
|
+
- JavaScript APIs.
|
|
138
|
+
|
|
139
|
+
The aim is not to replace every Prolog system. It is to make portable Prolog reasoning easy to embed and inspect.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## RDF and knowledge graphs
|
|
144
|
+
|
|
145
|
+
EyeProlog pairs naturally with RDF through `rdf-prolog-roundtrip`:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
RDF dataset
|
|
149
|
+
-> rdf(S, P, O, Graph) facts
|
|
150
|
+
-> EyeProlog rules
|
|
151
|
+
-> result_rdf/4 facts
|
|
152
|
+
-> RDF dataset
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
RDF remains the interchange layer. Prolog remains the transparent reasoning layer.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## What this enables
|
|
160
|
+
|
|
161
|
+
The example suite uses the same pattern for:
|
|
162
|
+
|
|
163
|
+
- policy decisions;
|
|
164
|
+
- incident response;
|
|
165
|
+
- software supply-chain risk;
|
|
166
|
+
- scientific evidence assessment;
|
|
167
|
+
- EV-depot configuration;
|
|
168
|
+
- symbiotic human/AI/knowledge-graph workflows.
|
|
169
|
+
|
|
170
|
+
Each scenario keeps data, rules, answers, and materialized results as separate inspectable artifacts.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Testing and conformance
|
|
175
|
+
|
|
176
|
+
EyeProlog’s release gate includes:
|
|
177
|
+
|
|
178
|
+
- file-based conformance cases;
|
|
179
|
+
- strict ISO tests;
|
|
180
|
+
- WG17 syntax cases;
|
|
181
|
+
- regression tests;
|
|
182
|
+
- runnable examples;
|
|
183
|
+
- proof examples;
|
|
184
|
+
- documentation synchronization checks.
|
|
185
|
+
|
|
186
|
+
The point is simple: behavior should be reproducible, not anecdotal.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## When EyeProlog fits
|
|
191
|
+
|
|
192
|
+
EyeProlog is a good fit when a project needs:
|
|
193
|
+
|
|
194
|
+
- explicit rules;
|
|
195
|
+
- inspectable conclusions;
|
|
196
|
+
- portable Prolog syntax;
|
|
197
|
+
- embeddable JavaScript runtime;
|
|
198
|
+
- checked examples and proofs;
|
|
199
|
+
- a clean boundary between data, rules, and results.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Where to go next
|
|
204
|
+
|
|
205
|
+
- Read [`README.md`](../../README.md) for setup and links.
|
|
206
|
+
- Read [`why-eyeprolog.md`](../../why-eyeprolog.md) for the design motivation.
|
|
207
|
+
- Open [`playground.html`](../../playground.html) to run examples in a browser.
|
|
208
|
+
- Explore [`examples/`](../) for runnable programs.
|
|
209
|
+
- Read [`the-art-of-eyeprolog.md`](../../the-art-of-eyeprolog.md) for the full reference.
|
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -2623,10 +2623,53 @@ function validateControlCallable(term, culprit, env) {
|
|
|
2623
2623
|
}
|
|
2624
2624
|
}
|
|
2625
2625
|
}
|
|
2626
|
+
function convertMetaCallBodyTerm(term, env, module) {
|
|
2627
|
+
// ISO term-to-body conversion is applied when call/1 begins, before any
|
|
2628
|
+
// goal in the converted body is executed. An unbound variable occurring as
|
|
2629
|
+
// a body goal therefore becomes call(Var) at that point. If an earlier goal
|
|
2630
|
+
// later binds Var to !, the resulting call(!) has its own opaque cut scope
|
|
2631
|
+
// instead of turning into a textual cut in the enclosing meta-call.
|
|
2632
|
+
//
|
|
2633
|
+
// Build the converted control tree iteratively so a deeply nested sequence
|
|
2634
|
+
// of conjunctions/disjunctions cannot consume the JavaScript call stack.
|
|
2635
|
+
const convertNode = (input, inheritedModule) => {
|
|
2636
|
+
const resolved = deref(input, env);
|
|
2637
|
+
const effectiveModule = resolved.module ?? inheritedModule;
|
|
2638
|
+
if (resolved.type === VAR) {
|
|
2639
|
+
const wrapped = compound('call', [resolved]);
|
|
2640
|
+
wrapped.module = effectiveModule;
|
|
2641
|
+
return { term: wrapped, source: null, module: effectiveModule };
|
|
2642
|
+
}
|
|
2643
|
+
if (resolved.type !== ATOM && resolved.type !== COMPOUND) {
|
|
2644
|
+
throw new PrologError('type_error(callable)', term);
|
|
2645
|
+
}
|
|
2646
|
+
if (resolved.type === COMPOUND && [',', ';', '->'].includes(resolved.name) && resolved.arity === 2) {
|
|
2647
|
+
const converted = compound(resolved.name, [...resolved.args]);
|
|
2648
|
+
converted.module = effectiveModule;
|
|
2649
|
+
return { term: converted, source: resolved, module: effectiveModule };
|
|
2650
|
+
}
|
|
2651
|
+
return { term: resolved, source: null, module: effectiveModule };
|
|
2652
|
+
};
|
|
2653
|
+
|
|
2654
|
+
const root = convertNode(term, module);
|
|
2655
|
+
const pending = root.source == null ? [] : [root];
|
|
2656
|
+
while (pending.length > 0) {
|
|
2657
|
+
const current = pending.pop();
|
|
2658
|
+
for (let index = 0; index < 2; index++) {
|
|
2659
|
+
const child = convertNode(current.source.args[index], current.module);
|
|
2660
|
+
current.term.args[index] = child.term;
|
|
2661
|
+
if (child.source != null) pending.push(child);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
return root.term;
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2626
2667
|
function expandCallGoal({ goal, env }) {
|
|
2627
2668
|
const invoked = callable(goal.args[0], env);
|
|
2628
|
-
|
|
2629
|
-
|
|
2669
|
+
const module = invoked.module ?? goal.module ?? 'user';
|
|
2670
|
+
const converted = convertMetaCallBodyTerm(invoked, env, module);
|
|
2671
|
+
if (converted.module == null) converted.module = module;
|
|
2672
|
+
return converted;
|
|
2630
2673
|
}
|
|
2631
2674
|
function* callBuiltin(context) {
|
|
2632
2675
|
const { solver, env } = context;
|
|
@@ -36,7 +36,7 @@ relevant implementation-dependent overlap choice to an explicit audit outcome.
|
|
|
36
36
|
| 7.4 — Prolog text and directives | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes the preparation/directive rows: declaration semantics, source clauses, cross-text operators/character conversion/flags, initialization order/lifetime, `include/1`, and one-time `ensure_loaded/1`, with implementation-defined cross-text choices indexed under 5.4. |
|
|
37
37
|
| 7.5-7.6 — database and term/clause conversion | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes static/dynamic and private/public procedure semantics, clause order, logical-update visibility, empty/unknown lifetime, and term/body/clause conversion. Source and runtime assertion conversion recurse through `,/2`, `;/2`, and `->/2`, preserve variable identity, and protect standardized static/control procedures. |
|
|
38
38
|
| 7.7 — execution and backtracking | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes ordinary clause selection/backtracking, source/database order, empty-versus-unknown procedures, logical-update visibility, and strict dispatch. EyeProlog automatic tabling, cycle guards, and recursive numeric shortcuts remain disabled in the strict profile. |
|
|
39
|
-
| 7.8 — control constructs and exceptions | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes `true/0`, `fail/0`, `call/1`, cut, conjunction, disjunction, if-then(-else), and `catch/3`/`throw/1`, including Corrigendum 2 catchability of errors arising from the protected goal and Corrigendum 3 term-to-body conversion
|
|
39
|
+
| 7.8 — control constructs and exceptions | covered | [ISO-PROLOG-TEXT-EXECUTION-MATRIX.md](ISO-PROLOG-TEXT-EXECUTION-MATRIX.md) closes `true/0`, `fail/0`, `call/1`, cut, conjunction, disjunction, if-then(-else), and `catch/3`/`throw/1`, including Corrigendum 2 catchability of errors arising from the protected goal and Corrigendum 3 term-to-body conversion at `call/1` entry; the regression suite pins the resulting cut scope when an initially unbound variable goal later becomes `!`. The 8.15 built-in additions remain independently row-audited. |
|
|
40
40
|
| 7.9 — expression evaluation | covered | [ISO-EVALUABLE-FUNCTOR-MATRIX.md](ISO-EVALUABLE-FUNCTOR-MATRIX.md) closes recursive evaluation, direct-variable precedence, corrected non-evaluable `F/N` errors (including STC #69), operand template/type rules, mixed arithmetic-comparison conversion, exceptional values, and finite-host resource normalization. The audit also fixes float-only `float_integer_part/1` and `float_fractional_part/1` so even huge valid integers reach `type_error(float,...)` before any I->F overflow. |
|
|
41
41
|
| 7.10 — input/output concepts | covered | Higher-level stream semantics are pinned for non-atom ground stream terms, alias lifetime, current/target stream selection and fallback, write truncation, append positioning, contradictory stream options, exact binary round-tripping, flushing, standard-stream close behavior, and repositioned overwrite. Term-output spelling is also checked lexically where ISO distinguishes token forms: Corrigendum 2 bar operators are written with bare `|`, ordinary atom/functor `|` remains quoted, and the semicolon name token is emitted bare by canonical output. The complete 8.11-8.14 built-in mode/error family remains row-audited in [ISO-BUILTIN-MODE-ERROR-MATRIX.md](ISO-BUILTIN-MODE-ERROR-MATRIX.md), while all implementation-defined stream choices are indexed in [ISO-IMPLEMENTATION-DEFINED.md](ISO-IMPLEMENTATION-DEFINED.md). |
|
|
42
42
|
| 7.11 — flags | covered | The complete Part 1 flag set, selected defaults, standard value domains, changeability, `current_prolog_flag/2`, and `set_prolog_flag/2` error behavior have dedicated strict tests. EyeProlog selects `bounded=false` and `integer_rounding_function=toward_zero`; valid alternative values of those fixed flags reach `permission_error(modify,flag,...)`, while `max_integer` and `min_integer` have no current value. STC #70 is recorded explicitly: EyeProlog has no separate finite procedure-arity ceiling, so the optional `max_procedure_arity` flag is absent while `max_arity` remains `unbounded`. Strict mode excludes the EyeProlog `occurs_check` extension. |
|
|
@@ -55,8 +55,12 @@ The complete 8.8-8.9 database built-in modes/errors remain covered by
|
|
|
55
55
|
| 7.6.3 clause-to-term observation | covered | `clause/2` returns fresh renamed clause observations with the required sharing between head and body. |
|
|
56
56
|
| 7.6.4 update conversion | covered | `asserta/1`, `assertz/1`, `retract/1`, and prepared source clauses use the same strict body-conversion rules and reject malformed bodies before database mutation. |
|
|
57
57
|
|
|
58
|
-
Corrigendum 3's explicit `call/1` term-to-body wording is covered by the source
|
|
59
|
-
|
|
58
|
+
Corrigendum 3's explicit `call/1` term-to-body wording is covered by the source,
|
|
59
|
+
asserted-body, and meta-call conversion regressions. In particular, `call/1`
|
|
60
|
+
converts an initially unbound variable goal to `call(Var)` before execution; if
|
|
61
|
+
an earlier goal later binds that variable to `!`, the resulting `call(!)` keeps
|
|
62
|
+
its own opaque cut boundary instead of becoming a textual cut in the enclosing
|
|
63
|
+
converted body.
|
|
60
64
|
|
|
61
65
|
## 7.7 - execution and backtracking
|
|
62
66
|
|
|
@@ -74,8 +78,8 @@ and asserted-body conversion regressions.
|
|
|
74
78
|
| --- | --- | --- |
|
|
75
79
|
| 7.8.1 `true/0` | covered | succeeds once |
|
|
76
80
|
| 7.8.2 `fail/0` | covered | fails |
|
|
77
|
-
| 7.8.3 `call/1` | covered | executes the converted goal; variables
|
|
78
|
-
| 7.8.4 cut | covered | cut commits within its defined invocation scope; callee-local cuts do not incorrectly prune
|
|
81
|
+
| 7.8.3 `call/1` | covered | executes the converted goal; variables that are unbound at meta-call entry become `call(Var)` before execution, so a later binding to `!` remains an opaque `call(!)`; variables already bound at entry are dereferenced during conversion |
|
|
82
|
+
| 7.8.4 cut | covered | cut commits within its defined invocation scope; callee-local cuts and cuts reached through a converted variable goal do not incorrectly prune alternatives outside that invocation |
|
|
79
83
|
| 7.8.5 conjunction | covered | left-to-right execution and backtracking; nested malformed converted goals retain the whole control-term culprit |
|
|
80
84
|
| 7.8.6 disjunction | covered | left branch is tried before the right and failure backtracks into the right branch |
|
|
81
85
|
| 7.8.7 if-then | covered | successful condition commits to the selected condition solution before the then branch |
|
package/test/run-iso-strict.mjs
CHANGED
|
@@ -1214,6 +1214,21 @@ export function runIsoStrict(reporter = new TestReporter()) {
|
|
|
1214
1214
|
isoStrict: true,
|
|
1215
1215
|
goal: 'catch(throw(ball(X)),ball(Y),X=Y)',
|
|
1216
1216
|
}).stats.completed_goal_lists > 0, true, 'throw/1 and catch/3 unify through a renamed thrown term');
|
|
1217
|
+
|
|
1218
|
+
const answerCount = (text) => {
|
|
1219
|
+
const program = Program.parse('', { isoStrict: true });
|
|
1220
|
+
const solver = new Solver(program, { isoStrict: true });
|
|
1221
|
+
return [...solver.solve([parseGoalText(text, {
|
|
1222
|
+
isoStrict: true,
|
|
1223
|
+
operatorDefinitions: [...program.operators.values()],
|
|
1224
|
+
})], new Env(), 0)].length;
|
|
1225
|
+
};
|
|
1226
|
+
equal(answerCount('call((Z=!, (X=1;X=2), Z)) ; T=end'), 3,
|
|
1227
|
+
'call/1 converts an initially unbound variable goal before it can become a cut (issue #86)');
|
|
1228
|
+
equal(answerCount('call((Z=!, (X=1;X=2), Z, X=2))'), 1,
|
|
1229
|
+
'call(Var) cut remains opaque to choices made earlier in the converted body');
|
|
1230
|
+
equal(answerCount('Z=!, (call(((X=1;X=2),Z)) ; T=end)'), 2,
|
|
1231
|
+
'a variable already bound before call/1 conversion is dereferenced as a direct cut');
|
|
1217
1232
|
});
|
|
1218
1233
|
|
|
1219
1234
|
reporter.test('rejects EyeProlog module directives', () => {
|
package/test/run-regression.mjs
CHANGED
|
@@ -1116,6 +1116,31 @@ c4 ?- call((!;1)).
|
|
|
1116
1116
|
assertEqual(result.stdout, 'quads: 22 run, 22 passed, 0 failed.\n', 'quad report');
|
|
1117
1117
|
},
|
|
1118
1118
|
},
|
|
1119
|
+
{
|
|
1120
|
+
name: 'call/1 converts variable goals before execution so later cuts stay opaque (issue #86)',
|
|
1121
|
+
run: () => {
|
|
1122
|
+
const quad = publicApi.runQuads(`?- call((Z = !,(X=1;X=2), Z)) ; T=end.\n` +
|
|
1123
|
+
` Z = !, X = 1\n` +
|
|
1124
|
+
`; Z = !, X = 2\n` +
|
|
1125
|
+
`; T = end.\n`);
|
|
1126
|
+
assertEqual(quad.total, 1, 'issue #86 quad total');
|
|
1127
|
+
assertEqual(quad.passed, 1, 'issue #86 exact answer sequence');
|
|
1128
|
+
assertEqual(quad.failed, 0, 'issue #86 quad failures');
|
|
1129
|
+
|
|
1130
|
+
const program = Program.parse('');
|
|
1131
|
+
const answerCount = (text) => {
|
|
1132
|
+
const solver = new Solver(program, { registry: getEyePrologRegistry() });
|
|
1133
|
+
return [...solver.solve([parseGoalText(text)], new Env(), 0)].length;
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
assertEqual(answerCount('call((Z=!, (X=1;X=2), Z)) ; T=end'), 3,
|
|
1137
|
+
'issue #86 outer answer count');
|
|
1138
|
+
assertEqual(answerCount('call((Z=!, (X=1;X=2), Z, X=2))'), 1,
|
|
1139
|
+
'later call(!) does not prune the X=2 alternative');
|
|
1140
|
+
assertEqual(answerCount('Z=!, (call(((X=1;X=2),Z)) ; T=end)'), 2,
|
|
1141
|
+
'pre-bound cut stays direct inside the meta-call');
|
|
1142
|
+
},
|
|
1143
|
+
},
|
|
1119
1144
|
{
|
|
1120
1145
|
name: 'negation observes disjunction through direct and call/1 execution',
|
|
1121
1146
|
run: () => {
|