eyeling 1.11.15 → 1.11.17
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 +3 -12
- package/README.md +1 -0
- package/SEMANTICS.md +56 -0
- package/examples/godel-template.n3 +172 -0
- package/examples/output/godel-template.n3 +8 -0
- package/examples/path-discovery.n3 +1 -1
- package/eyeling-builtins.ttl +2 -2
- package/eyeling.js +468 -545
- package/lib/builtins.js +3 -8
- package/lib/cli.js +3 -2
- package/lib/deref.js +1 -7
- package/lib/engine.js +439 -494
- package/lib/explain.js +1 -11
- package/lib/lexer.js +23 -6
- package/lib/prelude.js +0 -2
- package/lib/rules.js +1 -8
- package/lib/skolem.js +2 -12
- package/package.json +2 -1
- package/test/api.test.js +7 -7
- package/test/manifest.test.js +6 -5
- package/test/n3gen.test.js +19 -10
- package/test/package.test.js +67 -66
- package/test/playground.test.js +182 -146
- package/tools/bundle.js +47 -39
- package/tools/n3gen.js +3 -3
package/HANDBOOK.md
CHANGED
|
@@ -553,21 +553,12 @@ That “standardize apart” step is essential. Without it, reusing a rule multi
|
|
|
553
553
|
**Implementation note (performance):** `standardizeRule` is called for every backward-rule candidate during proof search.
|
|
554
554
|
To reduce allocation pressure, Eyeling reuses a single fresh `Var(...)` object per *original* variable name within one standardization pass (all occurrences of `?x` in the rule become the same fresh `?x__N` object). This is semantics-preserving — it still “separates” invocations — but it avoids creating many duplicate Var objects when a variable appears repeatedly in a rule body.
|
|
555
555
|
|
|
556
|
-
### 8.6 Substitution
|
|
556
|
+
### 8.6 Substitution size on deep proofs
|
|
557
557
|
|
|
558
558
|
The trail-based substitution store removes the biggest accidental quadratic cost (copying a growing substitution object at every step).
|
|
559
|
-
|
|
559
|
+
In deep and branchy searches, the substitution trail still grows, and long variable-to-variable chains increase the work done by `applySubstTerm`.
|
|
560
560
|
|
|
561
|
-
Eyeling
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
* if depth is high or substitution is large, it keeps only bindings relevant to:
|
|
565
|
-
|
|
566
|
-
* the remaining goals
|
|
567
|
-
* variables from the original goal list (“answer variables”)
|
|
568
|
-
* plus variables transitively referenced inside kept bindings
|
|
569
|
-
|
|
570
|
-
This is semantics-preserving for the ongoing proof search, but dramatically improves performance on deep recursive proofs.
|
|
561
|
+
Eyeling currently keeps the full trail as-is during search and when emitting answers; it does not run a substitution compaction pass, and it does not perform explicit substitution composition.
|
|
571
562
|
|
|
572
563
|
---
|
|
573
564
|
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ A compact [Notation3 (N3)](https://notation3.org/) reasoner in **JavaScript**.
|
|
|
10
10
|
## Links
|
|
11
11
|
|
|
12
12
|
- **Handbook:** [https://eyereasoner.github.io/eyeling/HANDBOOK](https://eyereasoner.github.io/eyeling/HANDBOOK)
|
|
13
|
+
- **Semantics:** [https://eyereasoner.github.io/eyeling/SEMANTICS](https://eyereasoner.github.io/eyeling/SEMANTICS)
|
|
13
14
|
- **Playground:** [https://eyereasoner.github.io/eyeling/demo](https://eyereasoner.github.io/eyeling/demo)
|
|
14
15
|
- **Notation3 test suite:** [https://codeberg.org/phochste/notation3tests](https://codeberg.org/phochste/notation3tests)
|
|
15
16
|
- **Eyeling conformance report:** [https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md](https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md)
|
package/SEMANTICS.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
How does the [Eyeling HANDBOOK](https://eyereasoner.github.io/eyeling/HANDBOOK) line up with the W3C CG [Notation3 Semantics](https://w3c.github.io/N3/spec/semantics) document — and where does it intentionally diverge?
|
|
2
|
+
|
|
3
|
+
## Where Eyeling is strongly aligned
|
|
4
|
+
|
|
5
|
+
* **Core term model (IRIs, literals, variables, blank nodes, lists, quoted formulas):**
|
|
6
|
+
The semantics spec treats N3 terms as IRIs/literals/variables plus **lists** and **graph terms**.
|
|
7
|
+
Eyeling’s handbook describes the same internal term universe: `Iri`, `Literal`, `Var`, `Blank`, `ListTerm`, `GraphTerm`.
|
|
8
|
+
|
|
9
|
+
* **Quoted formulas need alpha-equivalence / isomorphism:**
|
|
10
|
+
The semantics spec defines isomorphism for graphs and graph terms using renaming mappings (including special handling for nested scopes).
|
|
11
|
+
Eyeling implements this operationally as **alpha-equivalence for `GraphTerm`**, explicitly describing “consistent renaming” as the match criterion.
|
|
12
|
+
|
|
13
|
+
* **Rules as implication (and `true` as empty formula):**
|
|
14
|
+
The semantics spec defines log-semantics for `log:implies` and explicitly treats boolean `true`/`false` as special literals, with `true` corresponding to the empty formula.
|
|
15
|
+
Eyeling’s parser/normalizer explicitly supports `{P} => {C}` and `{P} log:implies {C}`, and treats `true` as `{}`.
|
|
16
|
+
|
|
17
|
+
* **Lists as first-class citizens (not just RDF collections):**
|
|
18
|
+
The semantics spec treats lists as proper N3 terms.
|
|
19
|
+
Eyeling uses concrete `ListTerm`s and even materializes RDF `rdf:first`/`rdf:rest` chains into list terms to operate uniformly.
|
|
20
|
+
|
|
21
|
+
## Where Eyeling diverges or goes beyond the semantics doc
|
|
22
|
+
|
|
23
|
+
### 1) Blank nodes in **rule bodies**: Eyeling chooses “N3 practice” over “bnodes are existential”
|
|
24
|
+
|
|
25
|
+
The semantics doc states (for concrete syntax intuition) that **blank nodes correspond to existentially quantified variables** with **local scope**.
|
|
26
|
+
Eyeling *intentionally* rewrites blanks in **premises** into variables (“universally-quantified placeholders”), to avoid “existential in the body” behavior.
|
|
27
|
+
|
|
28
|
+
This is a *real semantic choice*: it matches how many people *write* N3 rules, but it is not the same as a straightforward “bnodes are existentials everywhere” reading.
|
|
29
|
+
|
|
30
|
+
### 2) “Groundness” of quoted formulas containing variables
|
|
31
|
+
|
|
32
|
+
In the semantics spec, whether a graph term is ground depends on whether the underlying graph is closed (no free variables), and it discusses how variables can appear free when you isolate a nested graph term.
|
|
33
|
+
Eyeling explicitly makes a pragmatic choice: **variables inside a `GraphTerm` do not make the surrounding triple non-ground** (“variables inside formulas don’t leak”).
|
|
34
|
+
|
|
35
|
+
That’s convenient for operational indexing/matching, but it doesn’t mirror the model-theoretic notion of ground graph terms one-to-one.
|
|
36
|
+
|
|
37
|
+
### 3) Eyeling implements lots of behavior the semantics doc does not yet define
|
|
38
|
+
|
|
39
|
+
The semantics report currently only gives special meaning to `log:implies` (and says LP is planned to be extended).
|
|
40
|
+
Eyeling defines a large operational “standard library” of builtins and advanced control features (e.g., scoped querying / snapshotting). For example, it gives `log:includes`/`log:notIncludes` a two-phase snapshot semantics for determinism.
|
|
41
|
+
|
|
42
|
+
So: Eyeling is **ahead of / outside** what `semantics.html` formally specifies today.
|
|
43
|
+
|
|
44
|
+
### 4) Constraint handling via “inference fuses” (`=> false`) is operational
|
|
45
|
+
|
|
46
|
+
The semantics doc includes a notion of `false` in connection with `log:implies` constraints.
|
|
47
|
+
Eyeling turns `{...} => false` into an *engine-level hard failure* (exit status, message), i.e., a procedural constraint mechanism rather than just a semantic condition.
|
|
48
|
+
|
|
49
|
+
That’s useful in tooling, but it’s not something the model-theoretic semantics itself “does” (it defines truth/entailment, not process control).
|
|
50
|
+
|
|
51
|
+
### 5) Possible coverage gaps vs full N3 surface language (not strictly “semantics.html”, but relevant)
|
|
52
|
+
|
|
53
|
+
Eyeling’s handbook lists supported directives/tokens (`@prefix`, `@base`, etc.) but does not mention explicit quantifier directives like `@forAll` / `@forSome`.
|
|
54
|
+
The semantics document leans on explicit quantification in its **abstract syntax** discussion.
|
|
55
|
+
So Eyeling appears to support *implicit* quantification via `?x` and blanks (plus its own rule-normalization choices), but may not implement the full explicit-quantifier surface syntax.
|
|
56
|
+
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# Gödel Template + Substitution Example
|
|
3
|
+
#
|
|
4
|
+
# Goal
|
|
5
|
+
# Model a “Gödel-like” sentence as DATA:
|
|
6
|
+
# - a token template with a hole (prefix ++ numeral(n) ++ suffix)
|
|
7
|
+
# - a substitution step that splices in the decimal numeral tokens for n
|
|
8
|
+
# - a Gödel encoding that maps the resulting token list to a number:
|
|
9
|
+
# g = Π prime[i] ^ code(token[i])
|
|
10
|
+
#
|
|
11
|
+
# Notes
|
|
12
|
+
# • This is a toy arithmetization: the token alphabet and codes are small and
|
|
13
|
+
# illustrative, not the full formal encoding used in incompleteness proofs.
|
|
14
|
+
# • The numeral construction is demand-driven via backward rules (<=):
|
|
15
|
+
# Eyeling will only compute digits(n) when some other rule needs it.
|
|
16
|
+
# • Integer quotient is obtained via:
|
|
17
|
+
# r = n mod 10
|
|
18
|
+
# q = (n - r) / 10
|
|
19
|
+
# so q stays integral even though math:quotient is real division.
|
|
20
|
+
#
|
|
21
|
+
# What to look for in the output
|
|
22
|
+
# - :Demo :digits ( :d4 :d2 ) (numeral tokens)
|
|
23
|
+
# - :Demo :tokens ( ... :d4 :d2 ... ) (instantiated sentence)
|
|
24
|
+
# - :Demo :godelPairs ( (2 c0) (3 c1) ... ) (prime/exponent pairs)
|
|
25
|
+
# - :Demo :godelNumber <big integer> (Gödel number)
|
|
26
|
+
# ============================================================================
|
|
27
|
+
|
|
28
|
+
@prefix : <http://example.org/godel-template#>.
|
|
29
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
|
|
30
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
31
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
32
|
+
|
|
33
|
+
# ----------------------------------------------------------------------------
|
|
34
|
+
# 0) Token alphabet + codes (toy)
|
|
35
|
+
# ----------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
:NOT :code 1.
|
|
38
|
+
:PROVABLE :code 2.
|
|
39
|
+
:IN :code 3.
|
|
40
|
+
:S :code 4.
|
|
41
|
+
:LP :code 5.
|
|
42
|
+
:RP :code 6.
|
|
43
|
+
|
|
44
|
+
# Digit tokens (each has a numeric value + a token code)
|
|
45
|
+
:d0 :digitValue 0; :code 10.
|
|
46
|
+
:d1 :digitValue 1; :code 11.
|
|
47
|
+
:d2 :digitValue 2; :code 12.
|
|
48
|
+
:d3 :digitValue 3; :code 13.
|
|
49
|
+
:d4 :digitValue 4; :code 14.
|
|
50
|
+
:d5 :digitValue 5; :code 15.
|
|
51
|
+
:d6 :digitValue 6; :code 16.
|
|
52
|
+
:d7 :digitValue 7; :code 17.
|
|
53
|
+
:d8 :digitValue 8; :code 18.
|
|
54
|
+
:d9 :digitValue 9; :code 19.
|
|
55
|
+
|
|
56
|
+
# primes for positions 0.. (enough for prefix + digits + suffix)
|
|
57
|
+
:primeBases :is ( 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 ).
|
|
58
|
+
|
|
59
|
+
# ----------------------------------------------------------------------------
|
|
60
|
+
# 1) Template schema as data
|
|
61
|
+
# Think: NOT PROVABLE IN S ( <decimal numeral> )
|
|
62
|
+
# ----------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
:GSchema a :Template;
|
|
65
|
+
:english "This statement cannot be proven within this system.";
|
|
66
|
+
:prefixTokens ( :NOT :PROVABLE :IN :S :LP );
|
|
67
|
+
:suffixTokens ( :RP ).
|
|
68
|
+
|
|
69
|
+
# ----------------------------------------------------------------------------
|
|
70
|
+
# 2) Decimal numeral tokens via backward rules (demand-driven)
|
|
71
|
+
# ----------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
# Base case: n < 10 => digits(n) = ( d(n) )
|
|
74
|
+
{
|
|
75
|
+
?n :digits ( ?d ).
|
|
76
|
+
} <= {
|
|
77
|
+
?n math:lessThan 10.
|
|
78
|
+
?d :digitValue ?n.
|
|
79
|
+
}.
|
|
80
|
+
|
|
81
|
+
# Recursive case: n >= 10
|
|
82
|
+
# digits(n) = digits(q) ++ ( d(r) )
|
|
83
|
+
# where r = n mod 10
|
|
84
|
+
# q = (n - r)/10 (integer-safe)
|
|
85
|
+
{
|
|
86
|
+
?n :digits ?digs.
|
|
87
|
+
} <= {
|
|
88
|
+
?n math:notLessThan 10.
|
|
89
|
+
|
|
90
|
+
( ?n 10 ) math:remainder ?r.
|
|
91
|
+
( ?n ?r ) math:difference ?nMinusR.
|
|
92
|
+
( ?nMinusR 10 ) math:quotient ?q.
|
|
93
|
+
|
|
94
|
+
?q :digits ?qdigs.
|
|
95
|
+
?dr :digitValue ?r.
|
|
96
|
+
|
|
97
|
+
( ?qdigs ( ?dr ) ) list:append ?digs.
|
|
98
|
+
}.
|
|
99
|
+
|
|
100
|
+
# ----------------------------------------------------------------------------
|
|
101
|
+
# 3) Substitute numeral(n) into the template to get the token list
|
|
102
|
+
# ----------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
:Demo :n 42.
|
|
105
|
+
|
|
106
|
+
{
|
|
107
|
+
:Demo :n ?n.
|
|
108
|
+
?n :digits ?digits.
|
|
109
|
+
|
|
110
|
+
:GSchema :prefixTokens ?pre.
|
|
111
|
+
:GSchema :suffixTokens ?suf.
|
|
112
|
+
|
|
113
|
+
( ?pre ?digits ) list:append ?tmp.
|
|
114
|
+
( ?tmp ?suf ) list:append ?tokens.
|
|
115
|
+
}
|
|
116
|
+
=>
|
|
117
|
+
{
|
|
118
|
+
:Demo :digits ?digits.
|
|
119
|
+
:Demo :tokens ?tokens.
|
|
120
|
+
}.
|
|
121
|
+
|
|
122
|
+
# ----------------------------------------------------------------------------
|
|
123
|
+
# 4) Gödel encode tokens: g = Π prime[i] ^ code(token[i])
|
|
124
|
+
# - build pairs (prime code)
|
|
125
|
+
# - compute factors prime^code
|
|
126
|
+
# - multiply factors
|
|
127
|
+
# ----------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
# Build godelPairs as a list of list-pairs: ( (p0 c0) (p1 c1) ... )
|
|
130
|
+
{
|
|
131
|
+
:Demo :tokens ?tokens.
|
|
132
|
+
:primeBases :is ?primes.
|
|
133
|
+
|
|
134
|
+
( (?p ?c)
|
|
135
|
+
{
|
|
136
|
+
?tokens list:iterate (?i ?tok).
|
|
137
|
+
( ?primes ?i ) list:memberAt ?p.
|
|
138
|
+
?tok :code ?c.
|
|
139
|
+
}
|
|
140
|
+
?pairs
|
|
141
|
+
) log:collectAllIn _:scope.
|
|
142
|
+
}
|
|
143
|
+
=>
|
|
144
|
+
{
|
|
145
|
+
:Demo :godelPairs ?pairs.
|
|
146
|
+
}.
|
|
147
|
+
|
|
148
|
+
# Compute godelNumber from the factors (p^c) and their product
|
|
149
|
+
{
|
|
150
|
+
:Demo :godelPairs ?pairs.
|
|
151
|
+
|
|
152
|
+
( ?factor
|
|
153
|
+
{
|
|
154
|
+
?pairs list:member ?pair.
|
|
155
|
+
?pair math:exponentiation ?factor.
|
|
156
|
+
}
|
|
157
|
+
?factors
|
|
158
|
+
) log:collectAllIn _:scope.
|
|
159
|
+
|
|
160
|
+
?factors math:product ?g.
|
|
161
|
+
}
|
|
162
|
+
=>
|
|
163
|
+
{
|
|
164
|
+
:Demo :godelNumber ?g.
|
|
165
|
+
|
|
166
|
+
# convenient outputs (so you can spot them quickly)
|
|
167
|
+
:outDigits :is ?digits.
|
|
168
|
+
:outTokens :is ?tokens.
|
|
169
|
+
:outPairs :is ?pairs.
|
|
170
|
+
:outG :is ?g.
|
|
171
|
+
}.
|
|
172
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
@prefix : <http://example.org/godel-template#> .
|
|
2
|
+
|
|
3
|
+
:Demo :digits (:d4 :d2) .
|
|
4
|
+
:Demo :tokens (:NOT :PROVABLE :IN :S :LP :d4 :d2 :RP) .
|
|
5
|
+
:Demo :godelPairs ((2 1) (3 2) (5 3) (7 4) (11 5) (13 14) (17 12) (19 6)) .
|
|
6
|
+
:Demo :godelNumber 93897428453934743498383922339000952994095115872750 .
|
|
7
|
+
:outPairs :is ((2 1) (3 2) (5 3) (7 4) (11 5) (13 14) (17 12) (19 6)) .
|
|
8
|
+
:outG :is 93897428453934743498383922339000952994095115872750 .
|
|
@@ -96451,7 +96451,7 @@ res:AIRPORT_N nepo:hasOutboundRouteTo res:AIRPORT_N.
|
|
|
96451
96451
|
?route list:firstRest (?from ?newRoute).
|
|
96452
96452
|
}.
|
|
96453
96453
|
|
|
96454
|
-
# find routes from
|
|
96454
|
+
# find routes from Ostend-Bruges International Airport to Václav Havel Airport Prague with at most 2 stopovers
|
|
96455
96455
|
{
|
|
96456
96456
|
?source rdfs:label "Ostend-Bruges International Airport".
|
|
96457
96457
|
?destination rdfs:label "Václav Havel Airport Prague".
|
package/eyeling-builtins.ttl
CHANGED
|
@@ -275,10 +275,10 @@ log:uri a ex:Builtin ; ex:kind ex:Function ;
|
|
|
275
275
|
rdfs:comment "Converts between an IRI and its string representation (with safety checks on IRIREF characters)." .
|
|
276
276
|
|
|
277
277
|
log:trace a ex:Builtin ; ex:kind ex:SideEffect ;
|
|
278
|
-
rdfs:comment "Side-effect (debug tracing). Prints '<subject> TRACE <object>' to stderr (Node: process.stderr; browser: console.error). Always succeeds once; does not bind variables.
|
|
278
|
+
rdfs:comment "Side-effect (debug tracing). Prints '<subject> TRACE <object>' to stderr (Node: process.stderr; browser: console.error). Always succeeds once; does not bind variables." .
|
|
279
279
|
|
|
280
280
|
log:outputString a ex:Builtin ; ex:kind ex:SideEffect ;
|
|
281
|
-
rdfs:comment "Side-effect (printing). Requires ground subject and non-variable object string. Does not bind variables.
|
|
281
|
+
rdfs:comment "Side-effect (printing). Requires ground subject and non-variable object string. Does not bind variables." .
|
|
282
282
|
|
|
283
283
|
# --- string: (tests + functions) ------------------------------------
|
|
284
284
|
|