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 +208 -51
- package/dist/browser/eyeling.browser.js +29 -5
- package/eyeling.js +20 -5
- package/lib/rules.js +25 -5
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
313
|
+
### 5.1.1 Quoted formulas in rule bodies: direct pattern positions vs nested data positions
|
|
312
314
|
|
|
313
|
-
There is one important
|
|
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
|
|
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 `{ ... }`
|
|
328
|
-
2. **Formula as a query pattern** —
|
|
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
|
-
> **
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
470
|
-
2. It
|
|
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
|
|
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
|
|
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
|
-
“
|
|
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
|
|
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 “
|
|
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
|
|
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 “
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 /
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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/
|
|
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/
|
|
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
|
|
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
|
|
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(
|
|
13045
|
-
if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(
|
|
13046
|
-
if (t instanceof GraphTerm)
|
|
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
|
-
|
|
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
|
|
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(
|
|
13004
|
-
if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(
|
|
13005
|
-
if (t instanceof GraphTerm)
|
|
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
|
-
|
|
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
|
|
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(
|
|
46
|
-
if (t instanceof OpenListTerm) return new OpenListTerm(t.prefix.map(
|
|
47
|
-
if (t instanceof GraphTerm)
|
|
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(
|
|
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
|
|