eyeling 1.22.11 → 1.22.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/HANDBOOK.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ## A compact Notation3 reasoner in JavaScript — a handbook
4
4
 
5
5
  > This handbook is written for a computer science student who wants to understand Eyeling as _code_ and as a _reasoning machine_.
6
- > It’s meant to be read linearly, but each chapter stands on its own.
6
+ > It is meant to be read linearly, but each chapter stands on its own.
7
7
 
8
8
  ## Contents
9
9
 
@@ -33,6 +33,8 @@
33
33
  - [Appendix F — The ARC approach: Answer • Reason Why • Check](#app-f)
34
34
  - [Appendix G — Eyeling and the W3C CG Notation3 Semantics](#app-g)
35
35
  - [Appendix H — Applied Constructor-Theory and the N3 ARC examples](#app-h)
36
+ - [Appendix I — The Eyeling Playground](#app-i)
37
+ - [Appendix J — Formalism Is Fine](#app-j)
36
38
 
37
39
  ---
38
40
 
@@ -47,7 +49,7 @@ Eyeling is a small Notation3 (N3) reasoner implemented in JavaScript. Its job is
47
49
 
48
50
  and compute consequences until nothing new follows.
49
51
 
50
- If you’ve seen Datalog or Prolog, the shape will feel familiar. Eyeling blends both:
52
+ If you have seen Datalog or Prolog, the shape will feel familiar. Eyeling blends both:
51
53
 
52
54
  - **Forward chaining** (like Datalog saturation) for `=>` rules.
53
55
  - **Backward chaining** (like Prolog goal solving) for `<=` rules _and_ for built-in predicates.
@@ -138,7 +140,7 @@ If you want to follow the code in the same order Eyeling “thinks”, read:
138
140
  10. `lib/cli.js` + `lib/entry.js` — command-line wiring and bundle entry exports.
139
141
  11. `index.js` — the npm API wrapper (spawns the bundled CLI synchronously).
140
142
 
141
- This is almost literally a tiny compiler pipeline:
143
+ This is very nearly a tiny compiler pipeline:
142
144
 
143
145
  ```
144
146
 
@@ -199,7 +201,7 @@ In addition, interned **Iri**/**Literal** terms (and generated **Blank** terms)
199
201
 
200
202
  For blanks, the id is derived from the blank label (so different blank labels remain different existentials).
201
203
 
202
- Terms are treated as immutable: once interned/created, the code assumes you won’t mutate `.value` (or `.label` for blanks).
204
+ Terms are treated as immutable: once interned/created, the code assumes you will not mutate `.value` (or `.label` for blanks).
203
205
 
204
206
  ### 3.4 Prefix environment
205
207
 
@@ -245,7 +247,7 @@ The parser supports:
245
247
  - keyword-ish sugar like `is ... of` and inverse arrows
246
248
  - path operators `!` and `^` that may generate helper triples via fresh blanks
247
249
 
248
- A nice detail: the parser maintains a `pendingTriples` list used when certain syntactic forms expand into helper triples (for example, some path/property-list expansions). It ensures the “surface statement” still emits all required triples even if the subject itself was syntactic sugar.
250
+ A useful detail: the parser maintains a `pendingTriples` list used when certain syntactic forms expand into helper triples (for example, some path/property-list expansions). It ensures the “surface statement” still emits all required triples even if the subject itself was syntactic sugar.
249
251
 
250
252
  ### 4.3 Parsing rules: `=>`, `<=`, and log idioms
251
253
 
@@ -286,7 +288,7 @@ Internally:
286
288
 
287
289
  ## Chapter 5 — Rule normalization: “compile-time” semantics (`lib/rules.js`)
288
290
 
289
- Before rules hit the engine, Eyeling performs one lightweight transformation. A second “make it work” trick—deferring built-ins that can’t run yet—happens later inside the goal prover.
291
+ Before rules hit the engine, Eyeling performs one lightweight transformation. A second “make it work” trick—deferring built-ins that cannot run yet—happens later inside the goal prover.
290
292
 
291
293
  ### 5.1 Lifting blank nodes in rule bodies into variables
292
294
 
@@ -308,9 +310,35 @@ This avoids the “existential in the body” trap and matches how most rule aut
308
310
 
309
311
  Blanks in the **conclusion** are _not_ lifted — they remain blanks and later become existentials (Chapter 9).
310
312
 
311
- ### 5.1.1 Quoted formulas keep their own blank-node scope
313
+ ### 5.1.1 Quoted formulas in rule bodies: direct pattern positions vs nested data positions
312
314
 
313
- There is one important exception to the “lift blanks in rule bodies” rule: **do not descend into a quoted formula** (`GraphTerm`) and lift the blanks that appear _inside_ it.
315
+ There is one important refinement to the “lift blanks in rule bodies” rule when a rule body mentions a quoted formula (`GraphTerm`).
316
+
317
+ Eyeling now distinguishes **direct quoted-formula positions** from **nested quoted-formula data**.
318
+
319
+ #### Direct quoted-formula positions in a premise triple
320
+
321
+ When a quoted formula appears **directly** as the subject, predicate, or object term of a premise triple, Eyeling treats blank nodes inside that quoted formula as **rule-body placeholders** and lifts them to rule variables.
322
+
323
+ Example:
324
+
325
+ ```n3
326
+ { :A :B :C } a :Statement.
327
+
328
+ {
329
+ { _:X :B :C } a :Statement.
330
+ } => {
331
+ :result :is true.
332
+ }.
333
+ ```
334
+
335
+ This matches and derives `:result :is true.` because the direct quoted formula `{ _:X :B :C }` is being used as a **pattern-bearing term** in the premise triple.
336
+
337
+ This behavior is mainly for interoperability with engines that treat blank nodes in such direct quoted-formula premise positions as pattern placeholders.
338
+
339
+ #### Nested quoted formulas remain data
340
+
341
+ If the quoted formula is nested **inside another term** in the rule body — for example inside a list used by `log:conjunction` — Eyeling preserves the quoted formula’s own blank-node scope.
314
342
 
315
343
  So this rule body:
316
344
 
@@ -320,24 +348,24 @@ So this rule body:
320
348
  } => { ... }.
321
349
  ```
322
350
 
323
- must keep the inner `[]` as a **formula-local blank node**. Eyeling should treat it as belonging to the quoted graph, not as a rule-body variable that escapes into the surrounding rule.
351
+ must keep the inner `[]` as a **formula-local blank node**. Eyeling treats it as belonging to the quoted graph, not as a rule-body variable that escapes into the surrounding rule.
324
352
 
325
- That distinction matters because quoted formulas play **two different roles** in Eyeling:
353
+ That distinction matters because quoted formulas still play **two different roles** in Eyeling:
326
354
 
327
- 1. **Formula as data** — for example when constructing a formula with `log:conjunction` or storing `{ ... }` in a triple. In this role, local blanks stay blanks. They print as blank nodes and participate in alpha-equivalence only within that quoted formula.
328
- 2. **Formula as a query pattern** — for example when `log:includes`, `log:notIncludes`, `log:collectAllIn`, or `log:forAllIn` prove a quoted formula. In that role, the builtin may treat the formula’s **local blanks existentially** while matching.
355
+ 1. **Formula as data** — for example when constructing a formula with `log:conjunction` or storing `{ ... }` inside another data term. In this role, local blanks stay blanks. They print as blank nodes and participate in alpha-equivalence only within that quoted formula.
356
+ 2. **Formula as a query pattern** — either through query-like builtins such as `log:includes`, `log:notIncludes`, `log:collectAllIn`, or `log:forAllIn`, or through a **direct quoted-formula premise position** as described above. In that role, the formula’s local blanks may be treated existentially while matching.
329
357
 
330
358
  The practical rule is:
331
359
 
332
- > **Rule normalization preserves blank-node scope inside quoted formulas; builtins may later interpret those preserved blanks as existential query placeholders when the formula is used as a pattern.**
360
+ > **Eyeling lifts blanks inside quoted formulas only when the quoted formula appears directly in a premise triple position. Nested quoted formulas remain scoped data unless a query-like builtin interprets them as patterns.**
333
361
 
334
- This separation is deliberate. It keeps `log:conjunction` and formula printing honest, while still allowing query-like builtins to match formulas containing local `[]` placeholders.
362
+ This keeps `log:conjunction` and formula printing honest, while still allowing direct quoted-formula premise patterns such as `{ _:X :B :C } a :Statement.` to match interoperably.
335
363
 
336
364
  ### 5.2 Builtin deferral in forward-rule bodies
337
365
 
338
- In a depth-first proof, the order of goals matters. Many built-ins only become informative once parts of the triple are **already instantiated** (for example comparisons, pattern tests, and other built-ins that don’t normally create bindings).
366
+ In a depth-first proof, the order of goals matters. Many built-ins only become informative once parts of the triple are **already instantiated** (for example comparisons, pattern tests, and other built-ins that do not normally create bindings).
339
367
 
340
- If such a builtin runs while its subject/object still contain variables or blanks, it may return **no solutions** (because it can’t decide yet) or only the **empty delta** (`{}`), even though it would succeed (or fail) once other goals have bound the needed values.
368
+ If such a builtin runs while its subject/object still contain variables or blanks, it may return **no solutions** (because it cannot decide yet) or only the **empty delta** (`{}`), even though it would succeed (or fail) once other goals have bound the needed values.
341
369
 
342
370
  Eyeling supports a runtime deferral mechanism inside `proveGoals(...)`, enabled only when proving the bodies of forward rules.
343
371
 
@@ -350,9 +378,9 @@ What happens when `proveGoals(..., { deferBuiltins: true })` sees a builtin goal
350
378
  - the goal list hasn’t already been rotated too many times,
351
379
  - then Eyeling **rotates that builtin goal to the end** of the current goal list and continues with the next goal first.
352
380
 
353
- A small counter (`deferCount`) caps how many rotations can happen (at most the length of the current goal list), so the prover can’t loop forever by endlessly “trying later”.
381
+ A small counter (`deferCount`) caps how many rotations can happen (at most the length of the current goal list), so the prover cannot loop forever by endlessly “trying later”.
354
382
 
355
- There is one extra guard for a small whitelist of built-ins that are considered satisfiable even when both subject and object are completely unbound (see `__builtinIsSatisfiableWhenFullyUnbound`). For these, if evaluation yields no deltas and there is nothing left to bind (either it is the last goal, or deferral has already been exhausted), Eyeling treats the builtin as a vacuous success (`[{}]`) so it doesn’t block the proof.
383
+ There is one extra guard for a small whitelist of built-ins that are considered satisfiable even when both subject and object are completely unbound (see `__builtinIsSatisfiableWhenFullyUnbound`). For these, if evaluation yields no deltas and there is nothing left to bind (either it is the last goal, or deferral has already been exhausted), Eyeling treats the builtin as a vacuous success (`[{}]`) so it does not block the proof.
356
384
 
357
385
  This is intentionally enabled for **forward-chaining rule bodies only**. Backward rules keep their normal left-to-right goal order, which can be important for termination on some programs.
358
386
 
@@ -398,7 +426,7 @@ Eyeling has ordinary structural equality (term-by-term) for most terms.
398
426
 
399
427
  But **quoted formulas** (`GraphTerm`) demand something stronger. Two formulas should match even if their internal blank/variable names differ, as long as the structure is the same.
400
428
 
401
- That’s alpha-equivalence:
429
+ That is alpha-equivalence:
402
430
 
403
431
  - `{ _:x :p ?y. }` should match `{ _:z :p ?w. }`
404
432
 
@@ -408,14 +436,14 @@ Important scope nuance: only blanks/variables that are local to the quoted formu
408
436
 
409
437
  So `{ _:x :p :o }` obtained by substituting `?A = _:x` into `{ ?A :p :o }` must not alpha-match `{ _:b :p :o }` by renaming `_:x` to `_:b`.
410
438
 
411
- ### 6.2 Groundness: “variables inside formulas don’t leak”
439
+ ### 6.2 Groundness: “variables inside formulas do not leak”
412
440
 
413
441
  Eyeling makes a deliberate choice about _groundness_:
414
442
 
415
443
  - a triple is “ground” if it has no free variables in normal positions
416
444
  - **variables inside a `GraphTerm` do not make the surrounding triple non-ground**
417
445
 
418
- This is encoded in functions like `isGroundTermInGraph`. It’s what makes it possible to assert and store triples that _mention formulas with variables_ as data.
446
+ This is encoded in functions like `isGroundTermInGraph`. It is what makes it possible to assert and store triples that _mention formulas with variables_ as data.
419
447
 
420
448
  ### 6.3 Substitutions: chaining and application
421
449
 
@@ -466,8 +494,8 @@ Unification is implemented in `unifyTerm` / `unifyTriple`, with support for:
466
494
 
467
495
  There are two key traits of Eyeling’s graph unification:
468
496
 
469
- 1. It’s _set-like_: order doesn’t matter.
470
- 2. It’s _substitution-threaded_: choices made while matching one triple restrict the remaining matches, just like Prolog.
497
+ 1. It is _set-like_: order does not matter.
498
+ 2. It is _substitution-threaded_: choices made while matching one triple restrict the remaining matches, just like Prolog.
471
499
 
472
500
  ### 6.5 Literals: lexical vs semantic equality
473
501
 
@@ -568,7 +596,7 @@ for delta in deltas:
568
596
  undoTo(mark)
569
597
  ```
570
598
 
571
- **Implementation note (performance):** in the core DFS, Eyeling applies builtin (and unification) deltas into a single mutable substitution and uses a **trail** to undo bindings on backtracking. This preserves the meaning of “threading substitutions through a proof”, but avoids allocating and copying full substitution objects on every branch. Empty deltas (`{}`) are genuinely cheap: they don’t touch the trail and only incur the control-flow overhead of exploring a branch.
599
+ **Implementation note (performance):** in the core DFS, Eyeling applies builtin (and unification) deltas into a single mutable substitution and uses a **trail** to undo bindings on backtracking. This preserves the meaning of “threading substitutions through a proof”, but avoids allocating and copying full substitution objects on every branch. Empty deltas (`{}`) are genuinely cheap: they do not touch the trail and only incur the control-flow overhead of exploring a branch.
572
600
 
573
601
  **Implementation note (performance):** as of this version, Eyeling also avoids allocating short-lived substitution objects when matching goals against **facts** and when unifying a **backward-rule head** with the current goal. Instead of calling the pure `unifyTriple(..., subst)` (which clones the substitution on each variable bind), the prover performs an **in-place unification** directly into the mutable `substMut` store and records only the newly-bound variable names on the trail. This typically reduces GC pressure significantly on reachability / path-search workloads, where unification is executed extremely frequently.
574
602
 
@@ -576,9 +604,9 @@ So built-ins behave like relations that can generate zero, one, or many possible
576
604
 
577
605
  #### 8.3.1 Builtin deferral and “vacuous” solutions
578
606
 
579
- Conjunction in N3 is order-insensitive, but many builtins are only useful once some variables are bound by _other_ goals in the same body. When `proveGoals` is called from forward chaining, Eyeling enables **builtin deferral**: if a builtin goal can’t make progress yet, it is rotated to the end of the goal list and retried later (with a small cycle guard to avoid infinite rotation).
607
+ Conjunction in N3 is order-insensitive, but many builtins are only useful once some variables are bound by _other_ goals in the same body. When `proveGoals` is called from forward chaining, Eyeling enables **builtin deferral**: if a builtin goal cannot make progress yet, it is rotated to the end of the goal list and retried later (with a small cycle guard to avoid infinite rotation).
580
608
 
581
- Can’t make progress” includes both cases:
609
+ Cannot make progress” includes both cases:
582
610
 
583
611
  - the builtin returns **no solutions** (`[]`), and
584
612
  - the builtin returns only **vacuous solutions** (`[{}]`, i.e., success with _no new bindings_) while the goal still contains unbound vars/blanks.
@@ -715,7 +743,7 @@ Eyeling handles this by replacing head blank labels with fresh blank labels of t
715
743
 
716
744
  - `_:sk_0`, `_:sk_1`, …
717
745
 
718
- But it does something subtle and important: it caches skolemization per (rule firing, head blank label), so that the _same_ firing instance doesn’t keep generating new blanks across outer iterations.
746
+ But it does something subtle and important: it caches skolemization per (rule firing, head blank label), so that the _same_ firing instance does not keep generating new blanks across outer iterations.
719
747
 
720
748
  The “firing instance” is keyed by a deterministic string derived from the instantiated body (“firingKey”). This stabilizes the closure and prevents “existential churn.”
721
749
 
@@ -802,7 +830,7 @@ Some built-ins interpret a positive integer literal as a requested priority:
802
830
 
803
831
  If a rule requests priority `N`, Eyeling delays that builtin until `scopedClosureLevel >= N`.
804
832
 
805
- In practice this allows rule authors to write “don’t run this scoped query until the closure is stable enough” and is what lets Eyeling iterate safely when rule-producing rules introduce new needs.
833
+ In practice this allows rule authors to write “do not run this scoped query until the closure is stable enough” and is what lets Eyeling iterate safely when rule-producing rules introduce new needs.
806
834
 
807
835
  ### 10.3 `log:conclusion`: local deductive closure of a formula
808
836
 
@@ -813,7 +841,7 @@ In practice this allows rule authors to write “don’t run this scoped query u
813
841
  - extract rule triples inside it (`log:implies`, `log:impliedBy`)
814
842
  - run `forwardChain` locally over those triples
815
843
 
816
- - cache the result in a `WeakMap` so the same formula doesn’t get recomputed
844
+ - cache the result in a `WeakMap` so the same formula does not get recomputed
817
845
 
818
846
  Notably, `log:impliedBy` inside the formula is treated as forward implication too for closure computation (and also indexed as backward to help proving).
819
847
 
@@ -881,7 +909,7 @@ The N3 Builtins tradition often describes builtins using “schema” annotation
881
909
  Eyeling is a little more pragmatic: it implements the spirit of these schemas, but it also has several “engineering” conventions that appear across many builtins:
882
910
 
883
911
  1. **Variables (`?X`) may be bound** by a builtin if the builtin is written to do so.
884
- 2. **Blank nodes (`[]` / `_:`)** are frequently treated as “don’t care” placeholders. Many builtins accept a blank node in an output position and simply succeed without binding.
912
+ 2. **Blank nodes (`[]` / `_:`)** are frequently treated as “do not care” placeholders. Many builtins accept a blank node in an output position and simply succeed without binding.
885
913
  3. **Fully unbound relations are usually not enumerated.** If both sides are unbound and enumerating solutions would be infinite (or huge), a number of builtins treat that situation as “satisfiable” and succeed once without binding anything. (This is mainly to keep meta-tests and some N3 conformance cases happy.)
886
914
 
887
915
  With that, we can tour the builtin families as Eyeling actually implements them.
@@ -1005,7 +1033,7 @@ Eyeling supports:
1005
1033
  3. **DateTime minus duration**: `(dateTime durationOrSeconds) math:difference dateTime`
1006
1034
  - Subtracts a duration from a dateTime and yields a new dateTime.
1007
1035
 
1008
- If the types don’t fit any supported case, the builtin fails.
1036
+ If the types do not fit any supported case, the builtin fails.
1009
1037
 
1010
1038
  #### `math:quotient`
1011
1039
 
@@ -1061,7 +1089,7 @@ The **BigInt exact-integer mode** exists specifically to avoid rule-level “rep
1061
1089
 
1062
1090
  #### Unary “math relations” (often invertible)
1063
1091
 
1064
- Eyeling implements these as a shared pattern: if the subject is numeric, compute object; else if the object is numeric, compute subject via an inverse function; if both sides are unbound, succeed once (don’t enumerate).
1092
+ Eyeling implements these as a shared pattern: if the subject is numeric, compute object; else if the object is numeric, compute subject via an inverse function; if both sides are unbound, succeed once (do not enumerate).
1065
1093
 
1066
1094
  - `math:absoluteValue`
1067
1095
  - `math:negation`
@@ -1123,7 +1151,7 @@ Binds `?now` to the current local time as an `xsd:dateTime` literal.
1123
1151
 
1124
1152
  Two subtle but important engineering choices:
1125
1153
 
1126
- 1. Eyeling memoizes “now” per reasoning run so that repeated uses in one run don’t drift.
1154
+ 1. Eyeling memoizes “now” per reasoning run so that repeated uses in one run do not drift.
1127
1155
  2. Eyeling supports a fixed “now” override (used for deterministic tests).
1128
1156
 
1129
1157
  ---
@@ -1252,7 +1280,7 @@ Reversible in the sense that either side may be the list:
1252
1280
  - If subject is a list, object becomes its reversal.
1253
1281
  - If object is a list, subject becomes its reversal.
1254
1282
 
1255
- It does not enumerate arbitrary reversals; it’s a deterministic transform once one side is known.
1283
+ It does not enumerate arbitrary reversals; it is a deterministic transform once one side is known.
1256
1284
 
1257
1285
  #### `list:remove`
1258
1286
 
@@ -1270,7 +1298,7 @@ Succeeds iff the object cannot be unified with any element of the subject list.
1270
1298
 
1271
1299
  #### `list:append`
1272
1300
 
1273
- This is list concatenation, but Eyeling implements it in a pleasantly relational way.
1301
+ This is list concatenation, but Eyeling implements it in a usefully relational way.
1274
1302
 
1275
1303
  **Forward shape:** `( (a b) (c) (d e) ) list:append (a b c d e)`
1276
1304
 
@@ -1376,7 +1404,7 @@ These builtins reach outside the current fact set. They are synchronous by desig
1376
1404
 
1377
1405
  Dereferences and parses the remote/local resource as N3/Turtle-like syntax, returning a formula.
1378
1406
 
1379
- A nice detail: top-level rules in the parsed document are represented _as data_ inside the returned formula using `log:implies` / `log:impliedBy` triples between formula terms. This means you can treat “a document plus its rules” as a single first-class formula object.
1407
+ A useful detail: top-level rules in the parsed document are represented _as data_ inside the returned formula using `log:implies` / `log:impliedBy` triples between formula terms. This means you can treat “a document plus its rules” as a single first-class formula object.
1380
1408
 
1381
1409
  #### `log:semanticsOrError`
1382
1410
 
@@ -1498,7 +1526,7 @@ Eyeling has **two modes**:
1498
1526
 
1499
1527
  2. **Priority-gated global scope**: otherwise
1500
1528
  - Eyeling uses a _frozen snapshot_ of the current global closure.
1501
- - The “priority” is read from the subject if it’s a positive integer literal `N`.
1529
+ - The “priority” is read from the subject if it is a positive integer literal `N`.
1502
1530
  - If the closure level is below `N`, the builtin “delays” by failing at that point in the search.
1503
1531
 
1504
1532
  This priority mechanism exists because Eyeling’s forward chaining runs in outer iterations with a “freeze snapshot then evaluate scoped builtins” phase. The goal is to make scoped meta-builtins stable and deterministic: they query a fixed snapshot rather than chasing a fact store that is being mutated mid-iteration.
@@ -1564,7 +1592,7 @@ Bidirectional conversion between IRIs and their string form:
1564
1592
 
1565
1593
  - If subject is an IRI, object can be unified with a string literal of its IRI.
1566
1594
  - If object is a string literal, subject can be unified with the corresponding IRI — **but** Eyeling rejects strings that cannot be safely serialized as `<...>` in Turtle/N3, and it rejects `_:`-style strings to avoid confusing blank nodes with IRIs.
1567
- - Some “fully unbound / don’t-care” combinations succeed once to avoid infinite enumeration.
1595
+ - Some “fully unbound / do not-care” combinations succeed once to avoid infinite enumeration.
1568
1596
 
1569
1597
  ### Side effects and output directives
1570
1598
 
@@ -1587,7 +1615,7 @@ As a goal, this builtin simply checks that the terms are sufficiently bound/usab
1587
1615
  - When the final closure contains any `log:outputString` triples, the CLI collects all of them from the _saturated_ closure and renders those strings instead of the default N3 output.
1588
1616
  - It sorts them deterministically by the subject “key” and concatenates the string values in that order.
1589
1617
 
1590
- This is a pure test/side-effect marker (it shouldn’t drive search; it should merely validate that strings exist once other reasoning has produced them). In forward rules Eyeling may defer it if it is reached before the terms are usable.
1618
+ This is a pure test/side-effect marker (it should not drive search; it should merely validate that strings exist once other reasoning has produced them). In forward rules Eyeling may defer it if it is reached before the terms are usable.
1591
1619
 
1592
1620
  ---
1593
1621
 
@@ -1762,7 +1790,7 @@ Dereferencing is cached by IRI-without-fragment (fragments are stripped). There
1762
1790
  - parsed semantics (GraphTerm)
1763
1791
  - semantics-or-error
1764
1792
 
1765
- This is both a performance and a stability feature: repeated `log:semantics` calls in backward proofs won’t keep refetching.
1793
+ This is both a performance and a stability feature: repeated `log:semantics` calls in backward proofs will not keep refetching.
1766
1794
 
1767
1795
  ### 12.3 HTTPS enforcement
1768
1796
 
@@ -1795,7 +1823,7 @@ When enabled, Eyeling prints a compact comment block per derived triple:
1795
1823
  - the instantiated rule body that was provable
1796
1824
  - the schematic forward rule that produced it
1797
1825
 
1798
- It’s a “why this triple holds” explanation, not a globally exported proof graph.
1826
+ It is a “why this triple holds” explanation, not a globally exported proof graph.
1799
1827
 
1800
1828
  Implementation note: the engine records lightweight `DerivedFact` objects during forward chaining, and `lib/explain.js` (via `makeExplain(...)`) is responsible for turning those objects into the human-readable proof comment blocks.
1801
1829
 
@@ -2128,7 +2156,7 @@ What Eyeling does:
2128
2156
 
2129
2157
  6. The triple is ground and not already present, so it is added and (optionally) printed.
2130
2158
 
2131
- That’s the whole engine in miniature: unify, compose substitutions, emit head triples.
2159
+ That is the whole engine in miniature: unify, compose substitutions, emit head triples.
2132
2160
 
2133
2161
  ---
2134
2162
 
@@ -2658,7 +2686,7 @@ Example fuse:
2658
2686
  } => false.
2659
2687
  ```
2660
2688
 
2661
- If you don’t want “stop the world”, derive a `:Violation` fact instead, and keep going.
2689
+ If you do not want “stop the world”, derive a `:Violation` fact instead, and keep going.
2662
2690
 
2663
2691
  ### 3) Make the workflow test-driven (golden closures)
2664
2692
 
@@ -2672,7 +2700,7 @@ This turns rule edits into a normal change-management loop: diffs are explicit,
2672
2700
 
2673
2701
  ### 4) Use proofs/traces as the input to the LLM, not the other way around
2674
2702
 
2675
- If you want a natural-language explanation, don’t ask the model to “explain the rules from memory”. Instead:
2703
+ If you want a natural-language explanation, do not ask the model to “explain the rules from memory”. Instead:
2676
2704
 
2677
2705
  1. Run Eyeling with proof/trace enabled (Eyeling has explicit tracing hooks and proof-comment support in its output pipeline).
2678
2706
  2. Give the LLM the **derived triples + proof comments** and ask it to summarize:
@@ -2705,7 +2733,7 @@ A simple structure that keeps the LLM honest:
2705
2733
  - “Include at least N minimal tests as facts in a separate block/file.”
2706
2734
  - “If something is unknown, emit a placeholder fact (`:needsFact`) rather than guessing.”
2707
2735
 
2708
- The point isn’t that the LLM is “right”; it’s that **Eyeling makes the result checkable**, and the artifact becomes a maintainable program rather than a one-off generation.
2736
+ The point is not that the LLM is “right”; it is that **Eyeling makes the result checkable**, and the artifact becomes a maintainable program rather than a one-off generation.
2709
2737
 
2710
2738
  ---
2711
2739
 
@@ -3189,8 +3217,8 @@ The following examples are especially useful if you want to see Eyeling files th
3189
3217
  - [`examples/act-gravity-mediator-witness.n3`](examples/act-gravity-mediator-witness.n3) · [`examples/output/act-gravity-mediator-witness.txt`](examples/output/act-gravity-mediator-witness.txt) — applied constructor-theory witness showing that, under locality and interoperability, entanglement mediated only by gravity implies a non-classical gravitational mediator.
3190
3218
  - ['examples/act-yeast-self-reproduction.n3'](examples/act-yeast-self-reproduction.n3) · ['examples/output/act-yeast-self-reproduction.txt'](examples/output/act-yeast-self-reproduction.txt) — applied constructor-theory example of a yeast starter culture showing replicator, vehicle, self-reproduction, heritable variation, and natural selection under no-design laws.
3191
3219
  - ['examples/act-barley-seed-lineage.n3'](examples/act-barley-seed-lineage.n3) · ['examples/output/act-barley-seed-lineage.txt'](examples/output/act-barley-seed-lineage.txt) — applied constructor-theory ARC case showing both possible and impossible lineage tasks under no-design laws, including blocked reproduction, dormancy, and evolvability when key ingredients are missing.
3192
- - ['examples/act-tunnel-junction-wake-switch.n3'](examples/act-tunnel-junction-wake-switch.n3) · ['examples/output/act-tunnel-junction-wake-switch.txt'](examples/output/act-tunnel-junction-wake-switch.txt) — applied constructor-theory ARC case comparing a tunnel-junction wake switch with a conventional PN junction via explicit can/can’t rules for tunneling, sub-threshold current, negative differential response, and low-bias switching.
3193
- - ['examples/act-photosynthetic-exciton-transfer.n3'](examples/act-photosynthetic-exciton-transfer.n3) · ['examples/output/act-photosynthetic-exciton-transfer.txt'](examples/output/act-photosynthetic-exciton-transfer.txt) — applied constructor-theory ARC case for quantum-assisted exciton transfer in a photosynthetic antenna, contrasting a tuned complex with a detuned one via explicit can/can’t rules.
3220
+ - ['examples/act-tunnel-junction-wake-switch.n3'](examples/act-tunnel-junction-wake-switch.n3) · ['examples/output/act-tunnel-junction-wake-switch.txt'](examples/output/act-tunnel-junction-wake-switch.txt) — applied constructor-theory ARC case comparing a tunnel-junction wake switch with a conventional PN junction via explicit can/cannot rules for tunneling, sub-threshold current, negative differential response, and low-bias switching.
3221
+ - ['examples/act-photosynthetic-exciton-transfer.n3'](examples/act-photosynthetic-exciton-transfer.n3) · ['examples/output/act-photosynthetic-exciton-transfer.txt'](examples/output/act-photosynthetic-exciton-transfer.txt) — applied constructor-theory ARC case for quantum-assisted exciton transfer in a photosynthetic antenna, contrasting a tuned complex with a detuned one via explicit can/cannot rules.
3194
3222
  - ['examples/act-sensor-memory-reset.n3'](examples/act-sensor-memory-reset.n3) · ['examples/output/act-sensor-memory-reset.txt'](examples/output/act-sensor-memory-reset.txt) — applied constructor-theory ARC case showing that a sensor memory reset is possible with a work medium but not with heat alone, highlighting work/heat distinction and irreversibility.
3195
3223
 
3196
3224
  #### Deep-classification stress tests
@@ -3335,7 +3363,7 @@ and just as naturally:
3335
3363
  { ?system :lacks ?property . } => { ?system :cannot ?task . } .
3336
3364
  ```
3337
3365
 
3338
- That is already close to the “science of can and can’t” idiom.
3366
+ That is already close to the “science of can and cannot” idiom.
3339
3367
 
3340
3368
  Second, N3 can keep the explanation close to the answer. The conditions, the derived `:can` / `:cannot` facts, and the final human-readable report can all live in one file.
3341
3369
 
@@ -3421,8 +3449,7 @@ The better ACT examples are heavily commented. The comments should say not just
3421
3449
 
3422
3450
  #### H.5.6 Editorial conventions for ACT files
3423
3451
 
3424
- For this repository, the ACT examples should stay visibly **Eyeling-native**.
3425
- They should read as compact N3 task-logic models rather than as a second language layer.
3452
+ For this repository, the ACT examples should stay visibly **Eyeling-native**. They should read as compact N3 task-logic models rather than as a second language layer.
3426
3453
 
3427
3454
  A good default order is:
3428
3455
 
@@ -3434,7 +3461,6 @@ A good default order is:
3434
3461
 
3435
3462
  The ARC report should make the decisive contrast explicit: what task is possible, what task is impossible, and which missing ingredient or witness explains the contrast.
3436
3463
 
3437
-
3438
3464
  - interoperability
3439
3465
  - locality
3440
3466
  - no-cloning
@@ -3521,3 +3547,134 @@ That is valuable even for readers who do not plan to work on constructor theory
3521
3547
  > some scientific explanations are best understood not as “what happened once,” but as “what could be made to happen, what could not, and what structural features make the difference.”
3522
3548
 
3523
3549
  That is exactly the sort of explanation that N3, and Eyeling in particular, can make unusually clear.
3550
+
3551
+ <a id="app-i"></a>
3552
+
3553
+ ## Appendix I — The Eyeling Playground
3554
+
3555
+ The **Eyeling Playground** is the browser-based front end for experimenting with Eyeling without a local install or command-line workflow. It is meant for teaching, quick debugging, live demos, and shareable reasoning examples. Rather than treating reasoning as an offline batch process, the playground makes it interactive: users can edit N3 directly in the browser, load remote N3 from a URL, run reasoning, inspect streamed output, and share the current state through a link.
3556
+
3557
+ This appendix explains what the playground is for, how it is structured, and why it matters in practice.
3558
+
3559
+ ### I.1 Why the playground exists
3560
+
3561
+ Notation3 is expressive, compact, and unusually good at mixing RDF-style data with rules, but the first contact experience can still be awkward for many users. Command-line tools are powerful, but they are not always the best entry point for small experiments, teaching sessions, or public demonstrations.
3562
+
3563
+ The playground exists to lower that initial friction. It lets a user:
3564
+
3565
+ - open a page,
3566
+ - edit or paste a small N3 program,
3567
+ - run reasoning immediately,
3568
+ - inspect output and errors in place,
3569
+ - and share the exact setup with a URL.
3570
+
3571
+ That makes the playground useful not only for newcomers, but also for experienced users who want a fast feedback loop for small examples.
3572
+
3573
+ ### I.2 Core interaction model
3574
+
3575
+ At the center of the playground is an **editable N3 program**. This is the main authoring area for facts, rules, and output-oriented directives.
3576
+
3577
+ Alongside that editor is a **Load from URL** field. A remote N3 document can be fetched directly into the playground, which makes it easy to reuse examples stored in a repository or a raw hosted file.
3578
+
3579
+ A key recent addition is **background knowledge mode**. When enabled, the N3 loaded from a URL is not written into the editor. Instead, it is stored separately as background knowledge and merged with the editable program only when reasoning runs. This supports a very common workflow:
3580
+
3581
+ - keep a stable imported dataset or rule base,
3582
+ - keep the local editor small and focused,
3583
+ - iterate on local rules, queries, or reporting logic without repeatedly copying the larger imported source.
3584
+
3585
+ That separation is helpful both pedagogically and practically. It mirrors real reasoning work, where a user often reasons _over_ a fixed body of data rather than constantly rewriting it.
3586
+
3587
+ ### I.3 Execution behavior
3588
+
3589
+ The playground is designed to feel responsive even when reasoning is not trivial. To do that, it uses a browser execution model that can run inference in a worker rather than blocking the main UI thread. Output is then surfaced back into the page.
3590
+
3591
+ The user-facing controls support three main actions:
3592
+
3593
+ - **Run reasoning**,
3594
+ - **Pause/Resume**,
3595
+ - **Stop**.
3596
+
3597
+ This matters because the playground is not just a text box plus a submit button. It treats reasoning as a process that can be observed while it happens.
3598
+
3599
+ The output behavior also adapts to the kind of N3 program being run. In some cases the natural result is a streamed list of derived triples. In others, such as programs using output-oriented constructs like `log:outputString`, a rendered text result is more appropriate. The playground supports both styles.
3600
+
3601
+ ### I.4 Error handling and explainability
3602
+
3603
+ For an interactive reasoning environment, error behavior matters almost as much as successful output. The playground therefore gives particular attention to syntax and runtime feedback.
3604
+
3605
+ When an N3 syntax error occurs, the output pane shows the error with line and column information, and the editor highlights the offending line. This shortens the distance between the parser’s complaint and the place where the user needs to fix the program.
3606
+
3607
+ The playground also exposes two configuration toggles that are especially useful for explanation and browser safety:
3608
+
3609
+ - **proof comments**, which make reasoning output more explanatory,
3610
+ - **HTTPS dereferencing enforcement**, which helps avoid mixed-content problems when dereferencing from the browser.
3611
+
3612
+ Together these choices make the playground better suited to live explanation, teaching, and debugging than a minimal browser wrapper would be.
3613
+
3614
+ ### I.5 Shareable state through URLs
3615
+
3616
+ One of the most practical features of the playground is that its state can be encoded in the page URL.
3617
+
3618
+ The canonical query parameters are:
3619
+
3620
+ - `edit` — sets the editor content,
3621
+ - `url` — fills the URL field,
3622
+ - `loadbg` — determines whether the URL should be loaded as background knowledge,
3623
+ - `proofcomments` — initializes the proof-comments checkbox,
3624
+ - `httpsderef` — initializes the HTTPS dereferencing checkbox.
3625
+
3626
+ This makes the playground particularly strong for tutorials and demos. A link can specify not just a program, but a whole configuration: an imported resource, whether it belongs in background knowledge, a small editable overlay, and the relevant runtime toggles.
3627
+
3628
+ Older hash-based links are still accepted as a fallback, but new state updates are written using query parameters because they scale better as the UI grows beyond a single editor field.
3629
+
3630
+ ### I.6 What the playground is good for
3631
+
3632
+ The playground is especially valuable in four settings.
3633
+
3634
+ #### I.6.1 Teaching
3635
+
3636
+ Students can begin with a small example and see what changes immediately when they edit a fact or rule. This is a much more direct way to learn N3 than starting from installation instructions.
3637
+
3638
+ #### I.6.2 Live demos
3639
+
3640
+ A presenter can preload a scenario, show a compact local rule set, run inference, and then share a reproducible link afterward. Background knowledge mode is particularly helpful here because it keeps the visible editor small while still grounding the run in a richer imported source.
3641
+
3642
+ #### I.6.3 Debugging small programs
3643
+
3644
+ For short reasoning tasks, the playground can be a faster debugging surface than a command-line loop. It is well suited to checking syntax, validating a rule pattern, or inspecting a small proof-oriented run.
3645
+
3646
+ #### I.6.4 Sharing examples
3647
+
3648
+ A single link can capture enough context for another person to reproduce an example quickly. This is valuable in issue reports, discussions, teaching material, and public-facing demonstrations.
3649
+
3650
+ ### I.7 Limits of the playground
3651
+
3652
+ The playground is intentionally lightweight, and it should be understood in that role.
3653
+
3654
+ It is not meant to replace the command line for large-scale workloads, benchmarking, or repository-scale automation. Browser memory and execution limits still matter. Likewise, loading remote resources depends on ordinary web constraints such as network access and cross-origin availability.
3655
+
3656
+ In short: the playground is best thought of as a compact interactive front end for exploration, communication, and small-to-medium experiments.
3657
+
3658
+ ### I.8 Why it matters
3659
+
3660
+ The Eyeling Playground shows that N3 reasoning can be made substantially more approachable without flattening the underlying logic into a toy interface. A relatively small set of features — an editor, a URL loader, background knowledge mode, responsive execution, proof toggles, and shareable query parameters — is enough to support serious educational and exploratory work.
3661
+
3662
+ That is the main value of the playground. It gives Eyeling a public-facing, browser-native environment where reasoning is not hidden behind setup overhead, and where examples can move easily between author, teacher, student, and reviewer.
3663
+
3664
+ <a id="app-j"></a>
3665
+
3666
+ ## Appendix J — Formalism Is Fine
3667
+
3668
+ For Eyeling, formal methods are not an obstacle to practical reasoning. They are part of what makes the system useful. A reasoner is easier to trust when its facts, rules, derivations, and limits can be stated explicitly rather than hidden in application code. That is the sense in which formalism matters here: not as ceremony, but as a way of keeping the behavior of the system inspectable.
3669
+
3670
+ Horn logic is fine because it gives a disciplined core. It does not try to express every possible form of reasoning. Instead, it offers a fragment that is small enough to implement clearly and strong enough to support a wide range of real tasks. That trade is often a good one. In a compact reasoner, expressiveness only helps when it does not destroy clarity or operational control.
3671
+
3672
+ Notation3 is fine because a logic language also needs a readable surface. Eyeling works with terms, triples, formulas, and rules, but those structures still have to be written, reviewed, debugged, and shared. N3 matters because it keeps the logic close to the page. A rule still looks like something a person can follow. A quoted formula still looks like a graph that can be inspected. That readability is part of what makes the reasoner teachable and portable.
3673
+
3674
+ Executable specification is fine because there is real value in keeping semantics and implementation close together. When a specification can be run, it becomes easier to test the intended behavior on concrete inputs, compare outcomes across examples, and find the points where an abstract account is still too vague. Execution does not replace semantics, but it is often the best way to expose whether the semantics is precise enough to guide an implementation.
3675
+
3676
+ Herbrand semantics is fine because it gives symbolic reasoning a concrete semantic basis. Instead of beginning with an opaque external domain, it begins with the symbolic constructions themselves and asks what follows from them under the rules. That is a natural fit for Eyeling. The engine reasons over terms, substitutions, triples, formulas, and proof states. Herbrand-style semantics therefore does not feel like an imported philosophical story. It describes the level at which the system actually works.
3677
+
3678
+ Gödel incompleteness is fine because the limits of formal systems are not a refutation of formal reasoning. They are part of its shape. Once a system becomes expressive enough, one should expect structural limits on what it can prove about itself. That does not make formal methods less serious. It shows that their boundaries are principled rather than accidental. For a handbook like this one, that is the right lesson: formal systems are valuable not because they say everything, but because they say some things clearly, explicitly, and in a form that can be checked.
3679
+
3680
+ Taken together, these positions support a straightforward attitude toward Eyeling. Horn logic is fine. Notation3 is fine. Executable specification is fine. Herbrand semantics is fine. Gödel incompleteness is fine. None of these commitments make the reasoner narrower in a harmful sense. They make it clearer, easier to inspect, and easier to trust. For this project, that is enough.
@@ -13039,15 +13039,39 @@ ${triples.map((tr) => ` ${tripleToN3(tr, prefixes)}`).join('\n')}
13039
13039
  return t;
13040
13040
  }
13041
13041
 
13042
- function convertTerm(t) {
13042
+ function convertQuotedPatternTerm(t) {
13043
13043
  if (t instanceof Blank) return blankToVar(t.label);
13044
- if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertTerm));
13045
- if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertTerm), t.tailVar);
13046
- if (t instanceof GraphTerm) return copyQuotedTerm(t);
13044
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertQuotedPatternTerm));
13045
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertQuotedPatternTerm), t.tailVar);
13046
+ if (t instanceof GraphTerm) {
13047
+ const triples = t.triples.map(
13048
+ (tr) =>
13049
+ new Triple(
13050
+ convertQuotedPatternTerm(tr.s),
13051
+ convertQuotedPatternTerm(tr.p),
13052
+ convertQuotedPatternTerm(tr.o),
13053
+ ),
13054
+ );
13055
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
13056
+ }
13047
13057
  return t;
13048
13058
  }
13049
13059
 
13050
- const newPremise = premise.map((tr) => new Triple(convertTerm(tr.s), convertTerm(tr.p), convertTerm(tr.o)));
13060
+ function convertTerm(t, allowDirectQuotedPattern = false) {
13061
+ if (t instanceof Blank) return blankToVar(t.label);
13062
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map((e) => convertTerm(e, false)));
13063
+ if (t instanceof OpenListTerm)
13064
+ return new OpenListTerm(
13065
+ t.prefix.map((e) => convertTerm(e, false)),
13066
+ t.tailVar,
13067
+ );
13068
+ if (t instanceof GraphTerm) return allowDirectQuotedPattern ? convertQuotedPatternTerm(t) : copyQuotedTerm(t);
13069
+ return t;
13070
+ }
13071
+
13072
+ const newPremise = premise.map(
13073
+ (tr) => new Triple(convertTerm(tr.s, true), convertTerm(tr.p, true), convertTerm(tr.o, true)),
13074
+ );
13051
13075
  return [newPremise, conclusion];
13052
13076
  }
13053
13077
 
package/eyeling.js CHANGED
@@ -12998,15 +12998,30 @@ function liftBlankRuleVars(premise, conclusion) {
12998
12998
  return t;
12999
12999
  }
13000
13000
 
13001
- function convertTerm(t) {
13001
+ function convertQuotedPatternTerm(t) {
13002
13002
  if (t instanceof Blank) return blankToVar(t.label);
13003
- if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertTerm));
13004
- if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertTerm), t.tailVar);
13005
- if (t instanceof GraphTerm) return copyQuotedTerm(t);
13003
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertQuotedPatternTerm));
13004
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertQuotedPatternTerm), t.tailVar);
13005
+ if (t instanceof GraphTerm) {
13006
+ const triples = t.triples.map(
13007
+ (tr) => new Triple(convertQuotedPatternTerm(tr.s), convertQuotedPatternTerm(tr.p), convertQuotedPatternTerm(tr.o)),
13008
+ );
13009
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
13010
+ }
13006
13011
  return t;
13007
13012
  }
13008
13013
 
13009
- const newPremise = premise.map((tr) => new Triple(convertTerm(tr.s), convertTerm(tr.p), convertTerm(tr.o)));
13014
+ function convertTerm(t, allowDirectQuotedPattern = false) {
13015
+ if (t instanceof Blank) return blankToVar(t.label);
13016
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map((e) => convertTerm(e, false)));
13017
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map((e) => convertTerm(e, false)), t.tailVar);
13018
+ if (t instanceof GraphTerm) return allowDirectQuotedPattern ? convertQuotedPatternTerm(t) : copyQuotedTerm(t);
13019
+ return t;
13020
+ }
13021
+
13022
+ const newPremise = premise.map((tr) =>
13023
+ new Triple(convertTerm(tr.s, true), convertTerm(tr.p, true), convertTerm(tr.o, true)),
13024
+ );
13010
13025
  return [newPremise, conclusion];
13011
13026
  }
13012
13027
 
package/lib/rules.js CHANGED
@@ -40,15 +40,35 @@ function liftBlankRuleVars(premise, conclusion) {
40
40
  return t;
41
41
  }
42
42
 
43
- function convertTerm(t) {
43
+ function convertQuotedPatternTerm(t) {
44
44
  if (t instanceof Blank) return blankToVar(t.label);
45
- if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertTerm));
46
- if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertTerm), t.tailVar);
47
- if (t instanceof GraphTerm) return copyQuotedTerm(t);
45
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map(convertQuotedPatternTerm));
46
+ if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(convertQuotedPatternTerm), t.tailVar);
47
+ if (t instanceof GraphTerm) {
48
+ const triples = t.triples.map(
49
+ (tr) =>
50
+ new Triple(convertQuotedPatternTerm(tr.s), convertQuotedPatternTerm(tr.p), convertQuotedPatternTerm(tr.o)),
51
+ );
52
+ return copyQuotedGraphMetadata(t, new GraphTerm(triples));
53
+ }
54
+ return t;
55
+ }
56
+
57
+ function convertTerm(t, allowDirectQuotedPattern = false) {
58
+ if (t instanceof Blank) return blankToVar(t.label);
59
+ if (t instanceof ListTerm) return new ListTerm(t.elems.map((e) => convertTerm(e, false)));
60
+ if (t instanceof OpenListTerm)
61
+ return new OpenListTerm(
62
+ t.prefix.map((e) => convertTerm(e, false)),
63
+ t.tailVar,
64
+ );
65
+ if (t instanceof GraphTerm) return allowDirectQuotedPattern ? convertQuotedPatternTerm(t) : copyQuotedTerm(t);
48
66
  return t;
49
67
  }
50
68
 
51
- const newPremise = premise.map((tr) => new Triple(convertTerm(tr.s), convertTerm(tr.p), convertTerm(tr.o)));
69
+ const newPremise = premise.map(
70
+ (tr) => new Triple(convertTerm(tr.s, true), convertTerm(tr.p, true), convertTerm(tr.o, true)),
71
+ );
52
72
  return [newPremise, conclusion];
53
73
  }
54
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.22.11",
3
+ "version": "1.22.13",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [