eyelang 1.5.0 → 1.5.2

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
@@ -239,10 +239,10 @@ Predicate names and atom constants use the same lexical form. Namespace-like nam
239
239
 
240
240
  The CLI is output-oriented and uses `materialize/2` to decide what to print. Embedders can still use the JavaScript API and `Solver` directly for arbitrary goals and arities.
241
241
 
242
- Add `--stats` when you want lightweight solver counters on stderr without changing stdout:
242
+ Add `-s` or `--stats` when you want lightweight solver counters on stderr without changing stdout:
243
243
 
244
244
  ```sh
245
- bin/eyelang --stats examples/sudoku.pl
245
+ bin/eyelang -s examples/sudoku.pl
246
246
  ```
247
247
 
248
248
  The playground has matching `--stats` and `--proof` checkboxes, so browser runs can show the same counters or explanations like the CLI.
@@ -362,6 +362,7 @@ The repository includes examples for recursion, graph reachability, finite searc
362
362
  | [`delfour.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/delfour.pl) | Derives shopping and authorization recommendations. | [`output/delfour.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/delfour.pl) |
363
363
  | [`dense-hamiltonian-cycle.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/dense-hamiltonian-cycle.pl) | Searches a dense Hamiltonian cycle with aggregate minimization. | [`output/dense-hamiltonian-cycle.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/dense-hamiltonian-cycle.pl) |
364
364
  | [`deontic-logic.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/deontic-logic.pl) | Reports obligations, prohibitions, and violations. | [`output/deontic-logic.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/deontic-logic.pl) |
365
+ | [`derived-backward-rule.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/derived-backward-rule.pl) | Derives an inverse-property backward rule from rule data. | [`output/derived-backward-rule.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/derived-backward-rule.pl) |
365
366
  | [`derived-rule.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/derived-rule.pl) | Derives conclusions from rule data. | [`output/derived-rule.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/derived-rule.pl) |
366
367
  | [`diamond-property.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/diamond-property.pl) | Checks the diamond property of a relation. | [`output/diamond-property.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/diamond-property.pl) |
367
368
  | [`dijkstra-findall-sort.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/dijkstra-findall-sort.pl) | Finds shortest paths using collected candidates. | [`output/dijkstra-findall-sort.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/dijkstra-findall-sort.pl) |
@@ -500,7 +501,7 @@ node bin/eyelang --help
500
501
  Useful profiling smoke test:
501
502
 
502
503
  ```sh
503
- bin/eyelang --stats examples/sudoku.pl > /dev/null
504
+ bin/eyelang -s examples/sudoku.pl > /dev/null
504
505
  ```
505
506
 
506
507
  For a release:
@@ -513,7 +514,7 @@ For a release:
513
514
 
514
515
  ## Performance notes
515
516
 
516
- Use `--stats` for a quick sanity check while optimizing solver changes. It prints counters such as `solve_goals_calls`, `unify_calls`, `deterministic_rule_expansions`, `candidate_lists_selected`, `clause_candidates_considered`, `clauses_tried`, `max_depth`, and `max_solver_call_depth` to stderr, leaving normal output stable for golden-file tests. The `max_solver_call_depth` counter is especially useful for browser regressions, where the VM call stack can be tighter than a command-line run.
517
+ Use `-s` or `--stats` for a quick sanity check while optimizing solver changes. It prints counters such as `solve_goals_calls`, `unify_calls`, `deterministic_rule_expansions`, `candidate_lists_selected`, `clause_candidates_considered`, `clauses_tried`, `max_depth`, and `max_solver_call_depth` to stderr, leaving normal output stable for golden-file tests. The `max_solver_call_depth` counter is especially useful for browser regressions, where the VM call stack can be tighter than a command-line run.
517
518
 
518
519
  eyelang hashes predicate groups by name and arity, then indexes clauses by scalar argument values. It also builds two-argument composite indexes for scalar pairs and probes those composite indexes without per-lookup heap allocation. This helps both large generated programs with many predicates and selective queries such as:
519
520
 
