eyeprolog 1.3.94 → 1.3.96
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/examples/book/README.md +7 -6
- package/examples/book/chapter-39/01-program.pl +4 -0
- package/examples/book/chapter-39/{01-answer.pl → 02-answer.pl} +1 -1
- package/package.json +1 -1
- package/src/quads.js +14 -6
- package/test/run-regression.mjs +72 -0
- package/the-art-of-eyeprolog.md +15 -3
- /package/examples/book/chapter-39/{02-answer-2.pl → 03-answer-2.pl} +0 -0
- /package/examples/book/chapter-39/{03-answer-3.pl → 04-answer-3.pl} +0 -0
- /package/examples/book/chapter-39/{04-cost.pl → 05-cost.pl} +0 -0
- /package/examples/book/chapter-39/{05-message.pl → 06-message.pl} +0 -0
- /package/examples/book/chapter-39/{06-task.pl → 07-task.pl} +0 -0
package/examples/book/README.md
CHANGED
|
@@ -238,12 +238,13 @@ npm run generate
|
|
|
238
238
|
|
|
239
239
|
## Chapter 39: Built-in predicates by programming role
|
|
240
240
|
|
|
241
|
-
- [01-
|
|
242
|
-
- [02-answer
|
|
243
|
-
- [03-answer-
|
|
244
|
-
- [04-
|
|
245
|
-
- [05-
|
|
246
|
-
- [06-
|
|
241
|
+
- [01-program.pl](chapter-39/01-program.pl) — The EyeProlog library
|
|
242
|
+
- [02-answer.pl](chapter-39/02-answer.pl)
|
|
243
|
+
- [03-answer-2.pl](chapter-39/03-answer-2.pl)
|
|
244
|
+
- [04-answer-3.pl](chapter-39/04-answer-3.pl)
|
|
245
|
+
- [05-cost.pl](chapter-39/05-cost.pl)
|
|
246
|
+
- [06-message.pl](chapter-39/06-message.pl)
|
|
247
|
+
- [07-task.pl](chapter-39/07-task.pl)
|
|
247
248
|
|
|
248
249
|
## Chapter 40: Running EyeProlog: command line and corpus
|
|
249
250
|
|
package/package.json
CHANGED
package/src/quads.js
CHANGED
|
@@ -152,17 +152,25 @@ function checkAlternative(program, quad, alternative, options, context) {
|
|
|
152
152
|
// portably outlaw the implementation's chosen STO outcome. This is the
|
|
153
153
|
// case behind issue #60's `false, unexpected` example.
|
|
154
154
|
const stoPermitsUnexpected = context.declaresSto && actual.stoObserved && leaf.unexpected && !leaf.sto;
|
|
155
|
-
if (
|
|
155
|
+
if (leaf.unexpected) {
|
|
156
|
+
// `unexpected` is a negative assertion about the leaf at this answer
|
|
157
|
+
// position. Once the observed answer differs, the assertion is proved;
|
|
158
|
+
// it does not consume that answer or require the query to end afterward.
|
|
159
|
+
// Likewise, an STO-declared query may accept an implementation-dependent
|
|
160
|
+
// answer at this point without imposing any later answer-sequence check.
|
|
161
|
+
if (stoPermitsUnexpected) return { ok: true };
|
|
162
|
+
if (matches) return { ok: false };
|
|
163
|
+
if (actual.undecided && leafNeedsMoreSearch(leaf, actual, position)) {
|
|
164
|
+
return undecidedResult(actual, alternative);
|
|
165
|
+
}
|
|
166
|
+
return { ok: true };
|
|
167
|
+
}
|
|
168
|
+
if (!matches) {
|
|
156
169
|
if (actual.undecided && leafNeedsMoreSearch(leaf, actual, position)) {
|
|
157
170
|
return undecidedResult(actual, alternative);
|
|
158
171
|
}
|
|
159
172
|
return { ok: false };
|
|
160
173
|
}
|
|
161
|
-
// An unexpected error description is a negative assertion about that
|
|
162
|
-
// particular error pattern. A different exception may be present and is
|
|
163
|
-
// checked by other descriptions; do not make the final 'no error' test
|
|
164
|
-
// turn a successful negative match back into a failure.
|
|
165
|
-
if (leaf.unexpected && leaf.error != null) return { ok: true };
|
|
166
174
|
if (leaf.more) return { ok: true };
|
|
167
175
|
if (!leaf.unexpected && (leaf.false || leaf.loops || leaf.error != null)) {
|
|
168
176
|
if (actual.undecided) return undecidedResult(actual, alternative);
|
package/test/run-regression.mjs
CHANGED
|
@@ -400,6 +400,20 @@ function regressionCases() {
|
|
|
400
400
|
'dif/2 residuals are minimal without auxiliary terms');
|
|
401
401
|
assertEqual(residuals.stderr, '', 'dif/2 normalization display stderr');
|
|
402
402
|
|
|
403
|
+
const perfection = runCli([], {
|
|
404
|
+
input:
|
|
405
|
+
'dif(f(X,A),f(Y,B)), ( true ; A = B ).\n' +
|
|
406
|
+
';\n' +
|
|
407
|
+
'halt.\n',
|
|
408
|
+
});
|
|
409
|
+
assertEqual(perfection.status, 0, 'dif/2 branch-local projection status');
|
|
410
|
+
assertEqual(perfection.stdout,
|
|
411
|
+
'?- dif(f(X, A), f(Y, B))\n' +
|
|
412
|
+
'; A = B, dif(X, Y).\n' +
|
|
413
|
+
'?- ',
|
|
414
|
+
'dif/2 residual projection is re-evaluated after branch-local bindings');
|
|
415
|
+
assertEqual(perfection.stderr, '', 'dif/2 branch-local projection stderr');
|
|
416
|
+
|
|
403
417
|
const pendingDifCount = (text) => {
|
|
404
418
|
const solver = new Solver(Program.parse(''), { registry: getEyePrologRegistry() });
|
|
405
419
|
const solutions = solver.solve([parseGoalText(text)], new Env(), 0);
|
|
@@ -951,6 +965,64 @@ why(
|
|
|
951
965
|
assertEqual(result.stdout, 'quads: 13 run, 13 passed, 0 failed.\n', 'quad report');
|
|
952
966
|
},
|
|
953
967
|
},
|
|
968
|
+
{
|
|
969
|
+
name: 'runQuads treats unexpected as a negative assertion on the next leaf (issue #83)',
|
|
970
|
+
run: () => {
|
|
971
|
+
const source = `:- use_module(library(prologue)).
|
|
972
|
+
` +
|
|
973
|
+
`40
|
|
974
|
+
` +
|
|
975
|
+
`?- member(X,"abc").
|
|
976
|
+
` +
|
|
977
|
+
` X = a
|
|
978
|
+
` +
|
|
979
|
+
`; X = b
|
|
980
|
+
` +
|
|
981
|
+
`; X = c.
|
|
982
|
+
` +
|
|
983
|
+
` X = a
|
|
984
|
+
` +
|
|
985
|
+
`; X = c, unexpected.
|
|
986
|
+
|
|
987
|
+
` +
|
|
988
|
+
`41
|
|
989
|
+
` +
|
|
990
|
+
`?- member(X, Xs).
|
|
991
|
+
` +
|
|
992
|
+
` Xs = [X|_A]
|
|
993
|
+
` +
|
|
994
|
+
`; ... .
|
|
995
|
+
` +
|
|
996
|
+
` Xs = [X|_A]
|
|
997
|
+
` +
|
|
998
|
+
`; Xs = [_A,X|_B]
|
|
999
|
+
` +
|
|
1000
|
+
`; Xs = [_A,_B,X|_C]
|
|
1001
|
+
` +
|
|
1002
|
+
`; ... .
|
|
1003
|
+
` +
|
|
1004
|
+
` Xs = [X|_A]
|
|
1005
|
+
` +
|
|
1006
|
+
`; Xs = [], unexpected.
|
|
1007
|
+
`;
|
|
1008
|
+
const result = publicApi.runQuads(Program.parseSources([{ text: source, filename: 'unexpected-quad.pl' }]));
|
|
1009
|
+
assertEqual(result.total, 5, 'quad total');
|
|
1010
|
+
assertEqual(result.passed, 5, 'quad passed');
|
|
1011
|
+
assertEqual(result.failed, 0, 'quad failed');
|
|
1012
|
+
assertEqual(result.stdout, 'quads: 5 run, 5 passed, 0 failed.\n', 'quad report');
|
|
1013
|
+
|
|
1014
|
+
const forbidden = publicApi.runQuads(`:- use_module(library(prologue)).
|
|
1015
|
+
` +
|
|
1016
|
+
`?- member(X,[a,c]).
|
|
1017
|
+
` +
|
|
1018
|
+
` X = a
|
|
1019
|
+
` +
|
|
1020
|
+
`; X = c, unexpected.
|
|
1021
|
+
`);
|
|
1022
|
+
assertEqual(forbidden.total, 1, 'forbidden next answer total');
|
|
1023
|
+
assertEqual(forbidden.failed, 1, 'matching unexpected next answer fails');
|
|
1024
|
+
},
|
|
1025
|
+
},
|
|
954
1026
|
{
|
|
955
1027
|
name: 'runQuads distinguishes query variables from renamed throw variables',
|
|
956
1028
|
run: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -6293,7 +6293,15 @@ answers `Y=1`, `Y=2`, and `Y=3`.
|
|
|
6293
6293
|
can still unify. Its residual store is normalized by logical implication:
|
|
6294
6294
|
symmetric or equivalent constraints share one residual, and a stronger
|
|
6295
6295
|
constraint removes weaker ones regardless of insertion order. Independent
|
|
6296
|
-
disequalities remain separate.
|
|
6296
|
+
disequalities remain separate. Residual projection is re-evaluated in each
|
|
6297
|
+
solution environment, so a compound disequality is kept intact until later
|
|
6298
|
+
bindings make one aligned subterm pair sufficient. For example:
|
|
6299
|
+
|
|
6300
|
+
```eyeprolog
|
|
6301
|
+
?- dif(f(X,A),f(Y,B)), ( true ; A = B ).
|
|
6302
|
+
dif(f(X, A), f(Y, B))
|
|
6303
|
+
; A = B, dif(X, Y).
|
|
6304
|
+
```
|
|
6297
6305
|
|
|
6298
6306
|
<!-- eyeprolog-library-catalog:start -->
|
|
6299
6307
|
|
|
@@ -7045,8 +7053,12 @@ accepted as a negative answer.
|
|
|
7045
7053
|
Answer descriptions support ordered answers separated by `;`, acceptable
|
|
7046
7054
|
alternatives separated by `|`, `true`, `false`, standard error descriptions,
|
|
7047
7055
|
and the `unexpected` annotation for an answer that must not occur (`inattendue`
|
|
7048
|
-
is its synonym).
|
|
7049
|
-
|
|
7056
|
+
is its synonym). In an ordered answer sequence, `unexpected` is a negative
|
|
7057
|
+
assertion about the answer at that position: once the next observed answer does
|
|
7058
|
+
not match the annotated leaf, that description succeeds and does not require the
|
|
7059
|
+
query to have no further answers. Variables named in the query keep their
|
|
7060
|
+
identity inside answer descriptions; variables introduced only by a description
|
|
7061
|
+
are fresh. For example,
|
|
7050
7062
|
a query `throw(g(X))` is described by `throw(g(_X))`, while
|
|
7051
7063
|
`throw(g(X)), unexpected` verifies that ISO `throw/1` did not retain the query
|
|
7052
7064
|
variable in the renamed exception term. `...` and `ad_infinitum` accept further answers. The `maybe` annotation describes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|