eyeprolog 1.5.97 → 1.5.98
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 +21 -2
- package/test/regression/cases-regression.mjs +20 -0
package/package.json
CHANGED
package/src/quads.js
CHANGED
|
@@ -322,6 +322,7 @@ function describeLeaf(term) {
|
|
|
322
322
|
const leaf = {
|
|
323
323
|
bindings: [],
|
|
324
324
|
approximations: [],
|
|
325
|
+
literalApproximations: [],
|
|
325
326
|
unexpected: false,
|
|
326
327
|
more: false,
|
|
327
328
|
sto: false,
|
|
@@ -360,8 +361,21 @@ function describeLeaf(term) {
|
|
|
360
361
|
continue;
|
|
361
362
|
}
|
|
362
363
|
if (item.type === COMPOUND && item.name === '~~' && item.arity === 2) {
|
|
363
|
-
|
|
364
|
-
|
|
364
|
+
// The left-hand side names either one of the query's own variables
|
|
365
|
+
// (the usual case: compare what the query actually bound it to) or,
|
|
366
|
+
// just as legitimately, a literal number written directly in the
|
|
367
|
+
// answer description -- a free-standing "this value approximately
|
|
368
|
+
// denotes this decimal interval" claim with no query variable
|
|
369
|
+
// involved at all (issue #113: `0.04 ~~ '0.0'` was rejected as
|
|
370
|
+
// malformed purely for lacking a variable on the left, with no
|
|
371
|
+
// deeper reason for that restriction). Both are validated the same
|
|
372
|
+
// way one level up, in matchLeaf/substitutionMatches: syntactic
|
|
373
|
+
// acceptance here does not yet mean the value actually falls in the
|
|
374
|
+
// stated interval.
|
|
375
|
+
const lhsIsVarOrNumber = item.args[0].type === VAR || item.args[0].type === NUMBER;
|
|
376
|
+
if (!lhsIsVarOrNumber || approximateDecimalInterval(item.args[1]) == null) leaf.malformed ??= item;
|
|
377
|
+
else if (item.args[0].type === VAR) leaf.approximations.push(item);
|
|
378
|
+
else leaf.literalApproximations.push(item);
|
|
365
379
|
continue;
|
|
366
380
|
}
|
|
367
381
|
if (item.type === COMPOUND && item.name === 'inputs' && item.arity === 1) {
|
|
@@ -385,6 +399,7 @@ function describeLeaf(term) {
|
|
|
385
399
|
else leaf.malformed ??= item;
|
|
386
400
|
}
|
|
387
401
|
leaf.hasExpectation = leaf.bindings.length > 0 || leaf.approximations.length > 0 ||
|
|
402
|
+
leaf.literalApproximations.length > 0 ||
|
|
388
403
|
leaf.truth || leaf.false || leaf.maybe || leaf.loops || leaf.waits ||
|
|
389
404
|
leaf.error != null || leaf.output != null;
|
|
390
405
|
if (!leaf.hasExpectation && !leaf.more && !leaf.sto && leaf.unsupported == null) leaf.malformed ??= term;
|
|
@@ -528,6 +543,10 @@ function matchLeaf(program, query, leaf, actual, position) {
|
|
|
528
543
|
// its absence requires an unconstrained answer. In either case the stated
|
|
529
544
|
// substitutions remain exact and are checked below.
|
|
530
545
|
if (leaf.maybe !== hasPendingConstraints(solution.env)) return false;
|
|
546
|
+
// Literal `~~` claims (issue #113) name no query variable at all, so
|
|
547
|
+
// they carry no per-position runtime state to look up; they either hold
|
|
548
|
+
// or they don't, the same at every position.
|
|
549
|
+
if (leaf.literalApproximations.some((item) => !approximatelyMatches(item.args[0], item.args[1]))) return false;
|
|
531
550
|
return substitutionMatches(query, leaf.bindings, leaf.approximations, solution.env);
|
|
532
551
|
}
|
|
533
552
|
|
|
@@ -1195,6 +1195,26 @@ why(
|
|
|
1195
1195
|
assertEqual(malformedAtom.failed, 1, 'non-decimal atom is rejected');
|
|
1196
1196
|
assertIncludes(malformedAtom.stdout, 'MALFORMED', 'non-decimal atom diagnostic');
|
|
1197
1197
|
|
|
1198
|
+
// Issue #113: a literal number on the left of `~~` names no query
|
|
1199
|
+
// variable at all -- it is a free-standing "this value approximately
|
|
1200
|
+
// denotes this decimal interval" claim, and there is no deeper reason
|
|
1201
|
+
// to require a variable there. It must be accepted (and actually
|
|
1202
|
+
// checked against the interval, not accepted unconditionally), the
|
|
1203
|
+
// same as the query-variable form already was.
|
|
1204
|
+
const literalOk = publicApi.runQuads('?- true.\n 0.04 ~~ \'0.0\'.\n');
|
|
1205
|
+
assertEqual(literalOk.total, 1, 'literal approximation total');
|
|
1206
|
+
assertEqual(literalOk.passed, 1, 'literal approximation passed');
|
|
1207
|
+
assertEqual(literalOk.stdout, 'quads: 1 run, 1 passed, 0 failed.\n', 'literal approximation report');
|
|
1208
|
+
|
|
1209
|
+
const literalOutOfRange = publicApi.runQuads('?- true.\n 0.04 ~~ \'5.0\'.\n');
|
|
1210
|
+
assertEqual(literalOutOfRange.failed, 1, 'literal outside the claimed interval fails, not malformed');
|
|
1211
|
+
assertIncludes(literalOutOfRange.stdout, 'quads: FAILED', 'literal out-of-range diagnostic');
|
|
1212
|
+
assertNotIncludes(literalOutOfRange.stdout, 'MALFORMED', 'literal out-of-range is not malformed');
|
|
1213
|
+
|
|
1214
|
+
const literalNonNumberLhs = publicApi.runQuads('?- true.\n foo ~~ \'0.0\'.\n');
|
|
1215
|
+
assertEqual(literalNonNumberLhs.failed, 1, 'a non-variable, non-number left side remains malformed');
|
|
1216
|
+
assertIncludes(literalNonNumberLhs.stdout, 'MALFORMED', 'non-number left side diagnostic');
|
|
1217
|
+
|
|
1198
1218
|
const duplicate = publicApi.runQuads(`?- V is 14.2.
|
|
1199
1219
|
V = 14.2, V ~~ '14.2000'.
|
|
1200
1220
|
`);
|