eyeprolog 1.3.94 → 1.3.95
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/package.json +1 -1
- package/src/quads.js +14 -6
- package/test/run-regression.mjs +58 -0
- package/the-art-of-eyeprolog.md +6 -2
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
|
@@ -951,6 +951,64 @@ why(
|
|
|
951
951
|
assertEqual(result.stdout, 'quads: 13 run, 13 passed, 0 failed.\n', 'quad report');
|
|
952
952
|
},
|
|
953
953
|
},
|
|
954
|
+
{
|
|
955
|
+
name: 'runQuads treats unexpected as a negative assertion on the next leaf (issue #83)',
|
|
956
|
+
run: () => {
|
|
957
|
+
const source = `:- use_module(library(prologue)).
|
|
958
|
+
` +
|
|
959
|
+
`40
|
|
960
|
+
` +
|
|
961
|
+
`?- member(X,"abc").
|
|
962
|
+
` +
|
|
963
|
+
` X = a
|
|
964
|
+
` +
|
|
965
|
+
`; X = b
|
|
966
|
+
` +
|
|
967
|
+
`; X = c.
|
|
968
|
+
` +
|
|
969
|
+
` X = a
|
|
970
|
+
` +
|
|
971
|
+
`; X = c, unexpected.
|
|
972
|
+
|
|
973
|
+
` +
|
|
974
|
+
`41
|
|
975
|
+
` +
|
|
976
|
+
`?- member(X, Xs).
|
|
977
|
+
` +
|
|
978
|
+
` Xs = [X|_A]
|
|
979
|
+
` +
|
|
980
|
+
`; ... .
|
|
981
|
+
` +
|
|
982
|
+
` Xs = [X|_A]
|
|
983
|
+
` +
|
|
984
|
+
`; Xs = [_A,X|_B]
|
|
985
|
+
` +
|
|
986
|
+
`; Xs = [_A,_B,X|_C]
|
|
987
|
+
` +
|
|
988
|
+
`; ... .
|
|
989
|
+
` +
|
|
990
|
+
` Xs = [X|_A]
|
|
991
|
+
` +
|
|
992
|
+
`; Xs = [], unexpected.
|
|
993
|
+
`;
|
|
994
|
+
const result = publicApi.runQuads(Program.parseSources([{ text: source, filename: 'unexpected-quad.pl' }]));
|
|
995
|
+
assertEqual(result.total, 5, 'quad total');
|
|
996
|
+
assertEqual(result.passed, 5, 'quad passed');
|
|
997
|
+
assertEqual(result.failed, 0, 'quad failed');
|
|
998
|
+
assertEqual(result.stdout, 'quads: 5 run, 5 passed, 0 failed.\n', 'quad report');
|
|
999
|
+
|
|
1000
|
+
const forbidden = publicApi.runQuads(`:- use_module(library(prologue)).
|
|
1001
|
+
` +
|
|
1002
|
+
`?- member(X,[a,c]).
|
|
1003
|
+
` +
|
|
1004
|
+
` X = a
|
|
1005
|
+
` +
|
|
1006
|
+
`; X = c, unexpected.
|
|
1007
|
+
`);
|
|
1008
|
+
assertEqual(forbidden.total, 1, 'forbidden next answer total');
|
|
1009
|
+
assertEqual(forbidden.failed, 1, 'matching unexpected next answer fails');
|
|
1010
|
+
},
|
|
1011
|
+
},
|
|
954
1012
|
{
|
|
955
1013
|
name: 'runQuads distinguishes query variables from renamed throw variables',
|
|
956
1014
|
run: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -7045,8 +7045,12 @@ accepted as a negative answer.
|
|
|
7045
7045
|
Answer descriptions support ordered answers separated by `;`, acceptable
|
|
7046
7046
|
alternatives separated by `|`, `true`, `false`, standard error descriptions,
|
|
7047
7047
|
and the `unexpected` annotation for an answer that must not occur (`inattendue`
|
|
7048
|
-
is its synonym).
|
|
7049
|
-
|
|
7048
|
+
is its synonym). In an ordered answer sequence, `unexpected` is a negative
|
|
7049
|
+
assertion about the answer at that position: once the next observed answer does
|
|
7050
|
+
not match the annotated leaf, that description succeeds and does not require the
|
|
7051
|
+
query to have no further answers. Variables named in the query keep their
|
|
7052
|
+
identity inside answer descriptions; variables introduced only by a description
|
|
7053
|
+
are fresh. For example,
|
|
7050
7054
|
a query `throw(g(X))` is described by `throw(g(_X))`, while
|
|
7051
7055
|
`throw(g(X)), unexpected` verifies that ISO `throw/1` did not retain the query
|
|
7052
7056
|
variable in the renamed exception term. `...` and `ad_infinitum` accept further answers. The `maybe` annotation describes
|