eyeling 1.22.5 → 1.22.6
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 +13 -9
- package/dist/browser/eyeling.browser.js +34 -0
- package/eyeling.js +34 -0
- package/lib/builtins.js +34 -0
- package/package.json +1 -1
- package/test/api.test.js +58 -0
package/HANDBOOK.md
CHANGED
|
@@ -2599,19 +2599,23 @@ In that sense, N3 is less a bid to make the web “smarter” than a bid to make
|
|
|
2599
2599
|
|
|
2600
2600
|
<a id="app-c"></a>
|
|
2601
2601
|
|
|
2602
|
-
## Appendix C — N3 beyond Prolog: logic
|
|
2602
|
+
## Appendix C — N3 beyond Prolog: logic that survives the open web
|
|
2603
2603
|
|
|
2604
|
-
Notation3
|
|
2604
|
+
Notation3 and Prolog resemble each other at first glance. Both use variables, unification, and implication-shaped rules. Both can be used to derive consequences from a set of premises. But they were shaped for different native environments. Prolog was designed around executing logic programs. N3 was designed around expressing logic in the same world as RDF: a world of IRIs, triples, graphs, linked documents, and shared vocabularies. That difference in native setting gives N3 a wider built-in reach for web-scale knowledge work.
|
|
2605
2605
|
|
|
2606
|
-
In
|
|
2606
|
+
The first difference is the data model. In Prolog, the basic units are terms and clauses. In N3, the basic units are RDF-style triples and graphs, extended with variables, lists, graph terms, and logical implication. N3 is explicitly defined as an extension of RDF, not as a separate programming language that merely happens to manipulate graphs. That means identifiers, data, and inferred results all live in one interoperable form. A rule consumes graph patterns and produces graph patterns, so the output of reasoning can be published, merged, quoted, exchanged, and reasoned over again without changing representation.
|
|
2607
2607
|
|
|
2608
|
-
-
|
|
2609
|
-
- **Input and output are graphs.** Rules consume graph patterns and produce additional triples, so the result of inference can be represented in the same form as the input data.
|
|
2610
|
-
- **Quoted graphs allow statements-as-data.** N3 can treat a graph (a set of triples) as a term, which makes it possible to represent and reason about assertions (e.g., “this source says …” or “this formula implies …”) as data.
|
|
2611
|
-
- **Rules can be distributed as text artifacts.** Rules can live alongside data, be versioned, and be reused without requiring an external host language to “carry” the meaning.
|
|
2612
|
-
- **Built-ins cover common computations.** Many N3 workflows rely on built-ins for operations such as string handling, list processing, comparisons, and related utilities; some workflows also use IRIs as pointers to retrievable content.
|
|
2608
|
+
The second difference is more profound: N3 can treat graphs as first-class terms. A graph term is a quoted graph. It can appear in subject, predicate, or object position, and it does not automatically assert its contents as true. It is a resource in its own right. This makes it natural to represent claims, reports, beliefs, policies, source snapshots, and provenance. One can say not only “this is true,” but also “this document says this,” “this authority concludes that,” or “this rule applies to that quoted context.” In other words, N3 can reason about statements as statements, not only about the world those statements describe.
|
|
2613
2609
|
|
|
2614
|
-
|
|
2610
|
+
That leads directly to a third advantage: scope is part of the logic. In N3, implicit universal quantification is global, while implicit existential quantification is local to the graph in which it occurs. Once graphs can be nested and quoted, that distinction matters. It allows a reasoner to keep track of what is asserted here, what is asserted inside a quoted subgraph, and what variables belong to which level. This gives N3 a native way to express context-sensitive reasoning that would feel external or encoded-by-convention in classic Prolog.
|
|
2611
|
+
|
|
2612
|
+
A fourth difference is that N3 connects logic to the web inside the language itself. The N3 vocabulary includes `log:semantics` and `log:conclusion` for retrieving and reasoning over local or online sources. It also includes scoped operators such as `log:forAllIn` and `log:notIncludes`, which support scoped universal reasoning and scoped negation-as-failure over a chosen graph or document. So web retrieval, source-bounded reasoning, and reasoning over quoted content are not afterthoughts. They are part of the logic vocabulary. This is one of the clearest senses in which N3 has greater inherent potential: it is not only a language for deriving consequences from facts, but a language for locating, quoting, constraining, and comparing the sources of those facts.
|
|
2613
|
+
|
|
2614
|
+
By contrast, classic Prolog is centered on a proof procedure over definite clauses. In the usual operational reading, Prolog applies a restricted form of SLD-resolution, chooses the leftmost goal, tries clauses top-to-bottom, and searches depth-first. Traditional negation is negation-as-failure, described in the SWI-Prolog glossary as a weak negation. This makes Prolog extremely effective as an executable logic language, but it also means that much of its practical power comes from the operational behavior of the engine, not from a web-native knowledge model.
|
|
2615
|
+
|
|
2616
|
+
This does not make Prolog inferior. Modern Prolog systems add important capabilities beyond the classic core, including tabling, which improves termination behavior and can make Prolog behave more like a bottom-up theorem prover for some classes of problems. But that actually sharpens the comparison: when Prolog needs to work more comfortably in graph-heavy or knowledge-integration settings, it typically gains that power through extensions, libraries, or host-environment choices. N3 starts there. Graphs, quoted formulas, scoped reasoning, and web-connected semantics are part of its native conceptual center.
|
|
2617
|
+
|
|
2618
|
+
So the strongest claim is not that N3 replaces Prolog in every domain. The stronger and more precise claim is this: Prolog is a very strong language for executing logic programs, while N3 has greater inherent potential as a language for publishable, linkable, inspectable, and source-aware logic on the open web. It lets facts, rules, quoted claims, retrieved documents, and derived conclusions all live in one semantic space. For a system like Eyeling, that is not a cosmetic difference. It is the reason the language can scale from local inference to web-shaped reasoning without changing its basic form.
|
|
2615
2619
|
|
|
2616
2620
|
---
|
|
2617
2621
|
|
|
@@ -3988,6 +3988,23 @@
|
|
|
3988
3988
|
results.push(s2);
|
|
3989
3989
|
}
|
|
3990
3990
|
|
|
3991
|
+
// Also match quoted log:implies triples present as data in the current scope.
|
|
3992
|
+
if (facts && typeof facts.length === 'number') {
|
|
3993
|
+
ensureFactIndexes(facts);
|
|
3994
|
+
const pb = facts.__byPred.get(goal.p.__tid) || [];
|
|
3995
|
+
for (const idx of pb) {
|
|
3996
|
+
const tr = facts[idx];
|
|
3997
|
+
|
|
3998
|
+
let s2 = unifyTerm(goal.s, tr.s, subst);
|
|
3999
|
+
if (s2 === null) continue;
|
|
4000
|
+
|
|
4001
|
+
s2 = unifyTerm(goal.o, tr.o, s2);
|
|
4002
|
+
if (s2 === null) continue;
|
|
4003
|
+
|
|
4004
|
+
results.push(s2);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
|
|
3991
4008
|
return results;
|
|
3992
4009
|
}
|
|
3993
4010
|
|
|
@@ -4017,6 +4034,23 @@
|
|
|
4017
4034
|
results.push(s2);
|
|
4018
4035
|
}
|
|
4019
4036
|
|
|
4037
|
+
// Also match quoted log:impliedBy triples present as data in the current scope.
|
|
4038
|
+
if (facts && typeof facts.length === 'number') {
|
|
4039
|
+
ensureFactIndexes(facts);
|
|
4040
|
+
const pb = facts.__byPred.get(goal.p.__tid) || [];
|
|
4041
|
+
for (const idx of pb) {
|
|
4042
|
+
const tr = facts[idx];
|
|
4043
|
+
|
|
4044
|
+
let s2 = unifyTerm(goal.s, tr.s, subst);
|
|
4045
|
+
if (s2 === null) continue;
|
|
4046
|
+
|
|
4047
|
+
s2 = unifyTerm(goal.o, tr.o, s2);
|
|
4048
|
+
if (s2 === null) continue;
|
|
4049
|
+
|
|
4050
|
+
results.push(s2);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4020
4054
|
return results;
|
|
4021
4055
|
}
|
|
4022
4056
|
|
package/eyeling.js
CHANGED
|
@@ -3974,6 +3974,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3974
3974
|
results.push(s2);
|
|
3975
3975
|
}
|
|
3976
3976
|
|
|
3977
|
+
// Also match quoted log:implies triples present as data in the current scope.
|
|
3978
|
+
if (facts && typeof facts.length === 'number') {
|
|
3979
|
+
ensureFactIndexes(facts);
|
|
3980
|
+
const pb = facts.__byPred.get(goal.p.__tid) || [];
|
|
3981
|
+
for (const idx of pb) {
|
|
3982
|
+
const tr = facts[idx];
|
|
3983
|
+
|
|
3984
|
+
let s2 = unifyTerm(goal.s, tr.s, subst);
|
|
3985
|
+
if (s2 === null) continue;
|
|
3986
|
+
|
|
3987
|
+
s2 = unifyTerm(goal.o, tr.o, s2);
|
|
3988
|
+
if (s2 === null) continue;
|
|
3989
|
+
|
|
3990
|
+
results.push(s2);
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3977
3994
|
return results;
|
|
3978
3995
|
}
|
|
3979
3996
|
|
|
@@ -4003,6 +4020,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
4003
4020
|
results.push(s2);
|
|
4004
4021
|
}
|
|
4005
4022
|
|
|
4023
|
+
// Also match quoted log:impliedBy triples present as data in the current scope.
|
|
4024
|
+
if (facts && typeof facts.length === 'number') {
|
|
4025
|
+
ensureFactIndexes(facts);
|
|
4026
|
+
const pb = facts.__byPred.get(goal.p.__tid) || [];
|
|
4027
|
+
for (const idx of pb) {
|
|
4028
|
+
const tr = facts[idx];
|
|
4029
|
+
|
|
4030
|
+
let s2 = unifyTerm(goal.s, tr.s, subst);
|
|
4031
|
+
if (s2 === null) continue;
|
|
4032
|
+
|
|
4033
|
+
s2 = unifyTerm(goal.o, tr.o, s2);
|
|
4034
|
+
if (s2 === null) continue;
|
|
4035
|
+
|
|
4036
|
+
results.push(s2);
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
|
|
4006
4040
|
return results;
|
|
4007
4041
|
}
|
|
4008
4042
|
|
package/lib/builtins.js
CHANGED
|
@@ -3495,6 +3495,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3495
3495
|
results.push(s2);
|
|
3496
3496
|
}
|
|
3497
3497
|
|
|
3498
|
+
// Also match quoted log:implies triples present as data in the current scope.
|
|
3499
|
+
if (facts && typeof facts.length === 'number') {
|
|
3500
|
+
ensureFactIndexes(facts);
|
|
3501
|
+
const pb = facts.__byPred.get(goal.p.__tid) || [];
|
|
3502
|
+
for (const idx of pb) {
|
|
3503
|
+
const tr = facts[idx];
|
|
3504
|
+
|
|
3505
|
+
let s2 = unifyTerm(goal.s, tr.s, subst);
|
|
3506
|
+
if (s2 === null) continue;
|
|
3507
|
+
|
|
3508
|
+
s2 = unifyTerm(goal.o, tr.o, s2);
|
|
3509
|
+
if (s2 === null) continue;
|
|
3510
|
+
|
|
3511
|
+
results.push(s2);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3498
3515
|
return results;
|
|
3499
3516
|
}
|
|
3500
3517
|
|
|
@@ -3524,6 +3541,23 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
|
|
|
3524
3541
|
results.push(s2);
|
|
3525
3542
|
}
|
|
3526
3543
|
|
|
3544
|
+
// Also match quoted log:impliedBy triples present as data in the current scope.
|
|
3545
|
+
if (facts && typeof facts.length === 'number') {
|
|
3546
|
+
ensureFactIndexes(facts);
|
|
3547
|
+
const pb = facts.__byPred.get(goal.p.__tid) || [];
|
|
3548
|
+
for (const idx of pb) {
|
|
3549
|
+
const tr = facts[idx];
|
|
3550
|
+
|
|
3551
|
+
let s2 = unifyTerm(goal.s, tr.s, subst);
|
|
3552
|
+
if (s2 === null) continue;
|
|
3553
|
+
|
|
3554
|
+
s2 = unifyTerm(goal.o, tr.o, s2);
|
|
3555
|
+
if (s2 === null) continue;
|
|
3556
|
+
|
|
3557
|
+
results.push(s2);
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3527
3561
|
return results;
|
|
3528
3562
|
}
|
|
3529
3563
|
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -2178,6 +2178,64 @@ _:x :hates { _:foo :making :mess }.
|
|
|
2178
2178
|
/^"3"\^\^xsd:integer\s+:is\s+\("3"\s+xsd:integer\)\s*\./m,
|
|
2179
2179
|
],
|
|
2180
2180
|
},
|
|
2181
|
+
{
|
|
2182
|
+
name: '245 regression: log:includes sees quoted log:implies triples as data',
|
|
2183
|
+
opt: { proofComments: false },
|
|
2184
|
+
input: `@prefix : <http://example.org/> .
|
|
2185
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
|
2186
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
2187
|
+
|
|
2188
|
+
<> :data {
|
|
2189
|
+
{
|
|
2190
|
+
?A a ?B .
|
|
2191
|
+
?B rdfs:subClassOf ?C.
|
|
2192
|
+
}
|
|
2193
|
+
=>
|
|
2194
|
+
{
|
|
2195
|
+
?A a ?C .
|
|
2196
|
+
}.
|
|
2197
|
+
}.
|
|
2198
|
+
|
|
2199
|
+
{
|
|
2200
|
+
?W :data ?F.
|
|
2201
|
+
?F log:includes { ?X log:implies ?Y }.
|
|
2202
|
+
}
|
|
2203
|
+
=>
|
|
2204
|
+
{
|
|
2205
|
+
:result :is ?X .
|
|
2206
|
+
}.
|
|
2207
|
+
`,
|
|
2208
|
+
expect: [/^:result\s+:is\s+\{[\s\S]*\?A\s+a\s+\?B\s*\.[\s\S]*\?B\s+rdfs:subClassOf\s+\?C\s*\.[\s\S]*\}\s*\./m],
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
name: '246 regression: log:includes sees quoted log:impliedBy triples as data',
|
|
2212
|
+
opt: { proofComments: false },
|
|
2213
|
+
input: `@prefix : <http://example.org/> .
|
|
2214
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
|
2215
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
2216
|
+
|
|
2217
|
+
<> :data {
|
|
2218
|
+
{
|
|
2219
|
+
?A a ?C .
|
|
2220
|
+
}
|
|
2221
|
+
<=
|
|
2222
|
+
{
|
|
2223
|
+
?A a ?B .
|
|
2224
|
+
?B rdfs:subClassOf ?C.
|
|
2225
|
+
}.
|
|
2226
|
+
}.
|
|
2227
|
+
|
|
2228
|
+
{
|
|
2229
|
+
?W :data ?F.
|
|
2230
|
+
?F log:includes { ?Y <= ?X }.
|
|
2231
|
+
}
|
|
2232
|
+
=>
|
|
2233
|
+
{
|
|
2234
|
+
:result :is ?X .
|
|
2235
|
+
}.
|
|
2236
|
+
`,
|
|
2237
|
+
expect: [/^:result\s+:is\s+\{[\s\S]*\?A\s+a\s+\?B\s*\.[\s\S]*\?B\s+rdfs:subClassOf\s+\?C\s*\.[\s\S]*\}\s*\./m],
|
|
2238
|
+
},
|
|
2181
2239
|
];
|
|
2182
2240
|
|
|
2183
2241
|
let passed = 0;
|