package/bin/eyelang CHANGED
File without changes
@@ -0,0 +1,27 @@
1
+ % Derived backward rule example adapted from Eyeling derived-backward-rule.n3.
2
+ %
3
+ % Eyeling source shape:
4
+ % parentOf invOf childOf.
5
+ % alice parentOf bob.
6
+ % { ?p invOf ?q. } => { { ?x ?q ?y. } <= { ?y ?p ?x. }. }.
7
+ % { ?x childOf ?y. } => { ?x hasParent ?y. }.
8
+ %
9
+ % The generated backward rule is represented as quoted formula data in
10
+ % log_impliedBy/2, then mirrored as an ordinary eyelang rule so the generated
11
+ % childOf relation can feed the ordinary hasParent rule.
12
+
13
+ materialize(log_impliedBy, 2).
14
+ materialize(childOf, 2).
15
+ materialize(hasParent, 2).
16
+
17
+ invOf(parentOf, childOf).
18
+ parentOf(alice, bob).
19
+
20
+ log_impliedBy(childOf(var(x), var(y)), parentOf(var(y), var(x))) :-
21
+ invOf(parentOf, childOf).
22
+
23
+ childOf(X, Y) :-
24
+ log_impliedBy(childOf(var(x), var(y)), parentOf(var(y), var(x))),
25
+ parentOf(Y, X).
26
+
27
+ hasParent(X, Y) :- childOf(X, Y).
@@ -0,0 +1,3 @@
1
+ log_impliedBy(childOf(var(x), var(y)), parentOf(var(y), var(x))).
2
+ childOf(bob, alice).
3
+ hasParent(bob, alice).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "type": "module",
5
5
  "description": "A small rule engine for Prolog-style Horn clauses",
6
6
  "keywords": [
package/playground.html CHANGED
@@ -466,6 +466,7 @@
466
466
  "delfour",
467
467
  "dense-hamiltonian-cycle",
468
468
  "deontic-logic",
469
+ "derived-backward-rule",
469
470
  "derived-rule",
470
471
  "diamond-property",
471
472
  "dijkstra",
package/src/cli.js CHANGED
@@ -34,7 +34,7 @@ export async function main(argv) {
34
34
  return;
35
35
  } else if (!endOptions && (arg === '--proof' || arg === '-p')) {
36
36
  options.proof = true;
37
- } else if (!endOptions && arg === '--stats') {
37
+ } else if (!endOptions && (arg === '--stats' || arg === '-s')) {
38
38
  options.stats = true;
39
39
  } else if (!endOptions && arg.startsWith('-') && arg !== '-') {
40
40
  throw new Error(`unknown option: ${arg}`);
@@ -142,7 +142,7 @@ Input:
142
142
  Options:
143
143
  -h, --help Show this help text and exit.
144
144
  -p, --proof Enable proof explanations.
145
- --stats Print solver statistics to stderr after execution.
145
+ -s, --stats Print solver statistics to stderr after execution.
146
146
  -v, --version Show the package version and exit.
147
147
  -- Stop option parsing; following arguments are treated as files.
148
148
  `);
@@ -171,7 +171,8 @@ why(
171
171
  const result = runCli([]);
172
172
  assertEqual(result.status, 0, 'exit status');
173
173
  assertIncludes(result.stdout, 'Usage:\n eyelang [options] [file-or-url.pl|- ...]', 'stdout');
174
- assertIncludes(result.stdout, '--proof', 'stdout');
174
+ assertIncludes(result.stdout, '-p, --proof', 'stdout');
175
+ assertIncludes(result.stdout, '-s, --stats', 'stdout');
175
176
  assertEqual(result.stderr, '', 'stderr');
176
177
  },
177
178
  },
@@ -225,6 +226,28 @@ why(
225
226
  },
226
227
 
227
228
 
229
+ {
230
+ name: '--stats prints solver statistics to stderr',
231
+ run: () => {
232
+ const result = runCli(['--stats', '-'], { input: 'p(a, b).\nq(X, Y) :- p(X, Y).\n' });
233
+ assertEqual(result.status, 0, 'exit status');
234
+ assertEqual(result.stdout, 'q(a, b).\n', 'stdout');
235
+ assertIncludes(result.stderr, 'eyelang stats:\n', 'stderr');
236
+ assertIncludes(result.stderr, ' solve_goals_calls:', 'stderr');
237
+ },
238
+ },
239
+ {
240
+ name: '-s prints solver statistics to stderr',
241
+ run: () => {
242
+ const result = runCli(['-s', '-'], { input: 'p(a, b).\nq(X, Y) :- p(X, Y).\n' });
243
+ assertEqual(result.status, 0, 'exit status');
244
+ assertEqual(result.stdout, 'q(a, b).\n', 'stdout');
245
+ assertIncludes(result.stderr, 'eyelang stats:\n', 'stderr');
246
+ assertIncludes(result.stderr, ' solve_goals_calls:', 'stderr');
247
+ },
248
+ },
249
+
250
+
228
251
  {
229
252
  name: 'double dash permits option-shaped file names',
230
253
  run: () => {