eyeprolog 1.1.11 → 1.1.13
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 +7 -5
- package/package.json +1 -1
- package/src/iso.js +170 -7
- package/src/lib/eyeprolog.pl +15 -1
- package/src/lib/prologue.pl +105 -1
- package/src/standard-library.js +12 -6
- package/test/fixtures/number_chars_cont_quad.pl +235 -0
- package/test/fixtures/prologue_call_nth_quad.pl +32 -0
- package/test/fixtures/prologue_call_nth_quad_runner.pl +2 -0
- package/test/fixtures/prologue_extended_quad.pl +87 -0
- package/test/fixtures/prologue_extended_quad_runner.pl +13 -0
- package/test/run-regression.mjs +54 -7
- package/the-art-of-eyeprolog.md +17 -14
package/README.md
CHANGED
|
@@ -91,12 +91,14 @@ noun --> [world] | [prolog].
|
|
|
91
91
|
%% goal: phrase(sentence, Words)
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
EyeProlog also adds
|
|
95
|
-
**
|
|
96
|
-
`src/lib/lists.pl`, and `src/lib/prologue.pl
|
|
94
|
+
EyeProlog also adds 57 public library predicate indicators to its 129-entry ISO
|
|
95
|
+
profile. **56 are implemented entirely as ordinary Prolog clauses** in
|
|
96
|
+
`src/lib/eyeprolog.pl`, `src/lib/lists.pl`, and `src/lib/prologue.pl`; the
|
|
97
|
+
`call_nth/2` module clause uses one private host adapter to number solutions.
|
|
98
|
+
They are ISO/IEC 13211-2 modules,
|
|
97
99
|
loaded explicitly with `use_module(library(eyeprolog))`,
|
|
98
|
-
`use_module(library(lists))`, or `use_module(library(prologue))`.
|
|
99
|
-
|
|
100
|
+
`use_module(library(lists))`, or `use_module(library(prologue))`. Portable text
|
|
101
|
+
predicates use ISO atoms or character lists.
|
|
100
102
|
|
|
101
103
|
## Development
|
|
102
104
|
|
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isDecimalInteger, listFromItems, numberTerm, numberTextFromDouble,
|
|
6
6
|
properListItems, termIsGround, termToString, unify, variable, variantTerms,
|
|
7
7
|
} from './term.js';
|
|
8
|
-
import { createParserOperatorState, parseClauses } from './parser.js';
|
|
8
|
+
import { createParserOperatorState, parseClauses, parseGoalText } from './parser.js';
|
|
9
9
|
import { formatTermForWrite } from './write.js';
|
|
10
10
|
import { emptyTerminalSequence, expandDcgBody, isListOrPartialList, validateDcgEmbeddedGoals } from './dcg.js';
|
|
11
11
|
|
|
@@ -164,6 +164,15 @@ export const isoBuiltins = {
|
|
|
164
164
|
}
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
// call_nth/2 is a Prologue library predicate with control behavior that cannot
|
|
168
|
+
// be expressed portably as ordinary Prolog clauses. Keep the public wrapper
|
|
169
|
+
// in library(prologue) and expose only this private adapter to the host registry.
|
|
170
|
+
export const eyePrologLibraryBuiltins = {
|
|
171
|
+
register(registry) {
|
|
172
|
+
registry.add('eyeprolog__call_nth', 2, callNthBuiltin, { eyePrologLibrary: true });
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
167
176
|
function* unification({ goal, env }) {
|
|
168
177
|
const next = env.clone();
|
|
169
178
|
if (unify(goal.args[0], goal.args[1], next)) yield next;
|
|
@@ -1397,12 +1406,129 @@ function* charCodeBuiltin({ goal, env }) {
|
|
|
1397
1406
|
} else if (unify(goal.args[0], atom(String.fromCodePoint(Number(code.name))), next)) yield next;
|
|
1398
1407
|
}
|
|
1399
1408
|
|
|
1409
|
+
function skipNumberLayout(text, start) {
|
|
1410
|
+
let position = start;
|
|
1411
|
+
while (true) {
|
|
1412
|
+
while (position < text.length && /[\u0009-\u000d\u0020]/.test(text[position])) {
|
|
1413
|
+
position++;
|
|
1414
|
+
}
|
|
1415
|
+
if (text[position] === '%') {
|
|
1416
|
+
const newline = text.indexOf('\n', position + 1);
|
|
1417
|
+
if (newline < 0) return text.length;
|
|
1418
|
+
position = newline + 1;
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
if (text.startsWith('/*', position)) {
|
|
1422
|
+
const end = text.indexOf('*/', position + 2);
|
|
1423
|
+
if (end < 0) return text.length;
|
|
1424
|
+
position = end + 2;
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
return position;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function quotedNumberSign(text, start) {
|
|
1432
|
+
if (text[start] !== "'") return null;
|
|
1433
|
+
let position = start + 1;
|
|
1434
|
+
let value = '';
|
|
1435
|
+
while (position < text.length) {
|
|
1436
|
+
let character = text[position++];
|
|
1437
|
+
if (character === "'") {
|
|
1438
|
+
if (text[position] === "'") {
|
|
1439
|
+
position++;
|
|
1440
|
+
value += "'";
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
return { value, position };
|
|
1444
|
+
}
|
|
1445
|
+
if (character !== '\\') {
|
|
1446
|
+
value += character;
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
if (position >= text.length) return null;
|
|
1450
|
+
character = text[position++];
|
|
1451
|
+
if (character === '\n') continue;
|
|
1452
|
+
const controls = { a: '\x07', b: '\b', r: '\r', f: '\f', t: '\t', n: '\n', v: '\v' };
|
|
1453
|
+
if (controls[character] != null) {
|
|
1454
|
+
value += controls[character];
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
value += character;
|
|
1458
|
+
}
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1400
1462
|
function parseIsoNumber(text) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
if (
|
|
1404
|
-
|
|
1405
|
-
|
|
1463
|
+
// number_chars/2 accepts leading layout, but its character list must end
|
|
1464
|
+
// with the numeric token itself rather than trailing layout text.
|
|
1465
|
+
if (text.length === 0 || /[\u0009-\u000d\u0020]$/.test(text)) return null;
|
|
1466
|
+
let position = skipNumberLayout(text, 0);
|
|
1467
|
+
let sign = '';
|
|
1468
|
+
|
|
1469
|
+
if (text[position] === '-') {
|
|
1470
|
+
if (/[\u0009-\u000d\u0020]/.test(text[position + 1] ?? '')) {
|
|
1471
|
+
position = skipNumberLayout(text, position + 1);
|
|
1472
|
+
sign = '-';
|
|
1473
|
+
}
|
|
1474
|
+
} else {
|
|
1475
|
+
const quoted = quotedNumberSign(text, position);
|
|
1476
|
+
if (quoted?.value === '-') {
|
|
1477
|
+
position = skipNumberLayout(text, quoted.position);
|
|
1478
|
+
sign = '-';
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const numericText = `${sign}${text.slice(position)}`;
|
|
1483
|
+
// ISO floating-point syntax requires a decimal fraction before an exponent.
|
|
1484
|
+
if (/^-?\d+[eE][+-]?\d+$/.test(numericText)) return null;
|
|
1485
|
+
try {
|
|
1486
|
+
const parsed = parseGoalText(`number_chars_value(${numericText})`);
|
|
1487
|
+
if (parsed.type !== COMPOUND || parsed.name !== 'number_chars_value' ||
|
|
1488
|
+
parsed.arity !== 1 || parsed.args[0].type !== NUMBER) return null;
|
|
1489
|
+
const value = parsed.args[0];
|
|
1490
|
+
if (isDecimalInteger(value.name)) return numberTerm(BigInt(value.name).toString());
|
|
1491
|
+
const finite = Number(value.name);
|
|
1492
|
+
if (!Number.isFinite(finite)) return null;
|
|
1493
|
+
return numberTerm(numberTextFromDouble(finite));
|
|
1494
|
+
} catch (_) {
|
|
1495
|
+
return null;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function sameNumber(left, right) {
|
|
1500
|
+
const leftInteger = isDecimalInteger(left.name);
|
|
1501
|
+
const rightInteger = isDecimalInteger(right.name);
|
|
1502
|
+
if (leftInteger || rightInteger) {
|
|
1503
|
+
return leftInteger && rightInteger && BigInt(left.name) === BigInt(right.name);
|
|
1504
|
+
}
|
|
1505
|
+
const leftValue = Number(left.name);
|
|
1506
|
+
const rightValue = Number(right.name);
|
|
1507
|
+
return Number.isFinite(leftValue) && Number.isFinite(rightValue) && leftValue === rightValue;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function numberListText(list, env, kind, valueIsBound) {
|
|
1511
|
+
const whole = deref(list, env);
|
|
1512
|
+
const { items, tail } = listElements(list, env);
|
|
1513
|
+
const proper = tail.type === ATOM && tail.name === '[]';
|
|
1514
|
+
if (tail.type !== VAR && !proper) throw new PrologError('type_error(list)', whole);
|
|
1515
|
+
|
|
1516
|
+
const invalid = items.find((item) => item.type !== VAR &&
|
|
1517
|
+
(kind === 'chars' ? !oneChar(item) : !validCharacterCode(item)));
|
|
1518
|
+
if (invalid) {
|
|
1519
|
+
if (kind === 'chars') throw new PrologError('type_error(character)', invalid);
|
|
1520
|
+
if (invalid.type !== NUMBER || !isDecimalInteger(invalid.name)) {
|
|
1521
|
+
throw new PrologError('type_error(integer)', invalid);
|
|
1522
|
+
}
|
|
1523
|
+
throw new PrologError('representation_error(character_code)');
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const hasVariable = tail.type === VAR || items.some((item) => item.type === VAR);
|
|
1527
|
+
if (!valueIsBound && hasVariable) throw new PrologError('instantiation_error');
|
|
1528
|
+
if (hasVariable) return null;
|
|
1529
|
+
return items.map((item) => kind === 'chars'
|
|
1530
|
+
? item.name
|
|
1531
|
+
: String.fromCodePoint(Number(item.name))).join('');
|
|
1406
1532
|
}
|
|
1407
1533
|
|
|
1408
1534
|
function numberListBuiltin(kind) {
|
|
@@ -1411,14 +1537,21 @@ function numberListBuiltin(kind) {
|
|
|
1411
1537
|
if (value.type !== VAR && value.type !== NUMBER) throw new PrologError('type_error(number)', value);
|
|
1412
1538
|
const list = deref(goal.args[1], env);
|
|
1413
1539
|
if (value.type === VAR && list.type === VAR) throw new PrologError('instantiation_error');
|
|
1540
|
+
const text = numberListText(list, env, kind, value.type === NUMBER);
|
|
1414
1541
|
const next = env.clone();
|
|
1415
1542
|
if (value.type === NUMBER) {
|
|
1543
|
+
if (text != null) {
|
|
1544
|
+
const parsed = parseIsoNumber(text);
|
|
1545
|
+
if (parsed == null) throw new PrologError('syntax_error(number)');
|
|
1546
|
+
if (sameNumber(value, parsed)) yield next;
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1416
1549
|
const items = characters(value.name).map((ch) =>
|
|
1417
1550
|
kind === 'chars' ? atom(ch) : numberTerm(ch.codePointAt(0)));
|
|
1418
1551
|
if (unify(goal.args[1], listFromItems(items), next)) yield next;
|
|
1419
1552
|
return;
|
|
1420
1553
|
}
|
|
1421
|
-
const parsed = parseIsoNumber(
|
|
1554
|
+
const parsed = parseIsoNumber(text);
|
|
1422
1555
|
if (parsed == null) throw new PrologError('syntax_error(number)');
|
|
1423
1556
|
if (unify(goal.args[0], parsed, next)) yield next;
|
|
1424
1557
|
};
|
|
@@ -1564,6 +1697,36 @@ function* callClosureBuiltin({ solver, goal, env }) {
|
|
|
1564
1697
|
}
|
|
1565
1698
|
}
|
|
1566
1699
|
|
|
1700
|
+
function* callNthBuiltin({ solver, goal, env }) {
|
|
1701
|
+
const requestedTerm = deref(goal.args[1], env);
|
|
1702
|
+
// Zero is the one Nth value that fails before Goal is inspected.
|
|
1703
|
+
if (requestedTerm.type === NUMBER && isDecimalInteger(requestedTerm.name) && BigInt(requestedTerm.name) === 0n) return;
|
|
1704
|
+
|
|
1705
|
+
const invoked = callable(goal.args[0], env);
|
|
1706
|
+
let requested = null;
|
|
1707
|
+
if (requestedTerm.type !== VAR) {
|
|
1708
|
+
if (requestedTerm.type !== NUMBER || !isDecimalInteger(requestedTerm.name)) {
|
|
1709
|
+
throw new PrologError('type_error(integer)', requestedTerm);
|
|
1710
|
+
}
|
|
1711
|
+
requested = BigInt(requestedTerm.name);
|
|
1712
|
+
if (requested < 0n) throw new PrologError('domain_error(not_less_than_zero)', requestedTerm);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
const child = solver.cloneForInnerGoal();
|
|
1716
|
+
let nth = 0n;
|
|
1717
|
+
try {
|
|
1718
|
+
for (const answerEnv of child.solve([invoked], env, 0)) {
|
|
1719
|
+
nth++;
|
|
1720
|
+
if (requested != null && nth < requested) continue;
|
|
1721
|
+
const next = answerEnv.clone();
|
|
1722
|
+
if (unify(goal.args[1], numberTerm(nth.toString()), next)) yield next;
|
|
1723
|
+
if (requested != null) return;
|
|
1724
|
+
}
|
|
1725
|
+
} finally {
|
|
1726
|
+
solver.absorbStatsFrom(child);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1567
1730
|
function* phraseBuiltin({ solver, goal, env }) {
|
|
1568
1731
|
const grammarBody = deref(goal.args[0], env);
|
|
1569
1732
|
if (grammarBody.type === VAR) throw new PrologError('instantiation_error');
|
package/src/lib/eyeprolog.pl
CHANGED
|
@@ -622,7 +622,21 @@ number_string(Number, Text) :-
|
|
|
622
622
|
number_string(Number, Text) :-
|
|
623
623
|
var(Number),
|
|
624
624
|
eyeprolog__text_chars(Text, Chars),
|
|
625
|
-
catch(number_chars(Number, Chars), _,
|
|
625
|
+
catch(number_chars(Number, Chars), _,
|
|
626
|
+
catch(( eyeprolog__integer_exponent_chars(Chars, Expanded),
|
|
627
|
+
number_chars(Number, Expanded)
|
|
628
|
+
), _, fail)).
|
|
629
|
+
|
|
630
|
+
eyeprolog__integer_exponent_chars(Chars, Expanded) :-
|
|
631
|
+
eyeprolog__split_exponent(Chars, Mantissa, Marker, Exponent),
|
|
632
|
+
number_chars(Integer, Mantissa),
|
|
633
|
+
integer(Integer),
|
|
634
|
+
eyeprolog__append(Mantissa, ['.','0',Marker|Exponent], Expanded).
|
|
635
|
+
|
|
636
|
+
eyeprolog__split_exponent([e|Exponent], [], e, Exponent).
|
|
637
|
+
eyeprolog__split_exponent(['E'|Exponent], [], 'E', Exponent).
|
|
638
|
+
eyeprolog__split_exponent([C|Chars], [C|Mantissa], Marker, Exponent) :-
|
|
639
|
+
eyeprolog__split_exponent(Chars, Mantissa, Marker, Exponent).
|
|
626
640
|
|
|
627
641
|
atom_string(Atom, Text) :-
|
|
628
642
|
atom(Atom),
|
package/src/lib/prologue.pl
CHANGED
|
@@ -7,10 +7,36 @@
|
|
|
7
7
|
between/3,
|
|
8
8
|
select/3,
|
|
9
9
|
succ/2,
|
|
10
|
-
maplist/2
|
|
10
|
+
maplist/2,
|
|
11
|
+
maplist/3,
|
|
12
|
+
maplist/4,
|
|
13
|
+
maplist/5,
|
|
14
|
+
maplist/6,
|
|
15
|
+
maplist/7,
|
|
16
|
+
maplist/8,
|
|
17
|
+
nth0/3,
|
|
18
|
+
nth0/4,
|
|
19
|
+
nth1/3,
|
|
20
|
+
nth1/4,
|
|
21
|
+
call_nth/2,
|
|
22
|
+
foldl/4,
|
|
23
|
+
foldl/5,
|
|
24
|
+
foldl/6,
|
|
25
|
+
countall/2
|
|
11
26
|
]).
|
|
12
27
|
|
|
13
28
|
:- meta_predicate(maplist(1, '?')).
|
|
29
|
+
:- meta_predicate(maplist(2, '?', '?')).
|
|
30
|
+
:- meta_predicate(maplist(3, '?', '?', '?')).
|
|
31
|
+
:- meta_predicate(maplist(4, '?', '?', '?', '?')).
|
|
32
|
+
:- meta_predicate(maplist(5, '?', '?', '?', '?', '?')).
|
|
33
|
+
:- meta_predicate(maplist(6, '?', '?', '?', '?', '?', '?')).
|
|
34
|
+
:- meta_predicate(maplist(7, '?', '?', '?', '?', '?', '?', '?')).
|
|
35
|
+
:- meta_predicate(call_nth(0, '?')).
|
|
36
|
+
:- meta_predicate(foldl(3, '?', '?', '?')).
|
|
37
|
+
:- meta_predicate(foldl(4, '?', '?', '?', '?')).
|
|
38
|
+
:- meta_predicate(foldl(5, '?', '?', '?', '?', '?')).
|
|
39
|
+
:- meta_predicate(countall(0, '?')).
|
|
14
40
|
|
|
15
41
|
member(X, [X|_]).
|
|
16
42
|
member(X, [_|Xs]) :- member(X, Xs).
|
|
@@ -74,6 +100,84 @@ maplist(Closure, [X|Xs]) :-
|
|
|
74
100
|
call(Closure, X),
|
|
75
101
|
maplist(Closure, Xs).
|
|
76
102
|
|
|
103
|
+
maplist(_, [], []).
|
|
104
|
+
maplist(Closure, [A|As], [B|Bs]) :-
|
|
105
|
+
call(Closure, A, B),
|
|
106
|
+
maplist(Closure, As, Bs).
|
|
107
|
+
|
|
108
|
+
maplist(_, [], [], []).
|
|
109
|
+
maplist(Closure, [A|As], [B|Bs], [C|Cs]) :-
|
|
110
|
+
call(Closure, A, B, C),
|
|
111
|
+
maplist(Closure, As, Bs, Cs).
|
|
112
|
+
|
|
113
|
+
maplist(_, [], [], [], []).
|
|
114
|
+
maplist(Closure, [A|As], [B|Bs], [C|Cs], [D|Ds]) :-
|
|
115
|
+
call(Closure, A, B, C, D),
|
|
116
|
+
maplist(Closure, As, Bs, Cs, Ds).
|
|
117
|
+
|
|
118
|
+
maplist(_, [], [], [], [], []).
|
|
119
|
+
maplist(Closure, [A|As], [B|Bs], [C|Cs], [D|Ds], [E|Es]) :-
|
|
120
|
+
call(Closure, A, B, C, D, E),
|
|
121
|
+
maplist(Closure, As, Bs, Cs, Ds, Es).
|
|
122
|
+
|
|
123
|
+
maplist(_, [], [], [], [], [], []).
|
|
124
|
+
maplist(Closure, [A|As], [B|Bs], [C|Cs], [D|Ds], [E|Es], [F|Fs]) :-
|
|
125
|
+
call(Closure, A, B, C, D, E, F),
|
|
126
|
+
maplist(Closure, As, Bs, Cs, Ds, Es, Fs).
|
|
127
|
+
|
|
128
|
+
maplist(_, [], [], [], [], [], [], []).
|
|
129
|
+
maplist(Closure, [A|As], [B|Bs], [C|Cs], [D|Ds], [E|Es], [F|Fs], [G|Gs]) :-
|
|
130
|
+
call(Closure, A, B, C, D, E, F, G),
|
|
131
|
+
maplist(Closure, As, Bs, Cs, Ds, Es, Fs, Gs).
|
|
132
|
+
|
|
133
|
+
nth0(N, List, Elem) :- nth0(N, List, Elem, _).
|
|
134
|
+
|
|
135
|
+
nth0(N, List, Elem, Rest) :-
|
|
136
|
+
prologue__integer_or_variable(N),
|
|
137
|
+
( var(N) -> prologue__nth0(N, List, Elem, Rest)
|
|
138
|
+
; prologue__not_less_than_zero(N),
|
|
139
|
+
prologue__nth0(N, List, Elem, Rest)
|
|
140
|
+
).
|
|
141
|
+
|
|
142
|
+
prologue__nth0(0, [Elem|Rest], Elem, Rest).
|
|
143
|
+
prologue__nth0(N, [X|Xs], Elem, [X|Rest]) :-
|
|
144
|
+
var(N),
|
|
145
|
+
prologue__nth0(N0, Xs, Elem, Rest),
|
|
146
|
+
N is N0 + 1.
|
|
147
|
+
prologue__nth0(N, [X|Xs], Elem, [X|Rest]) :-
|
|
148
|
+
nonvar(N),
|
|
149
|
+
N > 0,
|
|
150
|
+
N0 is N - 1,
|
|
151
|
+
prologue__nth0(N0, Xs, Elem, Rest).
|
|
152
|
+
|
|
153
|
+
nth1(N, List, Elem) :- nth1(N, List, Elem, _).
|
|
154
|
+
|
|
155
|
+
nth1(N, List, Elem, Rest) :-
|
|
156
|
+
N \== 0,
|
|
157
|
+
nth0(N, [_|List], Elem, [_|Rest]),
|
|
158
|
+
N \== 0.
|
|
159
|
+
|
|
160
|
+
call_nth(Goal, Nth) :- eyeprolog__call_nth(Goal, Nth).
|
|
161
|
+
|
|
162
|
+
foldl(_, [], Acc, Acc).
|
|
163
|
+
foldl(Closure, [A|As], Acc0, Acc) :-
|
|
164
|
+
call(Closure, A, Acc0, Acc1),
|
|
165
|
+
foldl(Closure, As, Acc1, Acc).
|
|
166
|
+
|
|
167
|
+
foldl(_, [], [], Acc, Acc).
|
|
168
|
+
foldl(Closure, [A|As], [B|Bs], Acc0, Acc) :-
|
|
169
|
+
call(Closure, A, B, Acc0, Acc1),
|
|
170
|
+
foldl(Closure, As, Bs, Acc1, Acc).
|
|
171
|
+
|
|
172
|
+
foldl(_, [], [], [], Acc, Acc).
|
|
173
|
+
foldl(Closure, [A|As], [B|Bs], [C|Cs], Acc0, Acc) :-
|
|
174
|
+
call(Closure, A, B, C, Acc0, Acc1),
|
|
175
|
+
foldl(Closure, As, Bs, Cs, Acc1, Acc).
|
|
176
|
+
|
|
177
|
+
countall(Goal, Count) :-
|
|
178
|
+
findall(1, Goal, Ones),
|
|
179
|
+
length(Ones, Count).
|
|
180
|
+
|
|
77
181
|
prologue__integer_or_variable(X) :- var(X), !.
|
|
78
182
|
prologue__integer_or_variable(X) :- prologue__integer(X).
|
|
79
183
|
|
package/src/standard-library.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// The sources are registered here so library(Name) works in Node and browsers;
|
|
3
3
|
// unlike the former autoloader, no clauses are added unless use_module/1 or
|
|
4
4
|
// use_module/2 requests their module.
|
|
5
|
-
import { createDefaultRegistry } from './iso.js';
|
|
5
|
+
import { createDefaultRegistry, eyePrologLibraryBuiltins } from './iso.js';
|
|
6
6
|
import { fs, isNode } from './platform.js';
|
|
7
7
|
|
|
8
8
|
const moduleFiles = Object.freeze({
|
|
@@ -35,21 +35,27 @@ function libraryUrl(filename) {
|
|
|
35
35
|
return url;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export const eyePrologNativeLibraryIndicators = Object.freeze([]);
|
|
38
|
+
export const eyePrologNativeLibraryIndicators = Object.freeze(['call_nth/2']);
|
|
39
39
|
export const eyePrologPortableLibraryIndicators = Object.freeze([
|
|
40
|
-
'uuid/3', 'difference/3', 'maplist/2', 'maplist/3', '
|
|
40
|
+
'uuid/3', 'difference/3', 'maplist/2', 'maplist/3', 'maplist/4', 'maplist/5',
|
|
41
|
+
'maplist/6', 'maplist/7', 'maplist/8', 'lt/2', 'gt/2', 'le/2', 'ge/2',
|
|
41
42
|
'between/3', 'smallest_divisor_from/3', 'random/3', 'matches/3', 'split/3',
|
|
42
43
|
'replace/4', 'lowercase/2', 'uppercase/2', 'trim/2', 'number_string/2',
|
|
43
44
|
'atom_string/2', 'term_string/2', 'append/3', 'string_concat/3', 'contains/2',
|
|
44
45
|
'matches/2', 'join/3', 'substring/4', 'member/2', 'select/3', 'last/2',
|
|
45
|
-
'nth0/3', 'nth1/3', 'set_nth0/4', 'take/3', 'drop/3', 'slice/4', 'reverse/2',
|
|
46
|
+
'nth0/3', 'nth0/4', 'nth1/3', 'nth1/4', 'set_nth0/4', 'take/3', 'drop/3', 'slice/4', 'reverse/2',
|
|
46
47
|
'length/2', 'sum_list/2', 'min_list/2', 'max_list/2', 'list_to_set/2',
|
|
47
|
-
'countall/2', 'succ/2', '
|
|
48
|
+
'countall/2', 'succ/2', 'foldl/4', 'foldl/5', 'foldl/6',
|
|
49
|
+
'sumall/3', 'aggregate_min/5', 'aggregate_max/5',
|
|
50
|
+
]);
|
|
51
|
+
export const eyePrologLibraryIndicators = Object.freeze([
|
|
52
|
+
...eyePrologPortableLibraryIndicators,
|
|
53
|
+
...eyePrologNativeLibraryIndicators,
|
|
48
54
|
]);
|
|
49
|
-
export const eyePrologLibraryIndicators = Object.freeze([...eyePrologPortableLibraryIndicators]);
|
|
50
55
|
|
|
51
56
|
export function createEyePrologRegistry() {
|
|
52
57
|
const registry = createDefaultRegistry();
|
|
58
|
+
eyePrologLibraryBuiltins.register(registry);
|
|
53
59
|
registry.eyePrologLibrary = true;
|
|
54
60
|
return registry;
|
|
55
61
|
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
% ids refer to https://www.complang.tuwien.ac.at/ulrich/iso-prolog/number_chars_cont
|
|
2
|
+
|
|
3
|
+
1 ?- number_chars(1.2,"1.2").
|
|
4
|
+
true.
|
|
5
|
+
|
|
6
|
+
55 ?- number_chars(1.2,"1.20").
|
|
7
|
+
true.
|
|
8
|
+
|
|
9
|
+
2 ?- number_chars(1.0e9,"1.0E9").
|
|
10
|
+
true.
|
|
11
|
+
|
|
12
|
+
56 ?- number_chars(1.0e9,"1.0e9").
|
|
13
|
+
true.
|
|
14
|
+
|
|
15
|
+
64 ?- number_chars(0.0,"-0.0").
|
|
16
|
+
true.
|
|
17
|
+
|
|
18
|
+
3 ?- number_chars(1,"01").
|
|
19
|
+
true.
|
|
20
|
+
|
|
21
|
+
65 ?- number_chars(10,"010").
|
|
22
|
+
true.
|
|
23
|
+
|
|
24
|
+
66 ?- number_chars(N,"010").
|
|
25
|
+
N=10.
|
|
26
|
+
|
|
27
|
+
67 ?- number_chars(N,"08").
|
|
28
|
+
N=8.
|
|
29
|
+
|
|
30
|
+
68 ?- number_chars(N,"0b11").
|
|
31
|
+
N=3.
|
|
32
|
+
|
|
33
|
+
69 ?- number_chars(N,"0o11").
|
|
34
|
+
N=9.
|
|
35
|
+
|
|
36
|
+
70 ?- number_chars(N,"0x11").
|
|
37
|
+
N=17.
|
|
38
|
+
|
|
39
|
+
4 ?- number_chars(1,"a").
|
|
40
|
+
syntax_error(...).
|
|
41
|
+
|
|
42
|
+
5 ?- number_chars(1,[]).
|
|
43
|
+
syntax_error(...).
|
|
44
|
+
|
|
45
|
+
6 ?- number_chars(1,[[]]).
|
|
46
|
+
type_error(character,[]).
|
|
47
|
+
|
|
48
|
+
7 ?- number_chars(1,[' ',[]]).
|
|
49
|
+
type_error(character,[]).
|
|
50
|
+
|
|
51
|
+
8 ?- number_chars(1,[0]).
|
|
52
|
+
type_error(character,0).
|
|
53
|
+
|
|
54
|
+
9 ?- number_chars(1,[_,[]]).
|
|
55
|
+
type_error(character,[]).
|
|
56
|
+
|
|
57
|
+
10 ?- number_chars(N,[X]).
|
|
58
|
+
instantiation_error.
|
|
59
|
+
|
|
60
|
+
11 ?- number_chars(N,['0'|_]).
|
|
61
|
+
instantiation_error.
|
|
62
|
+
|
|
63
|
+
12 ?- number_chars(N,'1').
|
|
64
|
+
type_error(list,'1').
|
|
65
|
+
|
|
66
|
+
13 ?- number_chars(N,[a|a]).
|
|
67
|
+
type_error(list,[a|a]).
|
|
68
|
+
|
|
69
|
+
14 ?- number_chars(N,[49]).
|
|
70
|
+
type_error(character,49).
|
|
71
|
+
|
|
72
|
+
15 ?- number_chars(N,[]).
|
|
73
|
+
syntax_error(...).
|
|
74
|
+
|
|
75
|
+
16 ?- number_chars(N,"3 ").
|
|
76
|
+
syntax_error(...).
|
|
77
|
+
|
|
78
|
+
17 ?- number_chars(N,"3.").
|
|
79
|
+
syntax_error(...).
|
|
80
|
+
|
|
81
|
+
18 ?- number_chars(N," 1").
|
|
82
|
+
N = 1.
|
|
83
|
+
|
|
84
|
+
19 ?- number_chars(N,"\n1").
|
|
85
|
+
N = 1.
|
|
86
|
+
|
|
87
|
+
20 ?- number_chars(N," 0'a").
|
|
88
|
+
N = 0'a. N = 97.
|
|
89
|
+
|
|
90
|
+
58 ?- number_chars(N,"0'").
|
|
91
|
+
syntax_error(...).
|
|
92
|
+
|
|
93
|
+
59 ?- number_chars(N,"0'\n").
|
|
94
|
+
syntax_error(...).
|
|
95
|
+
|
|
96
|
+
60 ?- number_chars(N,"0'\\n").
|
|
97
|
+
N=0'\n. N=10.
|
|
98
|
+
|
|
99
|
+
61 ?- number_chars(N,"0'\\7\\").
|
|
100
|
+
N=7.
|
|
101
|
+
|
|
102
|
+
71 ?- number_chars(N,"0'\\7").
|
|
103
|
+
syntax_error(...).
|
|
104
|
+
|
|
105
|
+
62 ?- number_chars(N,"0'.").
|
|
106
|
+
N=0'. . N = 46.
|
|
107
|
+
|
|
108
|
+
21 ?- number_chars(N,"- 1").
|
|
109
|
+
N = -1.
|
|
110
|
+
|
|
111
|
+
54 ?- number_chars(N,"'-'1").
|
|
112
|
+
N = -1.
|
|
113
|
+
|
|
114
|
+
22 ?- number_chars(N,"/**/1").
|
|
115
|
+
N = 1.
|
|
116
|
+
|
|
117
|
+
23 ?- number_chars(N,"%\n1").
|
|
118
|
+
N = 1.
|
|
119
|
+
|
|
120
|
+
57 ?- number_chars(N,"- /**/1").
|
|
121
|
+
N = -1.
|
|
122
|
+
|
|
123
|
+
24 ?- number_chars(N,"-/**/1").
|
|
124
|
+
syntax_error(...).
|
|
125
|
+
|
|
126
|
+
63 ?- number_chars(N,"'\\\n-' 3").
|
|
127
|
+
N= -3.
|
|
128
|
+
|
|
129
|
+
25 ?- number_chars(N,"1e1").
|
|
130
|
+
syntax_error(...).
|
|
131
|
+
|
|
132
|
+
26 ?- number_chars(N,"1.0e").
|
|
133
|
+
syntax_error(...).
|
|
134
|
+
|
|
135
|
+
27 ?- number_chars(N,"1.0ee").
|
|
136
|
+
syntax_error(...).
|
|
137
|
+
|
|
138
|
+
28 ?- number_chars(N,"0x1").
|
|
139
|
+
N = 1.
|
|
140
|
+
|
|
141
|
+
29 ?- number_chars(N,"0X1").
|
|
142
|
+
syntax_error(...).
|
|
143
|
+
|
|
144
|
+
30 ?- number_chars(N,"1E1").
|
|
145
|
+
syntax_error(...).
|
|
146
|
+
|
|
147
|
+
47 ?- number_chars(1,['.'|_]).
|
|
148
|
+
false.
|
|
149
|
+
|
|
150
|
+
48 ?- number_chars(N,"+1").
|
|
151
|
+
syntax_error(...).
|
|
152
|
+
|
|
153
|
+
49 ?- number_chars(N,"+ 1").
|
|
154
|
+
syntax_error(...).
|
|
155
|
+
|
|
156
|
+
50 ?- number_chars(N,"'+'1").
|
|
157
|
+
syntax_error(...).
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
51 ?- number_chars(N,['11']).
|
|
161
|
+
type_error(character,'11').
|
|
162
|
+
|
|
163
|
+
52 ?- number_chars(N,['1.1']).
|
|
164
|
+
type_error(character,'1.1').
|
|
165
|
+
|
|
166
|
+
53 ?- number_chars(1+1,"2").
|
|
167
|
+
type_error(number,1+1).
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
31 ?- number_chars(1,[C]).
|
|
171
|
+
C = '1'.
|
|
172
|
+
|
|
173
|
+
32 ?- number_chars(1,[C,D]).
|
|
174
|
+
false.
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
33 ?- number_chars(1,[C,C]).
|
|
178
|
+
false.
|
|
179
|
+
|
|
180
|
+
34 ?- number_chars(0,[C,C]).
|
|
181
|
+
false.
|
|
182
|
+
|
|
183
|
+
35 ?- number_chars(10,[C,D]).
|
|
184
|
+
C = '1', D = '0'.
|
|
185
|
+
|
|
186
|
+
36 ?- number_chars(100,[C,D]).
|
|
187
|
+
false.
|
|
188
|
+
|
|
189
|
+
37 ?- number_chars(N,[X|2]).
|
|
190
|
+
type_error(list,[_|2])
|
|
191
|
+
| instantiation_error.
|
|
192
|
+
|
|
193
|
+
38 ?- number_chars(N,[1|_]).
|
|
194
|
+
instantiation_error
|
|
195
|
+
| type_error(character,1).
|
|
196
|
+
|
|
197
|
+
39 ?- number_chars(V,[1|2]).
|
|
198
|
+
type_error(list,[1|2])
|
|
199
|
+
| type_error(character,1).
|
|
200
|
+
|
|
201
|
+
40 ?- number_chars([],1).
|
|
202
|
+
type_error(number,[])
|
|
203
|
+
| type_error(list,1).
|
|
204
|
+
|
|
205
|
+
41 ?- number_chars(1,1).
|
|
206
|
+
type_error(list,1).
|
|
207
|
+
|
|
208
|
+
42 ?- number_chars(1,[a|2]).
|
|
209
|
+
type_error(list,[a|2]).
|
|
210
|
+
|
|
211
|
+
43 ?- number_chars(1,[_|2]).
|
|
212
|
+
type_error(list,[_|2]).
|
|
213
|
+
|
|
214
|
+
44 ?- number_chars(1,[[]|_]).
|
|
215
|
+
type_error(character,[]).
|
|
216
|
+
|
|
217
|
+
45 ?- number_chars(1,[[]|2]).
|
|
218
|
+
type_error(character,[])
|
|
219
|
+
| type_error(list,[[]|2]).
|
|
220
|
+
|
|
221
|
+
72 ?- number_chars(V,[1,[],X|2]).
|
|
222
|
+
type_error(list,[1,[],_A|2])
|
|
223
|
+
| type_error(character,1)
|
|
224
|
+
| type_error(character,[])
|
|
225
|
+
| instantiation_error.
|
|
226
|
+
|
|
227
|
+
46 ?- L=['1'|L], number_chars(N,L).
|
|
228
|
+
sto, ... ; ... .
|
|
229
|
+
( sto, type_error(list,['1'|...]) % rational trees
|
|
230
|
+
| sto, false % occurs-check
|
|
231
|
+
| sto, representation_error(term)
|
|
232
|
+
| sto, instantiation_error % literal substitutions
|
|
233
|
+
| sto, resource_error(...)
|
|
234
|
+
| sto, loops
|
|
235
|
+
).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
?- call_nth(true, Nth).
|
|
2
|
+
Nth = 1.
|
|
3
|
+
?- call_nth(false, Nth).
|
|
4
|
+
false.
|
|
5
|
+
?- call_nth(repeat, Nth).
|
|
6
|
+
Nth = 1
|
|
7
|
+
; Nth = 2
|
|
8
|
+
; Nth = 3
|
|
9
|
+
; Nth = 4
|
|
10
|
+
; Nth = 5
|
|
11
|
+
; ... .
|
|
12
|
+
?- call_nth(( N = 1 ; N = 2 ), Nth).
|
|
13
|
+
N = 1, Nth = 1
|
|
14
|
+
; N = 2, Nth = 2.
|
|
15
|
+
?- call_nth(true, non_integer).
|
|
16
|
+
type_error(integer,non_integer).
|
|
17
|
+
?- call_nth(true, 1.0).
|
|
18
|
+
type_error(integer,1.0).
|
|
19
|
+
?- call_nth(true, 0).
|
|
20
|
+
false.
|
|
21
|
+
?- call_nth(repeat, 0).
|
|
22
|
+
false.
|
|
23
|
+
?- call_nth(repeat, -1).
|
|
24
|
+
domain_error(not_less_than_zero,-1).
|
|
25
|
+
?- call_nth(length(L,N), 3).
|
|
26
|
+
L = [_A,_B], N = 2.
|
|
27
|
+
?- call_nth(inex, 0).
|
|
28
|
+
false.
|
|
29
|
+
?- call_nth(1, 0).
|
|
30
|
+
false.
|
|
31
|
+
?- call_nth(V, 0).
|
|
32
|
+
false.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
% Finite examples and arity coverage for Prologue p.p.7 through p.p.11.
|
|
2
|
+
|
|
3
|
+
?- maplist(same2, [a], [a]).
|
|
4
|
+
true.
|
|
5
|
+
?- maplist(same3, [a], [a], [a]).
|
|
6
|
+
true.
|
|
7
|
+
?- maplist(same4, [a], [a], [a], [a]).
|
|
8
|
+
true.
|
|
9
|
+
?- maplist(same5, [a], [a], [a], [a], [a]).
|
|
10
|
+
true.
|
|
11
|
+
?- maplist(same6, [a], [a], [a], [a], [a], [a]).
|
|
12
|
+
true.
|
|
13
|
+
?- maplist(same7, [a], [a], [a], [a], [a], [a], [a]).
|
|
14
|
+
true.
|
|
15
|
+
|
|
16
|
+
?- nth0(1, [a,b,c], E).
|
|
17
|
+
E = b.
|
|
18
|
+
?- nth0(N, [a,b,c], E).
|
|
19
|
+
N = 0, E = a
|
|
20
|
+
; N = 1, E = b
|
|
21
|
+
; N = 2, E = c.
|
|
22
|
+
?- nth0(0, [A,B|non_list], E).
|
|
23
|
+
A = E.
|
|
24
|
+
?- nth0(2, Es, E).
|
|
25
|
+
Es = [_A,_B,E|_C].
|
|
26
|
+
?- nth0(N, Es, E).
|
|
27
|
+
N = 0, Es = [E|_A]
|
|
28
|
+
; N = 1, Es = [_A,E|_B]
|
|
29
|
+
; N = 2, Es = [_A,_B,E|_C]
|
|
30
|
+
; N = 3, Es = [_A,_B,_C,E|_D]
|
|
31
|
+
; ... .
|
|
32
|
+
?- nth0(non_integer, Es, E).
|
|
33
|
+
type_error(integer, non_integer).
|
|
34
|
+
?- nth0(-1, Es, E).
|
|
35
|
+
domain_error(not_less_than_zero, -1).
|
|
36
|
+
?- nth0(N, [[]|Es], Es).
|
|
37
|
+
N = 0, Es = []
|
|
38
|
+
; sto, loops.
|
|
39
|
+
?- nth0(1, [a,b,c], E, Es).
|
|
40
|
+
E = b, Es = [a,c].
|
|
41
|
+
?- nth1(0, Es, E).
|
|
42
|
+
false.
|
|
43
|
+
?- nth1(2, [a,b,c], E, Es).
|
|
44
|
+
E = b, Es = [a,c].
|
|
45
|
+
|
|
46
|
+
?- call_nth(N = 1, N).
|
|
47
|
+
N = 1.
|
|
48
|
+
?- call_nth(N = -1, N).
|
|
49
|
+
false.
|
|
50
|
+
?- call_nth(repeat, 1+1).
|
|
51
|
+
type_error(integer, 1+1).
|
|
52
|
+
?- call_nth(inex, 0).
|
|
53
|
+
existence_error(procedure, _), unexpected.
|
|
54
|
+
?- call_nth(G, non_integer).
|
|
55
|
+
instantiation_error.
|
|
56
|
+
?- call_nth(1, -1).
|
|
57
|
+
type_error(callable, 1).
|
|
58
|
+
|
|
59
|
+
?- foldl(append, [[1,2],[3],[4,5]], [], Xs).
|
|
60
|
+
Xs = [4,5,3,1,2].
|
|
61
|
+
?- foldl(add2, [1,2], [10,20], 0, S).
|
|
62
|
+
S = 33.
|
|
63
|
+
?- foldl(add3, [1,2], [10,20], [100,200], 0, S).
|
|
64
|
+
S = 333.
|
|
65
|
+
|
|
66
|
+
?- countall((X=1;X=2), N).
|
|
67
|
+
N = 2.
|
|
68
|
+
?- countall((true;true), N).
|
|
69
|
+
N = 2.
|
|
70
|
+
?- countall(G, N).
|
|
71
|
+
instantiation_error.
|
|
72
|
+
?- countall((length(L,5),nth0(_,L,_),nth0(_,L,_)), N).
|
|
73
|
+
N = 25.
|
|
74
|
+
?- countall(N=1, N).
|
|
75
|
+
N = 1.
|
|
76
|
+
?- countall(N=non_integer, N).
|
|
77
|
+
N = 1.
|
|
78
|
+
?- countall(false, 1).
|
|
79
|
+
false.
|
|
80
|
+
?- countall(false, -1).
|
|
81
|
+
domain_error(not_less_than_zero, -1).
|
|
82
|
+
?- countall(false, non_integer).
|
|
83
|
+
type_error(integer, non_integer).
|
|
84
|
+
?- countall(G, -1).
|
|
85
|
+
instantiation_error.
|
|
86
|
+
?- countall(1, -1).
|
|
87
|
+
type_error(callable, 1).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
:- use_module(library(prologue)).
|
|
2
|
+
|
|
3
|
+
same2(X, X).
|
|
4
|
+
same3(X, X, X).
|
|
5
|
+
same4(X, X, X, X).
|
|
6
|
+
same5(X, X, X, X, X).
|
|
7
|
+
same6(X, X, X, X, X, X).
|
|
8
|
+
same7(X, X, X, X, X, X, X).
|
|
9
|
+
|
|
10
|
+
add2(X, Y, S0, S) :- S is S0 + X + Y.
|
|
11
|
+
add3(X, Y, Z, S0, S) :- S is S0 + X + Y + Z.
|
|
12
|
+
|
|
13
|
+
:- include('prologue_extended_quad.pl').
|
package/test/run-regression.mjs
CHANGED
|
@@ -50,7 +50,7 @@ const bin = path.join(packageRoot, 'bin', 'eyeprolog.js');
|
|
|
50
50
|
const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
51
51
|
let tmp = null;
|
|
52
52
|
let tmpCounter = 0;
|
|
53
|
-
const libraryCall = /\b(?:uuid|difference|maplist|lt|gt|le|ge|between|smallest_divisor_from|random|matches|split|replace|lowercase|uppercase|trim|number_string|atom_string|term_string|append|string_concat|contains|join|substring|member|select|last|nth0|nth1|set_nth0|take|drop|slice|reverse|length|sum_list|min_list|max_list|list_to_set|countall|sumall|aggregate_min|aggregate_max)\s*\(/;
|
|
53
|
+
const libraryCall = /\b(?:uuid|difference|maplist|foldl|call_nth|lt|gt|le|ge|between|smallest_divisor_from|random|matches|split|replace|lowercase|uppercase|trim|number_string|atom_string|term_string|append|string_concat|contains|join|substring|member|select|last|nth0|nth1|set_nth0|take|drop|slice|reverse|length|sum_list|min_list|max_list|list_to_set|countall|sumall|aggregate_min|aggregate_max)\s*\(/;
|
|
54
54
|
|
|
55
55
|
function withStandardModules(source) {
|
|
56
56
|
if (!libraryCall.test(source) || source.includes('use_module(library(') || source.includes(':- module(')) return source;
|
|
@@ -322,6 +322,20 @@ c4 ?- call((!;1)).
|
|
|
322
322
|
assertEqual(result.stdout, 'quads: 58 run, 58 passed, 0 failed.\n', 'quad report');
|
|
323
323
|
},
|
|
324
324
|
},
|
|
325
|
+
{
|
|
326
|
+
name: 'runQuads passes the complete vendored number_chars continuation corpus',
|
|
327
|
+
run: () => {
|
|
328
|
+
const filename = path.join(testRoot, 'fixtures', 'number_chars_cont_quad.pl');
|
|
329
|
+
const source = fs.readFileSync(filename, 'utf8');
|
|
330
|
+
const result = publicApi.runQuads(Program.parseSources([{
|
|
331
|
+
text: source,
|
|
332
|
+
filename,
|
|
333
|
+
}]));
|
|
334
|
+
assertEqual(result.total, 72, 'quad total');
|
|
335
|
+
assertEqual(result.passed, 72, 'quad passed');
|
|
336
|
+
assertEqual(result.stdout, 'quads: 72 run, 72 passed, 0 failed.\n', 'quad report');
|
|
337
|
+
},
|
|
338
|
+
},
|
|
325
339
|
{
|
|
326
340
|
name: 'runQuads passes the complete vendored Prolog Prologue quad corpus',
|
|
327
341
|
run: () => {
|
|
@@ -337,6 +351,36 @@ c4 ?- call((!;1)).
|
|
|
337
351
|
assertEqual(result.stdout, 'quads: 33 run, 33 passed, 0 failed.\n', 'quad report');
|
|
338
352
|
},
|
|
339
353
|
},
|
|
354
|
+
{
|
|
355
|
+
name: 'runQuads passes the authoritative Prologue call_nth quad corpus',
|
|
356
|
+
run: () => {
|
|
357
|
+
const filename = path.join(testRoot, 'fixtures', 'prologue_call_nth_quad_runner.pl');
|
|
358
|
+
const source = fs.readFileSync(filename, 'utf8');
|
|
359
|
+
const result = publicApi.runQuads(Program.parseSources([{
|
|
360
|
+
text: source,
|
|
361
|
+
filename,
|
|
362
|
+
baseDir: path.dirname(filename),
|
|
363
|
+
}]));
|
|
364
|
+
assertEqual(result.total, 13, 'quad total');
|
|
365
|
+
assertEqual(result.passed, 13, 'quad passed');
|
|
366
|
+
assertEqual(result.stdout, 'quads: 13 run, 13 passed, 0 failed.\n', 'quad report');
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: 'runQuads covers the remaining finite Prologue examples and arities',
|
|
371
|
+
run: () => {
|
|
372
|
+
const filename = path.join(testRoot, 'fixtures', 'prologue_extended_quad_runner.pl');
|
|
373
|
+
const source = fs.readFileSync(filename, 'utf8');
|
|
374
|
+
const result = publicApi.runQuads(Program.parseSources([{
|
|
375
|
+
text: source,
|
|
376
|
+
filename,
|
|
377
|
+
baseDir: path.dirname(filename),
|
|
378
|
+
}]));
|
|
379
|
+
assertEqual(result.total, 37, 'quad total');
|
|
380
|
+
assertEqual(result.passed, 37, 'quad passed');
|
|
381
|
+
assertEqual(result.stdout, 'quads: 37 run, 37 passed, 0 failed.\n', 'quad report');
|
|
382
|
+
},
|
|
383
|
+
},
|
|
340
384
|
{
|
|
341
385
|
name: 'runQuads rejects malformed answer substitutions',
|
|
342
386
|
run: () => {
|
|
@@ -1348,14 +1392,17 @@ open(X) :- candidate(X), \\+ closed(X).
|
|
|
1348
1392
|
assertEqual(Boolean(registry.get('is', 2)), true, 'ISO is/2 exists');
|
|
1349
1393
|
assertEqual(Boolean(registry.get('append', 3)), false, 'append/3 is not ISO core');
|
|
1350
1394
|
assertEqual(library.eyePrologLibrary, true, 'complete registry marker');
|
|
1351
|
-
assertEqual(library.defs.size,
|
|
1395
|
+
assertEqual(library.defs.size, 130, 'EyeProlog registry contains ISO definitions and one private library adapter');
|
|
1352
1396
|
assertEqual(Boolean(registry.get('phrase', 2)), true, 'Part 3 phrase/2 exists');
|
|
1353
1397
|
assertEqual(Boolean(registry.get('phrase', 3)), true, 'Part 3 phrase/3 exists');
|
|
1354
|
-
assertEqual(registeredNativeEyePrologLibraryNames().length,
|
|
1355
|
-
assertEqual(eyePrologPortableLibraryIndicators.length,
|
|
1356
|
-
assertEqual(eyePrologNativeLibraryIndicators.length,
|
|
1357
|
-
assertEqual(eyePrologNativeLibraryIndicators.join(','), '', '
|
|
1358
|
-
assertEqual(eyePrologLibraryIndicators.length,
|
|
1398
|
+
assertEqual(registeredNativeEyePrologLibraryNames().length, 1, 'public native EyeProlog builtin count');
|
|
1399
|
+
assertEqual(eyePrologPortableLibraryIndicators.length, 56, 'portable Prolog library count');
|
|
1400
|
+
assertEqual(eyePrologNativeLibraryIndicators.length, 1, 'native host library count');
|
|
1401
|
+
assertEqual(eyePrologNativeLibraryIndicators.join(','), 'call_nth/2', 'control predicate requiring host support');
|
|
1402
|
+
assertEqual(eyePrologLibraryIndicators.length, 57, 'complete EyeProlog library surface');
|
|
1403
|
+
assertEqual(registry.get('eyeprolog__call_nth', 2), null, 'private call_nth adapter is absent from ISO registry');
|
|
1404
|
+
assertEqual(Boolean(library.get('eyeprolog__call_nth', 2)), true, 'private call_nth adapter is registered for EyeProlog');
|
|
1405
|
+
assertEqual(library.get('eyeprolog__call_nth', 2)?.eyePrologLibrary, true, 'private adapter is marked as library support');
|
|
1359
1406
|
assertEqual(library.get('between', 3), null, 'between/3 remains portable Prolog');
|
|
1360
1407
|
assertEqual(library.get('smallest_divisor_from', 3), null, 'smallest_divisor_from/3 remains portable Prolog');
|
|
1361
1408
|
assertEqual(library.get('random', 3), null, 'random/3 remains portable Prolog');
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5636,7 +5636,7 @@ quoted_atom("ab"). % quoted_atom(ab)
|
|
|
5636
5636
|
| `sub_atom(+Atom,?Before,?Length,?After,?SubAtom)` | Enumerates substrings and their Unicode-code-point offsets. Supplied counts must be nonnegative integers. |
|
|
5637
5637
|
| `atom_chars(?Atom,?Chars)`, `atom_codes(?Atom,?Codes)` | Convert between an atom and a proper list of one-character atoms or Unicode scalar codes. At least one side must be instantiated. |
|
|
5638
5638
|
| `char_code(?Character,?Code)` | Converts one character atom and one Unicode scalar code. Surrogates and values outside `0..0x10ffff` raise a representation error. |
|
|
5639
|
-
| `number_chars(?Number,?Chars)`, `number_codes(?Number,?Codes)` | Convert finite numbers to canonical text or parse a proper character/code list. At least one side must be instantiated; malformed numeric input raises *syntax_error(number)*. |
|
|
5639
|
+
| `number_chars(?Number,?Chars)`, `number_codes(?Number,?Codes)` | Convert finite numbers to canonical text or parse a proper character/code list using ISO numeric-token syntax, including radix integers, character-code constants, and leading layout. At least one side must be instantiated; malformed numeric input raises *syntax_error(number)*. |
|
|
5640
5640
|
|
|
5641
5641
|
Conversions accept partial output lists when the atomic input is known, but
|
|
5642
5642
|
constructing an atom or number requires a complete proper list with no unbound
|
|
@@ -5761,22 +5761,25 @@ so side effects occur in Prolog execution order.
|
|
|
5761
5761
|
|
|
5762
5762
|
### The EyeProlog library
|
|
5763
5763
|
|
|
5764
|
-
EyeProlog exposes **
|
|
5765
|
-
indicators in its isolated ISO profile. **
|
|
5766
|
-
across `src/lib/eyeprolog.pl`, `src/lib/lists.pl`, and
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5764
|
+
EyeProlog exposes **57 library predicate indicators** in addition to the 129
|
|
5765
|
+
indicators in its isolated ISO profile. **56 are implemented entirely as
|
|
5766
|
+
ordinary Prolog clauses** across `src/lib/eyeprolog.pl`, `src/lib/lists.pl`, and
|
|
5767
|
+
`src/lib/prologue.pl`. The Prologue `call_nth/2` clause delegates its solution
|
|
5768
|
+
numbering to one private host adapter. The resulting normal EyeProlog language
|
|
5769
|
+
surface is therefore **186 public predicate indicators**. Internally, the
|
|
5770
|
+
runtime registry contains the 129 ISO definitions plus that private adapter;
|
|
5771
|
+
the remaining EyeProlog relations are module source clauses.
|
|
5771
5772
|
|
|
5772
5773
|
The three Prolog files declare `eyeprolog`, `lists`, and `prologue` with
|
|
5773
5774
|
`module/2`. A program loads them explicitly with
|
|
5774
5775
|
`use_module(library(eyeprolog))`, `use_module(library(lists))`, or
|
|
5775
5776
|
`use_module(library(prologue))`; `use_module/2` can select a smaller import
|
|
5776
|
-
list. The last module implements
|
|
5777
|
-
Prologue
|
|
5778
|
-
`
|
|
5779
|
-
|
|
5777
|
+
list. The last module implements p.p.1 through p.p.11 of the
|
|
5778
|
+
[working-draft Prologue](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue),
|
|
5779
|
+
with the published `call_nth/2` quads and finite examples retained as offline
|
|
5780
|
+
regressions. `src/standard-library.js` registers the module sources and private
|
|
5781
|
+
control adapter for Node and browser resolution, and never adds clauses
|
|
5782
|
+
implicitly.
|
|
5780
5783
|
The isolated ISO-only registry remains
|
|
5781
5784
|
available through `createDefaultRegistry()` and `getDefaultRegistry()` for
|
|
5782
5785
|
conformance work and advanced embedders. Module-local predicate identity keeps
|
|
@@ -5788,7 +5791,7 @@ private helpers and same-named predicates in different modules separate.
|
|
|
5788
5791
|
| --- | --- |
|
|
5789
5792
|
| `library(lists)` | `maplist/3`, `append/3`, `member/2`, `select/3`, `last/2`, `nth0/3`, `nth1/3`, `reverse/2`, `length/2`, `sum_list/2`, `min_list/2`, `max_list/2`, `list_to_set/2`, `countall/2` |
|
|
5790
5793
|
| `library(eyeprolog)` | `uuid/3`, `difference/3`, `lt/2`, `le/2`, `gt/2`, `ge/2`, `between/3`, `smallest_divisor_from/3`, `random/3`, `matches/3`, `split/3`, `replace/4`, `lowercase/2`, `uppercase/2`, `trim/2`, `number_string/2`, `atom_string/2`, `term_string/2`, `string_concat/3`, `contains/2`, `matches/2`, `join/3`, `substring/4`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4`, `sumall/3`, `aggregate_min/5`, `aggregate_max/5` |
|
|
5791
|
-
| `library(prologue)` | `member/2`, `append/3`, `length/2`, `between/3`, `select/3`, `succ/2`, `maplist/2` |
|
|
5794
|
+
| `library(prologue)` | `member/2`, `append/3`, `length/2`, `between/3`, `select/3`, `succ/2`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `call_nth/2`, `foldl/4`, `foldl/5`, `foldl/6`, `countall/2` |
|
|
5792
5795
|
|
|
5793
5796
|
<!-- eyeprolog-library-catalog:end -->
|
|
5794
5797
|
|
|
@@ -5915,7 +5918,7 @@ eyeprolog --goal 'answer(Kind, Value)' program.pl
|
|
|
5915
5918
|
The portable text API uses **ISO atoms or proper lists of one-character atoms**.
|
|
5916
5919
|
A generated text result defaults to an atom. Double-quoted source text uses the
|
|
5917
5920
|
ISO representation selected by `double_quotes`; with the default `chars`, it is
|
|
5918
|
-
already a proper character list accepted by this API. The
|
|
5921
|
+
already a proper character list accepted by this API. The 56-predicate portable
|
|
5919
5922
|
library itself has no STRING or JavaScript dependency.
|
|
5920
5923
|
|
|
5921
5924
|
| Predicate and principal mode | Behavior |
|