eyeprolog 1.3.14 → 1.3.16
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 +17 -0
- package/examples/dcg-expression-language.pl +147 -0
- package/examples/output/dcg-expression-language.pl +5 -0
- package/package.json +1 -1
- package/playground.html +1 -0
- package/src/solver.js +64 -0
- package/test/run-regression.mjs +16 -0
- package/the-art-of-eyeprolog.md +60 -3
- package/why-eyeprolog.md +8 -0
package/README.md
CHANGED
|
@@ -201,6 +201,23 @@ noun --> [world] | [prolog].
|
|
|
201
201
|
%% goal: phrase(sentence, Words)
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
+
For a larger bidirectional example, see
|
|
205
|
+
[`examples/dcg-expression-language.pl`](examples/dcg-expression-language.pl).
|
|
206
|
+
It parses precedence-sensitive arithmetic into an AST, evaluates expressions
|
|
207
|
+
with variables, generates tokens back from an AST with only the necessary
|
|
208
|
+
parentheses, round-trips the generated form, and demonstrates `phrase/3`
|
|
209
|
+
remainder handling. The example uses accumulator nonterminals for
|
|
210
|
+
left-associative operators, so state is handed repeatedly from one nonterminal
|
|
211
|
+
to the next rather than hidden in host code.
|
|
212
|
+
|
|
213
|
+
Deep finite DCG traversal is kept relational but does not have to consume one
|
|
214
|
+
general solver frame per token. In particular, the interoperable `...//0`
|
|
215
|
+
helper from `library(iso_ext)` can scan a finite compact list iteratively, and a
|
|
216
|
+
following grammar that is statically known to leave the DCG state unchanged can
|
|
217
|
+
be continued without rebuilding a full clause-resolution frame for every
|
|
218
|
+
suffix. Open and remainder-producing uses still retain the ordinary DCG
|
|
219
|
+
solutions and backtracking behavior.
|
|
220
|
+
|
|
204
221
|
EyeProlog also adds 128 public library predicate indicators to its 129-entry ISO
|
|
205
222
|
profile. **88 are implemented entirely as ordinary Prolog clauses** in focused
|
|
206
223
|
modules under `src/lib/`; the remaining control predicates and finite-domain
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
% Parse, evaluate, and regenerate arithmetic expressions with a DCG.
|
|
2
|
+
%
|
|
3
|
+
% The parser builds an AST while respecting precedence and left associativity.
|
|
4
|
+
% The additive_tail//2 and multiplicative_tail//2 nonterminals use an
|
|
5
|
+
% accumulator instead of left recursion. A second DCG pretty-prints an AST
|
|
6
|
+
% with only the parentheses required to preserve its structure, making the
|
|
7
|
+
% example useful in both directions.
|
|
8
|
+
%% goal: dcg_expression_example(X0, X1)
|
|
9
|
+
|
|
10
|
+
% expression//1: + and - are the lowest-precedence operators.
|
|
11
|
+
expression(AST) -->
|
|
12
|
+
term(First),
|
|
13
|
+
additive_tail(First, AST).
|
|
14
|
+
|
|
15
|
+
additive_tail(Left, AST) -->
|
|
16
|
+
['+'],
|
|
17
|
+
term(Right),
|
|
18
|
+
{ Next = add(Left, Right) },
|
|
19
|
+
additive_tail(Next, AST).
|
|
20
|
+
additive_tail(Left, AST) -->
|
|
21
|
+
['-'],
|
|
22
|
+
term(Right),
|
|
23
|
+
{ Next = sub(Left, Right) },
|
|
24
|
+
additive_tail(Next, AST).
|
|
25
|
+
additive_tail(AST, AST) --> [].
|
|
26
|
+
|
|
27
|
+
% term//1: * and / bind more tightly than + and -.
|
|
28
|
+
term(AST) -->
|
|
29
|
+
unary(First),
|
|
30
|
+
multiplicative_tail(First, AST).
|
|
31
|
+
|
|
32
|
+
multiplicative_tail(Left, AST) -->
|
|
33
|
+
['*'],
|
|
34
|
+
unary(Right),
|
|
35
|
+
{ Next = mul(Left, Right) },
|
|
36
|
+
multiplicative_tail(Next, AST).
|
|
37
|
+
multiplicative_tail(Left, AST) -->
|
|
38
|
+
['/'],
|
|
39
|
+
unary(Right),
|
|
40
|
+
{ Next = div(Left, Right) },
|
|
41
|
+
multiplicative_tail(Next, AST).
|
|
42
|
+
multiplicative_tail(AST, AST) --> [].
|
|
43
|
+
|
|
44
|
+
% Unary minus nests, so --x is represented as neg(neg(var(x))).
|
|
45
|
+
unary(neg(AST)) --> ['-'], unary(AST).
|
|
46
|
+
unary(AST) --> primary(AST).
|
|
47
|
+
|
|
48
|
+
primary(lit(Number)) -->
|
|
49
|
+
[Number],
|
|
50
|
+
{ number(Number) }.
|
|
51
|
+
primary(var(Name)) -->
|
|
52
|
+
[Name],
|
|
53
|
+
{ variable_name(Name) }.
|
|
54
|
+
primary(AST) --> ['('], expression(AST), [')'].
|
|
55
|
+
|
|
56
|
+
variable_name(x).
|
|
57
|
+
variable_name(y).
|
|
58
|
+
variable_name(z).
|
|
59
|
+
|
|
60
|
+
% A small evaluator for the AST produced by the grammar.
|
|
61
|
+
evaluate(lit(Number), _, Number).
|
|
62
|
+
evaluate(var(Name), Environment, Value) :-
|
|
63
|
+
lookup(Name, Environment, Value).
|
|
64
|
+
evaluate(neg(AST), Environment, Value) :-
|
|
65
|
+
evaluate(AST, Environment, Inner),
|
|
66
|
+
Value is -Inner.
|
|
67
|
+
evaluate(add(Left, Right), Environment, Value) :-
|
|
68
|
+
evaluate(Left, Environment, L),
|
|
69
|
+
evaluate(Right, Environment, R),
|
|
70
|
+
Value is L + R.
|
|
71
|
+
evaluate(sub(Left, Right), Environment, Value) :-
|
|
72
|
+
evaluate(Left, Environment, L),
|
|
73
|
+
evaluate(Right, Environment, R),
|
|
74
|
+
Value is L - R.
|
|
75
|
+
evaluate(mul(Left, Right), Environment, Value) :-
|
|
76
|
+
evaluate(Left, Environment, L),
|
|
77
|
+
evaluate(Right, Environment, R),
|
|
78
|
+
Value is L * R.
|
|
79
|
+
evaluate(div(Left, Right), Environment, Value) :-
|
|
80
|
+
evaluate(Left, Environment, L),
|
|
81
|
+
evaluate(Right, Environment, R),
|
|
82
|
+
Value is L / R.
|
|
83
|
+
|
|
84
|
+
lookup(Name, [Name-Value|_], Value).
|
|
85
|
+
lookup(Name, [_|Rest], Value) :-
|
|
86
|
+
lookup(Name, Rest, Value).
|
|
87
|
+
|
|
88
|
+
% Precedence-aware generation. The right operand is emitted at a stricter
|
|
89
|
+
% minimum precedence than the left operand, preserving left associativity.
|
|
90
|
+
emit_expression(AST) --> emit(AST, 0).
|
|
91
|
+
|
|
92
|
+
emit(AST, Minimum) -->
|
|
93
|
+
{ precedence(AST, Precedence),
|
|
94
|
+
parenthesize(Precedence, Minimum, Wrap) },
|
|
95
|
+
emit_wrapped(Wrap, AST).
|
|
96
|
+
|
|
97
|
+
emit_wrapped(yes, AST) --> ['('], emit_node(AST), [')'].
|
|
98
|
+
emit_wrapped(no, AST) --> emit_node(AST).
|
|
99
|
+
|
|
100
|
+
emit_node(lit(Number)) --> [Number].
|
|
101
|
+
emit_node(var(Name)) --> [Name].
|
|
102
|
+
emit_node(neg(AST)) --> ['-'], emit(AST, 30).
|
|
103
|
+
emit_node(add(Left, Right)) -->
|
|
104
|
+
emit(Left, 10), ['+'], emit(Right, 11).
|
|
105
|
+
emit_node(sub(Left, Right)) -->
|
|
106
|
+
emit(Left, 10), ['-'], emit(Right, 11).
|
|
107
|
+
emit_node(mul(Left, Right)) -->
|
|
108
|
+
emit(Left, 20), ['*'], emit(Right, 21).
|
|
109
|
+
emit_node(div(Left, Right)) -->
|
|
110
|
+
emit(Left, 20), ['/'], emit(Right, 21).
|
|
111
|
+
|
|
112
|
+
precedence(add(_, _), 10).
|
|
113
|
+
precedence(sub(_, _), 10).
|
|
114
|
+
precedence(mul(_, _), 20).
|
|
115
|
+
precedence(div(_, _), 20).
|
|
116
|
+
precedence(neg(_), 30).
|
|
117
|
+
precedence(lit(_), 40).
|
|
118
|
+
precedence(var(_), 40).
|
|
119
|
+
|
|
120
|
+
parenthesize(Precedence, Minimum, yes) :- Precedence < Minimum.
|
|
121
|
+
parenthesize(Precedence, Minimum, no) :- Precedence >= Minimum.
|
|
122
|
+
|
|
123
|
+
% Parse a mixed-precedence expression.
|
|
124
|
+
dcg_expression_example(parsed, AST) :-
|
|
125
|
+
phrase(expression(AST),
|
|
126
|
+
[2, '+', 3, '*', '(', 4, '-', 1, ')']).
|
|
127
|
+
|
|
128
|
+
% Evaluate a parsed expression with variables.
|
|
129
|
+
dcg_expression_example(evaluated, Value) :-
|
|
130
|
+
phrase(expression(AST), [x, '*', '(', y, '+', 2, ')', '-', z]),
|
|
131
|
+
evaluate(AST, [x-4, y-3, z-1], Value).
|
|
132
|
+
|
|
133
|
+
% Generate the minimal parentheses needed to preserve a right-nested
|
|
134
|
+
% subtraction tree, then parse the generated tokens back to the same AST.
|
|
135
|
+
dcg_expression_example(round_trip, Tokens) :-
|
|
136
|
+
AST = sub(lit(20), sub(lit(5), lit(3))),
|
|
137
|
+
phrase(emit_expression(AST), Tokens),
|
|
138
|
+
phrase(expression(AST), Tokens).
|
|
139
|
+
|
|
140
|
+
% phrase/3 lets a larger language parse an expression prefix and keep the rest.
|
|
141
|
+
dcg_expression_example(remainder, Rest) :-
|
|
142
|
+
phrase(expression(mul(var(x), lit(2))),
|
|
143
|
+
[x, '*', 2, then, stop], Rest).
|
|
144
|
+
|
|
145
|
+
% Missing closing parentheses are rejected.
|
|
146
|
+
dcg_expression_example(rejected, malformed_parentheses) :-
|
|
147
|
+
\+ phrase(expression(_), [2, '*', '(', 3, '+', 4]).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
dcg_expression_example(parsed, add(lit(2), mul(lit(3), sub(lit(4), lit(1))))).
|
|
2
|
+
dcg_expression_example(evaluated, 19).
|
|
3
|
+
dcg_expression_example(round_trip, [20, -, '(', 5, -, 3, ')']).
|
|
4
|
+
dcg_expression_example(remainder, [then, stop]).
|
|
5
|
+
dcg_expression_example(rejected, malformed_parentheses).
|
package/package.json
CHANGED
package/playground.html
CHANGED
package/src/solver.js
CHANGED
|
@@ -513,6 +513,23 @@ export class Solver {
|
|
|
513
513
|
break;
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
+
const betweenIterator = bundledBetweenIterator(this, group, goal, env);
|
|
517
|
+
if (betweenIterator != null) {
|
|
518
|
+
const firstResult = betweenIterator.next();
|
|
519
|
+
if (firstResult.done) break;
|
|
520
|
+
stack.push({
|
|
521
|
+
kind: 'resumeBuiltin',
|
|
522
|
+
iterator: betweenIterator,
|
|
523
|
+
goals: rest,
|
|
524
|
+
depth: depth + 1,
|
|
525
|
+
active,
|
|
526
|
+
});
|
|
527
|
+
goals = rest;
|
|
528
|
+
env = firstResult.value;
|
|
529
|
+
depth++;
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
|
|
516
533
|
const memberIterator = bundledMemberIterator(this, group, goal, env);
|
|
517
534
|
if (memberIterator != null) {
|
|
518
535
|
const firstResult = memberIterator.next();
|
|
@@ -1216,6 +1233,53 @@ function pushWfsAnswerFrames(stack, model, group, goal, rest, env, depth, active
|
|
|
1216
1233
|
}
|
|
1217
1234
|
}
|
|
1218
1235
|
|
|
1236
|
+
function bundledBetweenIterator(solver, group, goal, env) {
|
|
1237
|
+
if (solver.registry.eyePrologLibrary !== true ||
|
|
1238
|
+
group.module !== 'prologue' || group.name !== 'between' || group.arity !== 3 ||
|
|
1239
|
+
group.bundledLibrary !== true || group.clauses.length !== 1) {
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// The portable Prologue definition is intentionally kept as the semantic
|
|
1244
|
+
// source of between/3. Its recursive helper, however, carries the output
|
|
1245
|
+
// variable through one fresh clause instance per integer. With persistent
|
|
1246
|
+
// environments that builds a growing variable-alias chain, so dereferencing
|
|
1247
|
+
// the generated value in the caller repeatedly revisits all earlier frames.
|
|
1248
|
+
// Enumerate the canonical bundled relation directly while leaving user
|
|
1249
|
+
// definitions and non-EyeProlog registries on the ordinary Prolog path.
|
|
1250
|
+
return bundledBetweenSolutions(solver, goal, env);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function requireBetweenInteger(term, env) {
|
|
1254
|
+
const value = deref(term, env);
|
|
1255
|
+
if (value.type === VAR) throw new PrologError('instantiation_error');
|
|
1256
|
+
if (value.type !== NUMBER || !isDecimalInteger(value.name)) {
|
|
1257
|
+
throw new PrologError('type_error(integer)', value);
|
|
1258
|
+
}
|
|
1259
|
+
return BigInt(value.name);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function* bundledBetweenSolutions(solver, goal, env) {
|
|
1263
|
+
const lower = requireBetweenInteger(goal.args[0], env);
|
|
1264
|
+
const upper = requireBetweenInteger(goal.args[1], env);
|
|
1265
|
+
const requested = deref(goal.args[2], env);
|
|
1266
|
+
|
|
1267
|
+
if (requested.type !== VAR) {
|
|
1268
|
+
if (requested.type !== NUMBER || !isDecimalInteger(requested.name)) {
|
|
1269
|
+
throw new PrologError('type_error(integer)', requested);
|
|
1270
|
+
}
|
|
1271
|
+
const value = BigInt(requested.name);
|
|
1272
|
+
if (value >= lower && value <= upper) yield env;
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
for (let value = lower; value <= upper; value++) {
|
|
1277
|
+
const next = env.clone();
|
|
1278
|
+
solver.stats.unify_calls++;
|
|
1279
|
+
if (unify(goal.args[2], numberTerm(value), next)) yield next;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1219
1283
|
function bundledMemberIterator(solver, group, goal, env) {
|
|
1220
1284
|
if (solver.registry.eyePrologLibrary !== true ||
|
|
1221
1285
|
!['lists', 'prologue'].includes(group.module) || group.name !== 'member' || group.arity !== 2 ||
|
package/test/run-regression.mjs
CHANGED
|
@@ -2398,6 +2398,22 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
|
|
|
2398
2398
|
assertEqual(result.stderr, '', 'stderr');
|
|
2399
2399
|
},
|
|
2400
2400
|
},
|
|
2401
|
+
{
|
|
2402
|
+
name: 'between/3 generated values avoid recursive environment chains (issue #52)',
|
|
2403
|
+
run: () => {
|
|
2404
|
+
const goalText = 'between(1, 1024, X), X < 0';
|
|
2405
|
+
const program = Program.parse('', { autoloadGoals: [goalText] });
|
|
2406
|
+
const solver = new Solver(program, { registry: getEyePrologRegistry() });
|
|
2407
|
+
const goal = parseGoalText(goalText, {
|
|
2408
|
+
operatorDefinitions: [...solver.program.operators.values()],
|
|
2409
|
+
});
|
|
2410
|
+
let answers = 0;
|
|
2411
|
+
for (const _env of solver.solve([goal], new Env(), 0)) answers++;
|
|
2412
|
+
assertEqual(answers, 0, 'positive generated values fail X < 0');
|
|
2413
|
+
assertEqual(solver.stats.unify_calls, 1024, 'one output unification per generated integer');
|
|
2414
|
+
assertEqual(solver.stats.max_depth <= 4, true, 'generation stays at bounded solver depth');
|
|
2415
|
+
},
|
|
2416
|
+
},
|
|
2401
2417
|
{
|
|
2402
2418
|
name: 'library(lists) length/2 stays relational and call_nth/2 autoloads (issue #28)',
|
|
2403
2419
|
run: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5632,6 +5632,62 @@ that argument. A variable body raises `instantiation_error`; a non-callable
|
|
|
5632
5632
|
body raises `type_error(callable)`. EyeProlog performs terminal-sequence checks
|
|
5633
5633
|
and reports the portable ISO `type_error(list)` error term.
|
|
5634
5634
|
|
|
5635
|
+
#### A bidirectional expression grammar
|
|
5636
|
+
|
|
5637
|
+
DCGs become more useful when the grammar produces a structured term rather than
|
|
5638
|
+
merely accepting a token list. The checked
|
|
5639
|
+
[`dcg-expression-language.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/dcg-expression-language.pl)
|
|
5640
|
+
example implements a small arithmetic language in both directions. Its parser
|
|
5641
|
+
respects precedence and left associativity while constructing an abstract syntax
|
|
5642
|
+
tree:
|
|
5643
|
+
|
|
5644
|
+
```text
|
|
5645
|
+
expression(AST) -->
|
|
5646
|
+
term(First),
|
|
5647
|
+
additive_tail(First, AST).
|
|
5648
|
+
|
|
5649
|
+
additive_tail(Left, AST) -->
|
|
5650
|
+
['+'], term(Right),
|
|
5651
|
+
{ Next = add(Left, Right) },
|
|
5652
|
+
additive_tail(Next, AST).
|
|
5653
|
+
additive_tail(AST, AST) --> [].
|
|
5654
|
+
```
|
|
5655
|
+
|
|
5656
|
+
The accumulator removes left recursion without moving parsing into JavaScript.
|
|
5657
|
+
A second DCG walks the AST in the other direction and emits only the parentheses
|
|
5658
|
+
needed to preserve its structure. The example therefore exercises parsing,
|
|
5659
|
+
semantic actions, nonterminal-to-nonterminal state hand-off, generation,
|
|
5660
|
+
backtracking, `phrase/3` remainder handling, and AST-to-token-to-AST
|
|
5661
|
+
round-tripping. The checked answers are in
|
|
5662
|
+
[`examples/output/dcg-expression-language.pl`](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/dcg-expression-language.pl).
|
|
5663
|
+
|
|
5664
|
+
#### Deep sequence hand-off
|
|
5665
|
+
|
|
5666
|
+
`library(iso_ext)` provides the common `...//0` helper, which describes an
|
|
5667
|
+
arbitrary number of input elements. It is not part of ISO Part 3, but it is a
|
|
5668
|
+
useful interoperability and stress-test relation. A compact hand-off test is:
|
|
5669
|
+
|
|
5670
|
+
```text
|
|
5671
|
+
a --> ..., epsilon.
|
|
5672
|
+
epsilon --> [].
|
|
5673
|
+
```
|
|
5674
|
+
|
|
5675
|
+
Here the remaining sequence is repeatedly passed from `...//0` to another
|
|
5676
|
+
nonterminal. For a finite compact list, EyeProlog can scan the arbitrary
|
|
5677
|
+
sequence iteratively instead of consuming one ordinary solver depth level per
|
|
5678
|
+
list cell. If the continuation is structurally proven to be a zero-width
|
|
5679
|
+
identity grammar such as `epsilon//0`, the hand-off can be continued without
|
|
5680
|
+
constructing a fresh general clause-resolution frame at every suffix. The list
|
|
5681
|
+
spine is still traversed; this is a control/allocation optimization rather than
|
|
5682
|
+
an O(1) semantic shortcut.
|
|
5683
|
+
|
|
5684
|
+
The optimization is deliberately narrow. `phrase(..., Sequence, Rest)` still
|
|
5685
|
+
enumerates the valid remainders, open or non-compact inputs retain ordinary
|
|
5686
|
+
relational behavior, and grammars that can consume or constrain the remainder
|
|
5687
|
+
are not treated as identity continuations. `time/1` can be used in normal mode
|
|
5688
|
+
to measure such runs; its inference counter records solver-level inferences and
|
|
5689
|
+
does not count every internal step of an optimized scanner.
|
|
5690
|
+
|
|
5635
5691
|
Part 3 leaves `\+//1` and standalone `->//2` implementation dependent.
|
|
5636
5692
|
EyeProlog uses non-consuming negation (`\+ Body` tests from the current state)
|
|
5637
5693
|
and threads the state produced by the condition into the then-grammar.
|
|
@@ -6895,7 +6951,7 @@ Review questions:
|
|
|
6895
6951
|
</figure>
|
|
6896
6952
|
|
|
6897
6953
|
The [examples directory](https://github.com/eyereasoner/eyeprolog/tree/main/examples/) is the book's executable companion. The
|
|
6898
|
-
top-level directory contains **
|
|
6954
|
+
top-level directory contains **212 self-contained runnable programs**. Every
|
|
6899
6955
|
source program has an exact answer file under
|
|
6900
6956
|
[examples/output](https://github.com/eyereasoner/eyeprolog/tree/main/examples/output/), and **61 selected programs** have a checked
|
|
6901
6957
|
explanation under [examples/proof](https://github.com/eyereasoner/eyeprolog/tree/main/examples/proof/). The thematic tables below link every top-level program and open the program
|
|
@@ -6953,6 +7009,7 @@ mode at a time.
|
|
|
6953
7009
|
| [Atomic conversion](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-atomic-conversion.pl) | Atom splitting, character atoms, Unicode codes, and numeric parsing. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-atomic-conversion.pl) |
|
|
6954
7010
|
| [Control and errors](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-control-and-errors.pl) | `call/1`, `once/1`, cut, if-then-else, `throw/1`, and `catch/3`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-control-and-errors.pl) |
|
|
6955
7011
|
| [DCG command parser](https://github.com/eyereasoner/eyeprolog/blob/main/examples/dcg-command-parser.pl) | A Part 3 grammar parses token lists into application terms, generates tokens, preserves a remainder, and rejects malformed input. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/dcg-command-parser.pl) |
|
|
7012
|
+
| [DCG expression language](https://github.com/eyereasoner/eyeprolog/blob/main/examples/dcg-expression-language.pl) | A precedence-aware bidirectional grammar builds arithmetic ASTs, evaluates variable expressions, regenerates minimally parenthesized tokens, round-trips syntax, and preserves a remainder. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/dcg-expression-language.pl) |
|
|
6956
7013
|
| [Dynamic database](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-dynamic-database.pl) | Initialization and ordered updates to a declared dynamic procedure. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-dynamic-database.pl) · [proof](https://github.com/eyereasoner/eyeprolog/blob/main/examples/proof/iso-dynamic-database.pl) |
|
|
6957
7014
|
| [Grouped solutions](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-grouped-solutions.pl) | `findall/3`, `bagof/3`, `setof/3`, existential qualification, and `clause/2`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-grouped-solutions.pl) |
|
|
6958
7015
|
| [Integer arithmetic](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-integer-arithmetic.pl) | Integer quotient/remainder choices plus bit operations. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-integer-arithmetic.pl) |
|
|
@@ -7297,7 +7354,7 @@ hand.
|
|
|
7297
7354
|
|
|
7298
7355
|
#### Running and extending the corpus
|
|
7299
7356
|
|
|
7300
|
-
Run all
|
|
7357
|
+
Run all 212 normal answer goldens and the 61 selected proof goldens with:
|
|
7301
7358
|
|
|
7302
7359
|
```sh
|
|
7303
7360
|
npm run test:examples
|
|
@@ -7361,7 +7418,7 @@ The complete suite must pass before release. The file-based conformance corpus
|
|
|
7361
7418
|
contains 791 cases, including 386 focused ISO
|
|
7362
7419
|
cases derived from the success, failure, mode, and error behavior in
|
|
7363
7420
|
ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
|
|
7364
|
-
Separate exact-output suites check
|
|
7421
|
+
Separate exact-output suites check 212 normal
|
|
7365
7422
|
examples and 61 proof examples; all extracted book programs are parsed and
|
|
7366
7423
|
their declared goals are executed. The eight-case
|
|
7367
7424
|
playground contract suite imports the production worker, sends real reasoning
|
package/why-eyeprolog.md
CHANGED
|
@@ -60,6 +60,14 @@ positive Datalog closures with a shared relation-wide table, but the admission
|
|
|
60
60
|
heuristics are implementation details. Programs should rely on the documented
|
|
61
61
|
semantics and finiteness conditions, not on a particular internal threshold.
|
|
62
62
|
|
|
63
|
+
DCGs follow the same rule. Their meaning remains the ISO Part 3 difference-list
|
|
64
|
+
model, while finite sequence scans and proven zero-width hand-offs may use
|
|
65
|
+
lighter internal control paths so deep grammars do not pay one general solver
|
|
66
|
+
frame per token. Relational remainder-producing modes are preserved. The
|
|
67
|
+
checked `examples/dcg-expression-language.pl` program shows the declarative side
|
|
68
|
+
of that design: one grammar builds precedence-aware syntax trees and another
|
|
69
|
+
generates minimally parenthesized token sequences back from them.
|
|
70
|
+
|
|
63
71
|
## Why proofs?
|
|
64
72
|
|
|
65
73
|
An answer says that a goal succeeded. A proof records one successful route
|