eyelang 1.7.14 → 1.7.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 CHANGED
@@ -49,6 +49,7 @@ console.log(result.stdout);
49
49
  - [Playground](https://eyereasoner.github.io/eyelang/playground)
50
50
  - [Guide](docs/guide.md)
51
51
  - [Language reference](docs/language-reference.md)
52
+ - [A Compact Reasoning Workbench](docs/compact-reasoning-workbench.md)
52
53
 
53
54
  For local browser use, serve the checkout first so the playground can load ES modules and example files:
54
55
 
@@ -0,0 +1,35 @@
1
+ # A Compact Reasoning Workbench
2
+
3
+ Eyelang began as a small thing: a few facts, a few rules, a way to ask a question and get an answer.
4
+
5
+ At first it solved the familiar problems. Paths through graphs. Ancestors in families. Tiny proofs that fit on one screen. The early examples in `examples/` keep that beginning visible: they show the core idiom before anything becomes exotic. Facts describe a world, rules extend it, goals ask for visible consequences, and answers come back as ordinary terms.
6
+
7
+ Then the examples started to widen.
8
+
9
+ Mathematical programs turned recurrence, tabling, aggregation, and finite search into executable relations. Fibonacci numbers, Collatz traces, integer partitions, Catalan numbers, binomial identities, Stirling and Bell numbers, graph paths, and scheduling problems made performance visible without hiding the logic behind a host-language library.
10
+
11
+ Knowledge-modeling examples pulled the same syntax in a different direction. Policies, GDPR-style compliance, access control, clinical screening, deontic rules, purpose mapping, alignment flows, and context audits were not mainly numerical problems. They were problems of explanation, qualification, exception, and traceability. In those cases Eyelang stayed close to the vocabulary of the domain.
12
+
13
+ Science and engineering examples stretched the language again. Filters, beams, control systems, kinetics, gas laws, gradients, coding theory, and optimization showed that structured calculation could live inside a larger rule-based argument. The programs remained small enough to read, but substantial enough to test the engine.
14
+
15
+ Then came examples that looked more like systems research than toy logic programs. Markov Logic Network-style scoring represented weighted possible worlds while keeping the probability model explicit. Type inference, abstract interpretation, pointer analysis, SAT solving, truth maintenance, and register allocation showed that programming-language and analysis problems could be written as compact theories rather than opaque procedures.
16
+
17
+ At the far edge were the deliberately uncomfortable examples: partial evaluation, Knuth-Bendix completion, CDCL-style SAT solving, and bounded equality saturation. They did not pretend that Eyelang was a mutable union-find library, an industrial optimizer, or a replacement for every specialized solver. They were useful precisely because they marked the boundary. Some problems had to be bounded. Some had to be expressed carefully. Some revealed where a small logic engine ends and another tool should take over.
18
+
19
+ That made `examples/` more than a gallery. It became executable documentation, regression evidence, and a map of the language's range. Each file was a compact theory with an expected output that could be run, inspected, and kept honest by the test suite.
20
+
21
+ The surprise was not that every hard problem became easy. They did not.
22
+
23
+ The surprise was that the same small core kept reaching.
24
+
25
+ Across mathematics, knowledge modeling, science, program analysis, symbolic reasoning, optimization, explanation, and search, Eyelang stayed recognizable. The programs were not black boxes. They were little theories: readable, testable, and executable. When performance mattered, tabling and careful formulations often carried them farther than expected.
26
+
27
+ By the end, Eyelang no longer looked like a toy language with a few clever demos.
28
+
29
+ It looked like a compact reasoning workbench.
30
+
31
+ Small enough to understand.
32
+
33
+ Expressive enough to surprise.
34
+
35
+ Fast enough to take seriously.
package/docs/guide.md CHANGED
@@ -335,6 +335,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
335
335
  | [`canary-release.eye`](../examples/canary-release.eye) | Decides canary rollout or rollback. | [`output/canary-release.eye`](../examples/output/canary-release.eye) |
336
336
  | [`cat-koko.eye`](../examples/cat-koko.eye) | Demonstrates named existential witnesses from a Cat Koko rule pattern. | [`output/cat-koko.eye`](../examples/output/cat-koko.eye) |
337
337
  | [`catalan-convolution.eye`](../examples/catalan-convolution.eye) | Computes Catalan numbers by tabled convolution. | [`output/catalan-convolution.eye`](../examples/output/catalan-convolution.eye) |
338
+ | [`cdcl-sat-solver.eye`](../examples/cdcl-sat-solver.eye) | Simulates one CDCL conflict-analysis step with a learned clause and backjumped model. | [`output/cdcl-sat-solver.eye`](../examples/output/cdcl-sat-solver.eye) |
338
339
  | [`chart-parser.eye`](../examples/chart-parser.eye) | Parses small sentences with a tabled chart parser. | [`output/chart-parser.eye`](../examples/output/chart-parser.eye) |
339
340
  | [`clinical-trial-screening.eye`](../examples/clinical-trial-screening.eye) | Screens candidates for a trial. | [`output/clinical-trial-screening.eye`](../examples/output/clinical-trial-screening.eye) |
340
341
  | [`collatz-1000.eye`](../examples/collatz-1000.eye) | Materializes Collatz trajectories for starts 1000 down to 1. | [`output/collatz-1000.eye`](../examples/output/collatz-1000.eye) |
@@ -370,6 +371,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
370
371
  | [`drone-corridor-planner.eye`](../examples/drone-corridor-planner.eye) | Plans bounded drone corridor routes. | [`output/drone-corridor-planner.eye`](../examples/output/drone-corridor-planner.eye) |
371
372
  | [`easter-computus.eye`](../examples/easter-computus.eye) | Computes Gregorian Easter dates. | [`output/easter-computus.eye`](../examples/output/easter-computus.eye) |
372
373
  | [`electrical-rc-filter.eye`](../examples/electrical-rc-filter.eye) | Sizes an RC low-pass filter. | [`output/electrical-rc-filter.eye`](../examples/output/electrical-rc-filter.eye) |
374
+ | [`equality-saturation.eye`](../examples/equality-saturation.eye) | Performs bounded equality saturation and extracts the cheapest equivalent expression. | [`output/equality-saturation.eye`](../examples/output/equality-saturation.eye) |
373
375
  | [`epidemic-policy.eye`](../examples/epidemic-policy.eye) | Chooses policies from risk and social cost. | [`output/epidemic-policy.eye`](../examples/output/epidemic-policy.eye) |
374
376
  | [`equivalence-classes-overlap-implies-same-class.eye`](../examples/equivalence-classes-overlap-implies-same-class.eye) | Packages the shared-member proof pattern for equivalence classes. | [`output/equivalence-classes-overlap-implies-same-class.eye`](../examples/output/equivalence-classes-overlap-implies-same-class.eye) |
375
377
  | [`eulerian-path.eye`](../examples/eulerian-path.eye) | Finds an Eulerian path using each edge once. | [`output/eulerian-path.eye`](../examples/output/eulerian-path.eye) |
@@ -407,6 +409,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
407
409
  | [`intuitionistic-logic-kripke.eye`](../examples/intuitionistic-logic-kripke.eye) | Emulates intuitionistic Kripke forcing and constructive implication. | [`output/intuitionistic-logic-kripke.eye`](../examples/output/intuitionistic-logic-kripke.eye) |
408
410
  | [`job-shop-scheduling.eye`](../examples/job-shop-scheduling.eye) | Searches a small job-shop schedule and minimizes makespan. | [`output/job-shop-scheduling.eye`](../examples/output/job-shop-scheduling.eye) |
409
411
  | [`knapsack-optimization.eye`](../examples/knapsack-optimization.eye) | Optimizes a finite 0/1 knapsack pack with aggregation. | [`output/knapsack-optimization.eye`](../examples/output/knapsack-optimization.eye) |
412
+ | [`knuth-bendix-completion.eye`](../examples/knuth-bendix-completion.eye) | Checks bounded Knuth-Bendix-style critical pairs for joinability. | [`output/knuth-bendix-completion.eye`](../examples/output/knuth-bendix-completion.eye) |
410
413
  | [`knowledge-engineering-alignment-flow.eye`](../examples/knowledge-engineering-alignment-flow.eye) | Specializes reusable alignment rules into a target-shaped flow view. | [`output/knowledge-engineering-alignment-flow.eye`](../examples/output/knowledge-engineering-alignment-flow.eye) |
411
414
  | [`language.eye`](../examples/language.eye) | Shows the modern Eyelang surface syntax in one compact recursive graph example. | [`output/language.eye`](../examples/output/language.eye) |
412
415
  | [`law-of-cosines.eye`](../examples/law-of-cosines.eye) | Computes a triangle side by cosine law. | [`output/law-of-cosines.eye`](../examples/output/law-of-cosines.eye) |
@@ -434,6 +437,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
434
437
  | [`odrl-dpv-risk-ranked.eye`](../examples/odrl-dpv-risk-ranked.eye) | Ranks data-policy risks and mitigations. | [`output/odrl-dpv-risk-ranked.eye`](../examples/output/odrl-dpv-risk-ranked.eye) |
435
438
  | [`orbital-transfer-design.eye`](../examples/orbital-transfer-design.eye) | Designs a Hohmann orbital transfer. | [`output/orbital-transfer-design.eye`](../examples/output/orbital-transfer-design.eye) |
436
439
  | [`path-discovery.eye`](../examples/path-discovery.eye) | Discovers bounded air-route paths. | [`output/path-discovery.eye`](../examples/output/path-discovery.eye) |
440
+ | [`partial-evaluator.eye`](../examples/partial-evaluator.eye) | Specializes tiny expression programs by folding static inputs into residual code. | [`output/partial-evaluator.eye`](../examples/output/partial-evaluator.eye) |
437
441
  | [`peano-arithmetic.eye`](../examples/peano-arithmetic.eye) | Computes Peano addition, multiplication, and factorial. | [`output/peano-arithmetic.eye`](../examples/output/peano-arithmetic.eye) |
438
442
  | [`peano-calculus.eye`](../examples/peano-calculus.eye) | Computes Peano addition, multiplication, and factorial. | [`output/peano-calculus.eye`](../examples/output/peano-calculus.eye) |
439
443
  | [`peasant.eye`](../examples/peasant.eye) | Performs peasant multiplication and exponentiation. | [`output/peasant.eye`](../examples/output/peasant.eye) |
@@ -445,6 +449,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
445
449
  | [`proof-contrapositive.eye`](../examples/proof-contrapositive.eye) | Models proof by contrapositive. | [`output/proof-contrapositive.eye`](../examples/output/proof-contrapositive.eye) |
446
450
  | [`quadratic-formula.eye`](../examples/quadratic-formula.eye) | Solves sample quadratic equations. | [`output/quadratic-formula.eye`](../examples/output/quadratic-formula.eye) |
447
451
  | [`radioactive-decay.eye`](../examples/radioactive-decay.eye) | Computes radioactive decay over time. | [`output/radioactive-decay.eye`](../examples/output/radioactive-decay.eye) |
452
+ | [`register-allocation.eye`](../examples/register-allocation.eye) | Allocates temporaries to two registers with spilling by bounded graph-coloring search. | [`output/register-allocation.eye`](../examples/output/register-allocation.eye) |
448
453
  | [`reusable-builtins.eye`](../examples/reusable-builtins.eye) | Tours reusable numeric, list, and string builtins. | [`output/reusable-builtins.eye`](../examples/output/reusable-builtins.eye) |
449
454
  | [`riemann-hypothesis.eye`](../examples/riemann-hypothesis.eye) | Checks a finite catalogue of non-trivial zeta zeros against the Riemann-hypothesis condition. | [`output/riemann-hypothesis.eye`](../examples/output/riemann-hypothesis.eye) |
450
455
  | [`route-planning.eye`](../examples/route-planning.eye) | Finds routes and records them as explicit route terms. | [`output/route-planning.eye`](../examples/output/route-planning.eye) |
@@ -0,0 +1,65 @@
1
+ % A tiny CDCL-style SAT trace with one learned clause.
2
+ %
3
+ % Industrial CDCL solvers use watched literals, mutable trails, non-chronological
4
+ % backjumping, clause databases, restarts, and activity heuristics. Eyelang has
5
+ % no mutable state or destructive trail updates, so this example models one small
6
+ % conflict-analysis episode as relations over immutable terms.
7
+ %
8
+ % Formula:
9
+ % c1: not(a) or c
10
+ % c2: not(c)
11
+ % c3: a or b
12
+ %
13
+ % Decision a=true forces c=true by c1, which conflicts with c2. Resolving the
14
+ % reason c1 with the conflict c2 learns not(a). After backjumping, learned
15
+ % not(a) forces a=false, c2 gives c=false, and c3 forces b=true.
16
+
17
+ materialize(cdclAnswer, 2).
18
+
19
+ clause(c1, [neg(a), pos(c)]).
20
+ clause(c2, [neg(c)]).
21
+ clause(c3, [pos(a), pos(b)]).
22
+
23
+ % Initial trail before conflict analysis.
24
+ initial_value(a, true, decision(level1)).
25
+ initial_value(c, true, implied_by(c1)).
26
+
27
+ % The current trail makes a literal true or false.
28
+ lit_true(pos(?var), ?value_rel) :- call_value(?value_rel, ?var, true, ?).
29
+ lit_true(neg(?var), ?value_rel) :- call_value(?value_rel, ?var, false, ?).
30
+ lit_false(pos(?var), ?value_rel) :- call_value(?value_rel, ?var, false, ?).
31
+ lit_false(neg(?var), ?value_rel) :- call_value(?value_rel, ?var, true, ?).
32
+
33
+ % Eyelang cannot pass predicates as first-class values, so this small dispatcher
34
+ % lets the same literal helpers inspect either the initial or final trail.
35
+ call_value(initial, ?var, ?value, ?reason) :- initial_value(?var, ?value, ?reason).
36
+ call_value(final, ?var, ?value, ?reason) :- final_value(?var, ?value, ?reason).
37
+
38
+ % A clause is conflicting when all of its literals are false under a trail.
39
+ conflict(?trail, ?clause) :-
40
+ clause(?clause, ?literals),
41
+ forall(member(?literal, ?literals), lit_false(?literal, ?trail)).
42
+
43
+ % The learned clause for this tiny implication graph is obtained by resolving
44
+ % the conflict clause not(c) with c's reason not(a) or c, yielding not(a).
45
+ learned_clause(l1, [neg(a)]).
46
+ learned_from(l1, resolve(c2, c1, pivot(c))).
47
+
48
+ % After backjumping, the learned unit clause fixes a=false. Then the original
49
+ % clauses imply c=false and b=true.
50
+ final_value(a, false, learned(l1)).
51
+ final_value(c, false, unit(c2)).
52
+ final_value(b, true, unit(c3)).
53
+
54
+ model_satisfies_clause(?trail, ?clause) :-
55
+ clause(?clause, ?literals),
56
+ member(?literal, ?literals),
57
+ lit_true(?literal, ?trail).
58
+
59
+ final_model_ok(ok) :- forall(clause(?name, ?), model_satisfies_clause(final, ?name)).
60
+
61
+ cdclAnswer(conflict_clause, ?clause) :- conflict(initial, ?clause).
62
+ cdclAnswer(learned_clause, ?literals) :- learned_clause(l1, ?literals).
63
+ cdclAnswer(learned_from, ?reason) :- learned_from(l1, ?reason).
64
+ cdclAnswer(final_value(?var), ?value) :- final_model_ok(ok), final_value(?var, ?value, ?).
65
+ cdclAnswer(note, "one learned clause makes the final model satisfy all original clauses") :- final_model_ok(ok).
@@ -0,0 +1,84 @@
1
+ % Bounded equality saturation over tiny arithmetic expression terms.
2
+ %
3
+ % Real equality-saturation engines use mutable e-graphs, union-find classes,
4
+ % congruence closure, rewrite scheduling, and cost-based extraction. Eyelang has
5
+ % none of those primitives, so this example simulates the idea relationally: it
6
+ % enumerates expressions reachable within a small rewrite-fuel bound, scores the
7
+ % generated terms, and extracts the cheapest equivalent expression.
8
+ %
9
+ % The point is not to be fast. The point is to show the declarative shape of an
10
+ % e-graph optimizer in a language that was not designed for it.
11
+
12
+ materialize(egraphAnswer, 2).
13
+
14
+ table(equivalent_at_depth, 3).
15
+ table(expr_cost, 2).
16
+
17
+ % Start expression:
18
+ % ((x + 0) * 1) + 2 * (1 + 2)
19
+ % The best bounded rewrite result should be x + 6.
20
+ expr(start, add(mul(add(x, 0), 1), mul(2, add(1, 2)))).
21
+ fuel(6).
22
+
23
+ % Directed rewrite rules. A true e-graph would keep equalities in equivalence
24
+ % classes. This bounded version uses oriented rewrites so search terminates.
25
+ rewrite(add(?x, 0), ?x).
26
+ rewrite(add(0, ?x), ?x).
27
+ rewrite(mul(?x, 1), ?x).
28
+ rewrite(mul(1, ?x), ?x).
29
+ rewrite(mul(?x, 0), 0).
30
+ rewrite(mul(0, ?x), 0).
31
+
32
+ % Constant folding and one distributivity rule make the search space less toyish.
33
+ rewrite(add(?a, ?b), ?c) :- add(?a, ?b, ?c).
34
+ rewrite(mul(?a, ?b), ?c) :- mul(?a, ?b, ?c).
35
+ rewrite(mul(?x, add(?y, ?z)), add(mul(?x, ?y), mul(?x, ?z))).
36
+
37
+ % Apply a rewrite at the root or inside one subterm.
38
+ rewrite_anywhere(?in, ?out) :- rewrite(?in, ?out).
39
+ rewrite_anywhere(add(?a, ?b), add(?new_a, ?b)) :- rewrite_anywhere(?a, ?new_a).
40
+ rewrite_anywhere(add(?a, ?b), add(?a, ?new_b)) :- rewrite_anywhere(?b, ?new_b).
41
+ rewrite_anywhere(mul(?a, ?b), mul(?new_a, ?b)) :- rewrite_anywhere(?a, ?new_a).
42
+ rewrite_anywhere(mul(?a, ?b), mul(?a, ?new_b)) :- rewrite_anywhere(?b, ?new_b).
43
+
44
+ % Fuel-bounded closure. Every depth represents exactly that many rewrite steps;
45
+ % candidate_expression/1 looks across all depths from zero to the fuel limit.
46
+ equivalent_at_depth(0, ?expr, ?expr).
47
+ equivalent_at_depth(?depth, ?expr, ?out) :-
48
+ gt(?depth, 0),
49
+ sub(?depth, 1, ?previous_depth),
50
+ equivalent_at_depth(?previous_depth, ?expr, ?mid),
51
+ rewrite_anywhere(?mid, ?out).
52
+
53
+ candidate_expression(?candidate) :-
54
+ expr(start, ?expr),
55
+ fuel(?fuel),
56
+ between(0, ?fuel, ?depth),
57
+ equivalent_at_depth(?depth, ?expr, ?candidate).
58
+
59
+ % A tiny cost model for extraction. Leaves cost 1; compound nodes cost 1 plus
60
+ % their children. The numeric range is deliberately bounded for the generated
61
+ % constants in this example.
62
+ expr_cost(x, 1).
63
+ expr_cost(?n, 1) :- between(0, 20, ?n).
64
+ expr_cost(add(?a, ?b), ?cost) :-
65
+ expr_cost(?a, ?a_cost),
66
+ expr_cost(?b, ?b_cost),
67
+ add(?a_cost, ?b_cost, ?children),
68
+ add(?children, 1, ?cost).
69
+ expr_cost(mul(?a, ?b), ?cost) :-
70
+ expr_cost(?a, ?a_cost),
71
+ expr_cost(?b, ?b_cost),
72
+ add(?a_cost, ?b_cost, ?children),
73
+ add(?children, 1, ?cost).
74
+
75
+ best_expression(?expr, ?cost) :-
76
+ aggregate_min([?candidate_cost, ?candidate], ?candidate,
77
+ (candidate_expression(?candidate), expr_cost(?candidate, ?candidate_cost)),
78
+ [?cost, ?expr], ?expr).
79
+
80
+ egraphAnswer(start, ?expr) :- expr(start, ?expr).
81
+ egraphAnswer(best, ?expr) :- best_expression(?expr, ?).
82
+ egraphAnswer(cost, ?cost) :- best_expression(?, ?cost).
83
+ egraphAnswer(candidate_count, ?count) :- countall(candidate_expression(?), ?count).
84
+ egraphAnswer(note, "bounded equality saturation extracts the cheapest term without a real e-graph") :- best_expression(?, ?).
@@ -0,0 +1,55 @@
1
+ % Bounded Knuth-Bendix-style completion for append/2 terms.
2
+ %
3
+ % Full Knuth-Bendix completion orients equations, generates all critical pairs,
4
+ % reduces them, and may add new rules until all overlaps are joinable. That is
5
+ % a difficult global search problem. This example keeps the recognizable shape
6
+ % but bounds the hard part: the rewrite system is fixed, and a small catalog of
7
+ % critical overlaps is checked for joinability by normalization.
8
+
9
+ materialize(kbAnswer, 2).
10
+
11
+ table(normal_form, 2).
12
+
13
+ % Oriented equations for a monoid-like append constructor.
14
+ % append(nil, x) -> x
15
+ % append(x, nil) -> x
16
+ % append(append(x,y),z)-> append(x, append(y,z))
17
+ % The third rule right-associates terms so normalization terminates here.
18
+ oriented_rule(left_identity, append(nil, ?x), ?x).
19
+ oriented_rule(right_identity, append(?x, nil), ?x).
20
+ oriented_rule(associate_right, append(append(?x, ?y), ?z), append(?x, append(?y, ?z))).
21
+
22
+ % One rewrite at the root or below an append node.
23
+ rewrite_once(?in, ?out, ?rule) :- oriented_rule(?rule, ?in, ?out).
24
+ rewrite_once(append(?a, ?b), append(?new_a, ?b), ?rule) :- rewrite_once(?a, ?new_a, ?rule).
25
+ rewrite_once(append(?a, ?b), append(?a, ?new_b), ?rule) :- rewrite_once(?b, ?new_b, ?rule).
26
+
27
+ % Normalization by repeated rewriting. The rules above are oriented so that the
28
+ % recursive search has a normal form for the bounded terms in this example.
29
+ normal_form(?term, ?term) :- not(rewrite_once(?term, ?, ?)).
30
+ normal_form(?term, ?normal) :- rewrite_once(?term, ?next, ?), normal_form(?next, ?normal).
31
+
32
+ % A few critical pairs that arise from overlapping the three oriented rules.
33
+ % Each pair records the two one-step reducts that must later normalize together.
34
+ critical_pair(left_identity_assoc,
35
+ append(a, b),
36
+ append(nil, append(a, b))).
37
+ critical_pair(right_identity_assoc,
38
+ append(a, b),
39
+ append(a, append(nil, b))).
40
+ critical_pair(nested_assoc,
41
+ append(append(a, append(b, c)), d),
42
+ append(append(a, b), append(c, d))).
43
+
44
+ joined_pair(?name) :-
45
+ critical_pair(?name, ?left, ?right),
46
+ normal_form(?left, ?normal),
47
+ normal_form(?right, ?normal).
48
+
49
+ sample_term(append(append(nil, append(a, nil)), append(b, c))).
50
+
51
+ kbAnswer(sample_normal_form, ?normal) :- sample_term(?term), normal_form(?term, ?normal).
52
+ kbAnswer(joined_critical_pair, ?name) :- joined_pair(?name).
53
+ kbAnswer(oriented_rule_count, ?count) :- countall(oriented_rule(?, ?, ?), ?count).
54
+ kbAnswer(joined_critical_pair_count, ?count) :- countall(joined_pair(?), ?count).
55
+ kbAnswer(note, "a bounded completion check proves the selected critical pairs join") :- joined_pair(nested_assoc).
@@ -0,0 +1,7 @@
1
+ cdclAnswer(conflict_clause, c2).
2
+ cdclAnswer(learned_clause, [neg(a)]).
3
+ cdclAnswer(learned_from, resolve(c2, c1, pivot(c))).
4
+ cdclAnswer(final_value(a), false).
5
+ cdclAnswer(final_value(c), false).
6
+ cdclAnswer(final_value(b), true).
7
+ cdclAnswer(note, "one learned clause makes the final model satisfy all original clauses").
@@ -0,0 +1,5 @@
1
+ egraphAnswer(start, add(mul(add(x, 0), 1), mul(2, add(1, 2)))).
2
+ egraphAnswer(best, add(x, 6)).
3
+ egraphAnswer(cost, 3).
4
+ egraphAnswer(candidate_count, 32).
5
+ egraphAnswer(note, "bounded equality saturation extracts the cheapest term without a real e-graph").
@@ -0,0 +1,7 @@
1
+ kbAnswer(sample_normal_form, append(a, append(b, c))).
2
+ kbAnswer(joined_critical_pair, left_identity_assoc).
3
+ kbAnswer(joined_critical_pair, right_identity_assoc).
4
+ kbAnswer(joined_critical_pair, nested_assoc).
5
+ kbAnswer(oriented_rule_count, 3).
6
+ kbAnswer(joined_critical_pair_count, 3).
7
+ kbAnswer(note, "a bounded completion check proves the selected critical pairs join").
@@ -0,0 +1,4 @@
1
+ partialEvalAnswer(residual(poly_y), add(mul(const(10), var(y)), const(13))).
2
+ partialEvalAnswer(residual(static_branch), const(11)).
3
+ partialEvalAnswer(residual(dynamic_branch), if(var(flag), const(11), mul(var(y), const(2)))).
4
+ partialEvalAnswer(note, "static inputs are folded while dynamic variables remain as residual code").
@@ -0,0 +1,8 @@
1
+ registerAnswer(best_allocation, [bind(a, r1), bind(b, spill), bind(c, r2), bind(d, r1)]).
2
+ registerAnswer(spill_cost, 1).
3
+ registerAnswer(place(a), r1).
4
+ registerAnswer(place(b), spill).
5
+ registerAnswer(place(c), r2).
6
+ registerAnswer(place(d), r1).
7
+ registerAnswer(valid_allocation_count, 33).
8
+ registerAnswer(note, "the cheapest solution spills b to color the a-b-c triangle with two registers").
@@ -0,0 +1,74 @@
1
+ % A tiny partial evaluator for expression terms.
2
+ %
3
+ % Real partial evaluation can specialize interpreters, control unfolding, avoid
4
+ % code explosion, and even approach self-application. This bounded Eyelang
5
+ % version specializes a miniature expression language with known static inputs:
6
+ % constants are folded, known variables are substituted, and dynamic variables
7
+ % remain as residual code.
8
+
9
+ materialize(partialEvalAnswer, 2).
10
+
11
+ table(pe, 3).
12
+
13
+ % Expression language: const/1, bool/1, var/1, add/2, mul/2, and if/3.
14
+ % Static environments are lists of bind(Name, ResidualValue) terms.
15
+ program(poly_y,
16
+ add(mul(var(x), var(y)), add(var(x), const(3))),
17
+ [bind(x, const(10))]).
18
+ program(static_branch,
19
+ if(bool(true), add(var(x), const(1)), mul(var(y), const(999))),
20
+ [bind(x, const(10))]).
21
+ program(dynamic_branch,
22
+ if(var(flag), add(var(x), const(1)), mul(var(y), const(2))),
23
+ [bind(x, const(10))]).
24
+
25
+ lookup(?name, [bind(?name, ?value) | ?], ?value).
26
+ lookup(?name, [bind(?, ?) | ?rest], ?value) :- lookup(?name, ?rest, ?value).
27
+
28
+ known_var(?env, ?name, ?value) :- lookup(?name, ?env, ?value).
29
+ unknown_var(?env, ?name) :- not(known_var(?env, ?name, ?)).
30
+
31
+ pe(?, const(?n), const(?n)).
32
+ pe(?, bool(?b), bool(?b)).
33
+ pe(?env, var(?name), ?value) :- known_var(?env, ?name, ?value).
34
+ pe(?env, var(?name), var(?name)) :- unknown_var(?env, ?name).
35
+
36
+ % Constant folding for arithmetic when both residual operands became constants.
37
+ pe(?env, add(?left, ?right), const(?sum)) :-
38
+ pe(?env, ?left, const(?a)),
39
+ pe(?env, ?right, const(?b)),
40
+ add(?a, ?b, ?sum).
41
+ pe(?env, mul(?left, ?right), const(?product)) :-
42
+ pe(?env, ?left, const(?a)),
43
+ pe(?env, ?right, const(?b)),
44
+ mul(?a, ?b, ?product).
45
+
46
+ % Residual arithmetic when at least one operand remains dynamic.
47
+ pe(?env, add(?left, ?right), add(?left_residual, ?right_residual)) :-
48
+ pe(?env, ?left, ?left_residual),
49
+ pe(?env, ?right, ?right_residual),
50
+ not((eq(?left_residual, const(?a)), eq(?right_residual, const(?b)))).
51
+ pe(?env, mul(?left, ?right), mul(?left_residual, ?right_residual)) :-
52
+ pe(?env, ?left, ?left_residual),
53
+ pe(?env, ?right, ?right_residual),
54
+ not((eq(?left_residual, const(?a)), eq(?right_residual, const(?b)))).
55
+
56
+ % Static conditionals choose a branch; dynamic conditionals keep both residual
57
+ % branches after specializing their contents.
58
+ pe(?env, if(?cond, ?then, ?else), ?residual) :-
59
+ pe(?env, ?cond, bool(true)),
60
+ pe(?env, ?then, ?residual).
61
+ pe(?env, if(?cond, ?then, ?else), ?residual) :-
62
+ pe(?env, ?cond, bool(false)),
63
+ pe(?env, ?else, ?residual).
64
+ pe(?env, if(?cond, ?then, ?else), if(?cond_residual, ?then_residual, ?else_residual)) :-
65
+ pe(?env, ?cond, ?cond_residual),
66
+ not(eq(?cond_residual, bool(true))),
67
+ not(eq(?cond_residual, bool(false))),
68
+ pe(?env, ?then, ?then_residual),
69
+ pe(?env, ?else, ?else_residual).
70
+
71
+ residual_program(?name, ?residual) :- program(?name, ?expr, ?env), pe(?env, ?expr, ?residual).
72
+
73
+ partialEvalAnswer(residual(?name), ?residual) :- residual_program(?name, ?residual).
74
+ partialEvalAnswer(note, "static inputs are folded while dynamic variables remain as residual code") :- residual_program(poly_y, ?).
@@ -0,0 +1,77 @@
1
+ % Register allocation as bounded graph coloring with spilling.
2
+ %
3
+ % Production allocators combine liveness analysis, interference graphs, register
4
+ % classes, coalescing, rematerialization, and spill-code insertion. This Eyelang
5
+ % example reduces the problem to its logical core: enumerate assignments of a
6
+ % few temporaries to two registers or memory, reject register conflicts, and use
7
+ % aggregate_min/5 to choose the cheapest spill plan.
8
+
9
+ materialize(registerAnswer, 2).
10
+
11
+ % Two physical registers are available. The synthetic place spill means the
12
+ % temporary is kept in memory instead of a register.
13
+ register(r1).
14
+ register(r2).
15
+ place(?reg) :- register(?reg).
16
+ place(spill).
17
+
18
+ % Temporaries and the cost of spilling each one. The triangle a-b-c cannot be
19
+ % colored with only two registers, so at least one of them must spill.
20
+ temporary(a, 10).
21
+ temporary(b, 1).
22
+ temporary(c, 10).
23
+ temporary(d, 2).
24
+
25
+ interferes(a, b).
26
+ interferes(b, c).
27
+ interferes(c, a).
28
+ interferes(c, d).
29
+
30
+ % A candidate allocation is an immutable list of bindings. This is deliberately
31
+ % brute force: three choices for each of four temporaries.
32
+ candidate_allocation([
33
+ bind(a, ?a_place),
34
+ bind(b, ?b_place),
35
+ bind(c, ?c_place),
36
+ bind(d, ?d_place)
37
+ ]) :-
38
+ place(?a_place),
39
+ place(?b_place),
40
+ place(?c_place),
41
+ place(?d_place).
42
+
43
+ assigned(?var, [[?var, ?place] | ?], ?place).
44
+ assigned(?var, [bind(?var, ?place) | ?], ?place).
45
+ assigned(?var, [bind(?, ?) | ?rest], ?place) :- assigned(?var, ?rest, ?place).
46
+
47
+ % A conflict exists only when both interfering temporaries choose the same real
48
+ % register. Spilled temporaries do not occupy registers.
49
+ allocation_conflict(?allocation) :-
50
+ interferes(?left, ?right),
51
+ assigned(?left, ?allocation, ?reg),
52
+ assigned(?right, ?allocation, ?reg),
53
+ register(?reg).
54
+
55
+ valid_allocation(?allocation) :-
56
+ candidate_allocation(?allocation),
57
+ not(allocation_conflict(?allocation)).
58
+
59
+ spill_cost_of_place(?var, spill, ?cost) :- temporary(?var, ?cost).
60
+ spill_cost_of_place(?var, ?reg, 0) :- temporary(?var, ?), register(?reg).
61
+
62
+ allocation_cost(?allocation, ?cost) :-
63
+ findall(?item_cost,
64
+ (member(bind(?var, ?place), ?allocation), spill_cost_of_place(?var, ?place, ?item_cost)),
65
+ ?costs),
66
+ sum_list(?costs, ?cost).
67
+
68
+ best_allocation(?allocation, ?cost) :-
69
+ aggregate_min([?candidate_cost, ?candidate], ?candidate,
70
+ (valid_allocation(?candidate), allocation_cost(?candidate, ?candidate_cost)),
71
+ [?cost, ?allocation], ?allocation).
72
+
73
+ registerAnswer(best_allocation, ?allocation) :- best_allocation(?allocation, ?).
74
+ registerAnswer(spill_cost, ?cost) :- best_allocation(?, ?cost).
75
+ registerAnswer(place(?var), ?place) :- best_allocation(?allocation, ?), assigned(?var, ?allocation, ?place).
76
+ registerAnswer(valid_allocation_count, ?count) :- countall(valid_allocation(?), ?count).
77
+ registerAnswer(note, "the cheapest solution spills b to color the a-b-c triangle with two registers") :- best_allocation(?, ?).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "1.7.14",
3
+ "version": "1.7.16",
4
4
  "description": "A small Prolog-like logic programming language for facts, rules, goals, answers, and proofs.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/playground.html CHANGED
@@ -459,6 +459,7 @@
459
459
  "canary-release",
460
460
  "cat-koko",
461
461
  "catalan-convolution",
462
+ "cdcl-sat-solver",
462
463
  "chart-parser",
463
464
  "clinical-trial-screening",
464
465
  "collatz-1000",
@@ -494,6 +495,7 @@
494
495
  "drone-corridor-planner",
495
496
  "easter-computus",
496
497
  "electrical-rc-filter",
498
+ "equality-saturation",
497
499
  "epidemic-policy",
498
500
  "equivalence-classes-overlap-implies-same-class",
499
501
  "eulerian-path",
@@ -531,6 +533,7 @@
531
533
  "intuitionistic-logic-kripke",
532
534
  "job-shop-scheduling",
533
535
  "knapsack-optimization",
536
+ "knuth-bendix-completion",
534
537
  "knowledge-engineering-alignment-flow",
535
538
  "language",
536
539
  "law-of-cosines",
@@ -558,6 +561,7 @@
558
561
  "odrl-dpv-risk-ranked",
559
562
  "orbital-transfer-design",
560
563
  "path-discovery",
564
+ "partial-evaluator",
561
565
  "peano-arithmetic",
562
566
  "peano-calculus",
563
567
  "peasant",
@@ -569,6 +573,7 @@
569
573
  "proof-contrapositive",
570
574
  "quadratic-formula",
571
575
  "radioactive-decay",
576
+ "register-allocation",
572
577
  "reusable-builtins",
573
578
  "riemann-hypothesis",
574
579
  "route-planning",