eyeling 1.20.0 → 1.21.1
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 +12 -3
- package/dist/browser/eyeling.browser.js +38 -7
- package/examples/matrix-mechanics.n3 +374 -0
- package/examples/output/matrix-mechanics.n3 +14 -0
- package/eyeling.js +38 -7
- package/index.d.ts +1 -0
- package/lib/engine.js +38 -7
- package/package.json +1 -1
- package/test/api.test.js +54 -0
- package/test/playground.test.js +234 -72
- /package/examples/{collatz.n3 → collatz-1000.n3} +0 -0
- /package/examples/{goldbach.n3 → goldbach-1000.n3} +0 -0
- /package/examples/{kaprekar.n3 → kaprekar-6174.n3} +0 -0
- /package/examples/output/{collatz.n3 → collatz-1000.n3} +0 -0
- /package/examples/output/{goldbach.n3 → goldbach-1000.n3} +0 -0
- /package/examples/output/{kaprekar.n3 → kaprekar-6174.n3} +0 -0
package/HANDBOOK.md
CHANGED
|
@@ -1757,7 +1757,9 @@ The same API can now also emit RDF/JS output. When `rdfjs: true` is passed, ever
|
|
|
1757
1757
|
- `triple` — Eyeling’s N3 string form
|
|
1758
1758
|
- `quad` — the same fact as an RDF/JS default-graph quad
|
|
1759
1759
|
|
|
1760
|
-
|
|
1760
|
+
If your closure may contain N3-only terms such as quoted formulas (`GraphTerm`), RDF/JS conversion can fail because those terms have no standard RDF/JS representation. In that case, pass `skipUnsupportedRdfJs: true` to keep the full N3 closure while silently omitting any derived triples that cannot be represented as RDF/JS quads. When this flag is enabled, `onDerived(...)` still fires for every derived fact, but `quad` is only present for the representable ones.
|
|
1761
|
+
|
|
1762
|
+
For fully stream-oriented RDF/JS consumers there is also `reasonRdfJs(...)`, which exposes the derived facts as an async iterable of RDF/JS quads. The same `skipUnsupportedRdfJs: true` flag applies there as well.
|
|
1761
1763
|
|
|
1762
1764
|
---
|
|
1763
1765
|
|
|
@@ -2002,8 +2004,11 @@ import eyeling from './eyeling.js';
|
|
|
2002
2004
|
|
|
2003
2005
|
const result = eyeling.reasonStream(input, {
|
|
2004
2006
|
proof: false,
|
|
2005
|
-
|
|
2007
|
+
rdfjs: true,
|
|
2008
|
+
skipUnsupportedRdfJs: true,
|
|
2009
|
+
onDerived: ({ triple, quad }) => {
|
|
2006
2010
|
if (quad) console.log(quad);
|
|
2011
|
+
else console.warn('Skipped non-RDF/JS derived triple:', triple);
|
|
2007
2012
|
},
|
|
2008
2013
|
});
|
|
2009
2014
|
```
|
|
@@ -2011,11 +2016,15 @@ const result = eyeling.reasonStream(input, {
|
|
|
2011
2016
|
That same path also lets derived results be consumed as an async stream of RDF/JS quads:
|
|
2012
2017
|
|
|
2013
2018
|
```js
|
|
2014
|
-
for await (const quad of eyeling.reasonRdfJs(input
|
|
2019
|
+
for await (const quad of eyeling.reasonRdfJs(input, {
|
|
2020
|
+
skipUnsupportedRdfJs: true,
|
|
2021
|
+
})) {
|
|
2015
2022
|
console.log(quad);
|
|
2016
2023
|
}
|
|
2017
2024
|
```
|
|
2018
2025
|
|
|
2026
|
+
Use `skipUnsupportedRdfJs: true` when you want RDF/JS consumers to ignore derived triples that contain N3-only terms such as quoted formulas. This affects only RDF/JS export. The underlying Eyeling closure and `closureN3` output remain unchanged.
|
|
2027
|
+
|
|
2019
2028
|
Use these entry points when you need one or more of the following:
|
|
2020
2029
|
|
|
2021
2030
|
- RDF/JS quads as fact input
|
|
@@ -8679,6 +8679,23 @@ function forwardChainAndCollectLogQueryConclusions(
|
|
|
8679
8679
|
|
|
8680
8680
|
// (proof printing + log:outputString moved to lib/explain.js)
|
|
8681
8681
|
|
|
8682
|
+
function isUnsupportedRdfJsConversionError(err) {
|
|
8683
|
+
return (
|
|
8684
|
+
err instanceof TypeError &&
|
|
8685
|
+
typeof err.message === 'string' &&
|
|
8686
|
+
err.message.startsWith('Cannot convert N3-only term ')
|
|
8687
|
+
);
|
|
8688
|
+
}
|
|
8689
|
+
|
|
8690
|
+
function maybeTripleToRdfJsQuad(triple, rdfFactory, skipUnsupportedRdfJs) {
|
|
8691
|
+
try {
|
|
8692
|
+
return internalTripleToRdfJsQuad(triple, rdfFactory);
|
|
8693
|
+
} catch (err) {
|
|
8694
|
+
if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return null;
|
|
8695
|
+
throw err;
|
|
8696
|
+
}
|
|
8697
|
+
}
|
|
8698
|
+
|
|
8682
8699
|
function reasonStream(input, opts = {}) {
|
|
8683
8700
|
const {
|
|
8684
8701
|
baseIri = null,
|
|
@@ -8688,6 +8705,7 @@ function reasonStream(input, opts = {}) {
|
|
|
8688
8705
|
enforceHttps = false,
|
|
8689
8706
|
rdfjs = false,
|
|
8690
8707
|
dataFactory = null,
|
|
8708
|
+
skipUnsupportedRdfJs = false,
|
|
8691
8709
|
builtinModules = null,
|
|
8692
8710
|
} = opts;
|
|
8693
8711
|
|
|
@@ -8750,7 +8768,10 @@ function reasonStream(input, opts = {}) {
|
|
|
8750
8768
|
if (typeof onDerived === 'function') {
|
|
8751
8769
|
for (const qdf of queryDerived) {
|
|
8752
8770
|
const payload = { triple: tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
8753
|
-
if (rdfFactory)
|
|
8771
|
+
if (rdfFactory) {
|
|
8772
|
+
const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
8773
|
+
if (quad) payload.quad = quad;
|
|
8774
|
+
}
|
|
8754
8775
|
onDerived(payload);
|
|
8755
8776
|
}
|
|
8756
8777
|
}
|
|
@@ -8766,7 +8787,10 @@ function reasonStream(input, opts = {}) {
|
|
|
8766
8787
|
triple: tripleToN3(df.fact, prefixes),
|
|
8767
8788
|
df,
|
|
8768
8789
|
};
|
|
8769
|
-
if (rdfFactory)
|
|
8790
|
+
if (rdfFactory) {
|
|
8791
|
+
const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
8792
|
+
if (quad) payload.quad = quad;
|
|
8793
|
+
}
|
|
8770
8794
|
onDerived(payload);
|
|
8771
8795
|
}
|
|
8772
8796
|
},
|
|
@@ -8797,15 +8821,19 @@ function reasonStream(input, opts = {}) {
|
|
|
8797
8821
|
};
|
|
8798
8822
|
|
|
8799
8823
|
if (rdfFactory) {
|
|
8800
|
-
__out.closureQuads = closureTriples
|
|
8801
|
-
|
|
8824
|
+
__out.closureQuads = closureTriples
|
|
8825
|
+
.map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
|
|
8826
|
+
.filter(Boolean);
|
|
8827
|
+
__out.queryQuads = queryTriples
|
|
8828
|
+
.map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
|
|
8829
|
+
.filter(Boolean);
|
|
8802
8830
|
}
|
|
8803
8831
|
deref.setEnforceHttpsEnabled(__oldEnforceHttps);
|
|
8804
8832
|
return __out;
|
|
8805
8833
|
}
|
|
8806
8834
|
|
|
8807
8835
|
function reasonRdfJs(input, opts = {}) {
|
|
8808
|
-
const { dataFactory = null, ...restOpts } = opts || {};
|
|
8836
|
+
const { dataFactory = null, skipUnsupportedRdfJs = false, ...restOpts } = opts || {};
|
|
8809
8837
|
const rdfFactory = getDataFactory(dataFactory);
|
|
8810
8838
|
|
|
8811
8839
|
const queue = [];
|
|
@@ -8829,8 +8857,11 @@ function reasonRdfJs(input, opts = {}) {
|
|
|
8829
8857
|
...restOpts,
|
|
8830
8858
|
rdfjs: false,
|
|
8831
8859
|
onDerived: ({ df }) => {
|
|
8832
|
-
|
|
8833
|
-
|
|
8860
|
+
const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
8861
|
+
if (quad) {
|
|
8862
|
+
queue.push(quad);
|
|
8863
|
+
flush();
|
|
8864
|
+
}
|
|
8834
8865
|
},
|
|
8835
8866
|
});
|
|
8836
8867
|
} catch (e) {
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# =======================================================================
|
|
2
|
+
# matrix-mechanics.n3
|
|
3
|
+
#
|
|
4
|
+
# Purpose
|
|
5
|
+
# -------
|
|
6
|
+
# This is a small toy "matrix mechanics" example written in Eyeling-style
|
|
7
|
+
# Notation3. It models two 2x2 matrices:
|
|
8
|
+
#
|
|
9
|
+
# H = [[1,0],[0,2]]
|
|
10
|
+
# X = [[0,1],[1,0]]
|
|
11
|
+
#
|
|
12
|
+
# and derives:
|
|
13
|
+
# - trace(H)
|
|
14
|
+
# - det(H)
|
|
15
|
+
# - HX
|
|
16
|
+
# - XH
|
|
17
|
+
# - the commutator [H,X] = HX - XH
|
|
18
|
+
#
|
|
19
|
+
# What the example demonstrates
|
|
20
|
+
# -----------------------------
|
|
21
|
+
# 1. How to encode a small algebraic model as RDF-style facts.
|
|
22
|
+
# 2. How to derive new facts with N3 implication rules.
|
|
23
|
+
# 3. How to present conclusions with log:outputString.
|
|
24
|
+
# 4. How to add hard checks using `=> false`.
|
|
25
|
+
#
|
|
26
|
+
# Expected result
|
|
27
|
+
# ---------------
|
|
28
|
+
# - H has trace 3 and determinant 2.
|
|
29
|
+
# - X is an involution: X^2 = I.
|
|
30
|
+
# - [H,X] is non-zero, so H and X do not commute.
|
|
31
|
+
#
|
|
32
|
+
# Notes
|
|
33
|
+
# -----
|
|
34
|
+
# - This is a direct, concrete 2x2 example, not a general matrix library.
|
|
35
|
+
# - The structure follows the Eyeling handbook style:
|
|
36
|
+
# Facts -> Logic -> Presentation -> Checks
|
|
37
|
+
# - The output is organized in ARC form:
|
|
38
|
+
# Answer -> Reason Why -> Check
|
|
39
|
+
# =======================================================================
|
|
40
|
+
|
|
41
|
+
@prefix : <http://example.org/#> .
|
|
42
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
43
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#> .
|
|
44
|
+
@prefix string: <http://www.w3.org/2000/10/swap/string#> .
|
|
45
|
+
|
|
46
|
+
# -----
|
|
47
|
+
# Facts
|
|
48
|
+
# -----
|
|
49
|
+
|
|
50
|
+
:H :a11 1; :a12 0; :a21 0; :a22 2 .
|
|
51
|
+
:X :a11 0; :a12 1; :a21 1; :a22 0 .
|
|
52
|
+
|
|
53
|
+
# -----
|
|
54
|
+
# Logic
|
|
55
|
+
# -----
|
|
56
|
+
|
|
57
|
+
# trace(H) = H11 + H22
|
|
58
|
+
{
|
|
59
|
+
:H :a11 ?h11; :a22 ?h22 .
|
|
60
|
+
(?h11 ?h22) math:sum ?tr .
|
|
61
|
+
}
|
|
62
|
+
=>
|
|
63
|
+
{
|
|
64
|
+
:H :trace ?tr .
|
|
65
|
+
} .
|
|
66
|
+
|
|
67
|
+
# det(H) = H11*H22 - H12*H21
|
|
68
|
+
{
|
|
69
|
+
:H :a11 ?h11; :a12 ?h12; :a21 ?h21; :a22 ?h22 .
|
|
70
|
+
(?h11 ?h22) math:product ?p1 .
|
|
71
|
+
(?h12 ?h21) math:product ?p2 .
|
|
72
|
+
(?p1 ?p2) math:difference ?det .
|
|
73
|
+
}
|
|
74
|
+
=>
|
|
75
|
+
{
|
|
76
|
+
:H :det ?det .
|
|
77
|
+
} .
|
|
78
|
+
|
|
79
|
+
# HX = H * X
|
|
80
|
+
{
|
|
81
|
+
:H :a11 ?h11; :a12 ?h12; :a21 ?h21; :a22 ?h22 .
|
|
82
|
+
:X :a11 ?x11; :a12 ?x12; :a21 ?x21; :a22 ?x22 .
|
|
83
|
+
|
|
84
|
+
(?h11 ?x11) math:product ?hx11a .
|
|
85
|
+
(?h12 ?x21) math:product ?hx11b .
|
|
86
|
+
(?hx11a ?hx11b) math:sum ?hx11 .
|
|
87
|
+
|
|
88
|
+
(?h11 ?x12) math:product ?hx12a .
|
|
89
|
+
(?h12 ?x22) math:product ?hx12b .
|
|
90
|
+
(?hx12a ?hx12b) math:sum ?hx12 .
|
|
91
|
+
|
|
92
|
+
(?h21 ?x11) math:product ?hx21a .
|
|
93
|
+
(?h22 ?x21) math:product ?hx21b .
|
|
94
|
+
(?hx21a ?hx21b) math:sum ?hx21 .
|
|
95
|
+
|
|
96
|
+
(?h21 ?x12) math:product ?hx22a .
|
|
97
|
+
(?h22 ?x22) math:product ?hx22b .
|
|
98
|
+
(?hx22a ?hx22b) math:sum ?hx22 .
|
|
99
|
+
}
|
|
100
|
+
=>
|
|
101
|
+
{
|
|
102
|
+
:HX :a11 ?hx11; :a12 ?hx12; :a21 ?hx21; :a22 ?hx22 .
|
|
103
|
+
} .
|
|
104
|
+
|
|
105
|
+
# XH = X * H
|
|
106
|
+
{
|
|
107
|
+
:X :a11 ?x11; :a12 ?x12; :a21 ?x21; :a22 ?x22 .
|
|
108
|
+
:H :a11 ?h11; :a12 ?h12; :a21 ?h21; :a22 ?h22 .
|
|
109
|
+
|
|
110
|
+
(?x11 ?h11) math:product ?xh11a .
|
|
111
|
+
(?x12 ?h21) math:product ?xh11b .
|
|
112
|
+
(?xh11a ?xh11b) math:sum ?xh11 .
|
|
113
|
+
|
|
114
|
+
(?x11 ?h12) math:product ?xh12a .
|
|
115
|
+
(?x12 ?h22) math:product ?xh12b .
|
|
116
|
+
(?xh12a ?xh12b) math:sum ?xh12 .
|
|
117
|
+
|
|
118
|
+
(?x21 ?h11) math:product ?xh21a .
|
|
119
|
+
(?x22 ?h21) math:product ?xh21b .
|
|
120
|
+
(?xh21a ?xh21b) math:sum ?xh21 .
|
|
121
|
+
|
|
122
|
+
(?x21 ?h12) math:product ?xh22a .
|
|
123
|
+
(?x22 ?h22) math:product ?xh22b .
|
|
124
|
+
(?xh22a ?xh22b) math:sum ?xh22 .
|
|
125
|
+
}
|
|
126
|
+
=>
|
|
127
|
+
{
|
|
128
|
+
:XH :a11 ?xh11; :a12 ?xh12; :a21 ?xh21; :a22 ?xh22 .
|
|
129
|
+
} .
|
|
130
|
+
|
|
131
|
+
# XX = X * X
|
|
132
|
+
{
|
|
133
|
+
:X :a11 ?x11; :a12 ?x12; :a21 ?x21; :a22 ?x22 .
|
|
134
|
+
|
|
135
|
+
(?x11 ?x11) math:product ?xx11a .
|
|
136
|
+
(?x12 ?x21) math:product ?xx11b .
|
|
137
|
+
(?xx11a ?xx11b) math:sum ?xx11 .
|
|
138
|
+
|
|
139
|
+
(?x11 ?x12) math:product ?xx12a .
|
|
140
|
+
(?x12 ?x22) math:product ?xx12b .
|
|
141
|
+
(?xx12a ?xx12b) math:sum ?xx12 .
|
|
142
|
+
|
|
143
|
+
(?x21 ?x11) math:product ?xx21a .
|
|
144
|
+
(?x22 ?x21) math:product ?xx21b .
|
|
145
|
+
(?xx21a ?xx21b) math:sum ?xx21 .
|
|
146
|
+
|
|
147
|
+
(?x21 ?x12) math:product ?xx22a .
|
|
148
|
+
(?x22 ?x22) math:product ?xx22b .
|
|
149
|
+
(?xx22a ?xx22b) math:sum ?xx22 .
|
|
150
|
+
}
|
|
151
|
+
=>
|
|
152
|
+
{
|
|
153
|
+
:XX :a11 ?xx11; :a12 ?xx12; :a21 ?xx21; :a22 ?xx22 .
|
|
154
|
+
} .
|
|
155
|
+
|
|
156
|
+
# C = [H,X] = HX - XH
|
|
157
|
+
{
|
|
158
|
+
:HX :a11 ?hx11; :a12 ?hx12; :a21 ?hx21; :a22 ?hx22 .
|
|
159
|
+
:XH :a11 ?xh11; :a12 ?xh12; :a21 ?xh21; :a22 ?xh22 .
|
|
160
|
+
|
|
161
|
+
(?hx11 ?xh11) math:difference ?c11 .
|
|
162
|
+
(?hx12 ?xh12) math:difference ?c12 .
|
|
163
|
+
(?hx21 ?xh21) math:difference ?c21 .
|
|
164
|
+
(?hx22 ?xh22) math:difference ?c22 .
|
|
165
|
+
}
|
|
166
|
+
=>
|
|
167
|
+
{
|
|
168
|
+
:C :a11 ?c11; :a12 ?c12; :a21 ?c21; :a22 ?c22 .
|
|
169
|
+
} .
|
|
170
|
+
|
|
171
|
+
# Summary facts
|
|
172
|
+
{
|
|
173
|
+
:H :trace 3; :det 2 .
|
|
174
|
+
}
|
|
175
|
+
=>
|
|
176
|
+
{
|
|
177
|
+
:H :spectrumOk "yes" .
|
|
178
|
+
} .
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
:XX :a11 1; :a12 0; :a21 0; :a22 1 .
|
|
182
|
+
}
|
|
183
|
+
=>
|
|
184
|
+
{
|
|
185
|
+
:X :involution "yes" .
|
|
186
|
+
} .
|
|
187
|
+
|
|
188
|
+
{
|
|
189
|
+
:C :a11 ?v .
|
|
190
|
+
?v math:notEqualTo 0 .
|
|
191
|
+
}
|
|
192
|
+
=>
|
|
193
|
+
{
|
|
194
|
+
:C :nonzero "yes" .
|
|
195
|
+
} .
|
|
196
|
+
|
|
197
|
+
{
|
|
198
|
+
:C :a12 ?v .
|
|
199
|
+
?v math:notEqualTo 0 .
|
|
200
|
+
}
|
|
201
|
+
=>
|
|
202
|
+
{
|
|
203
|
+
:C :nonzero "yes" .
|
|
204
|
+
} .
|
|
205
|
+
|
|
206
|
+
{
|
|
207
|
+
:C :a21 ?v .
|
|
208
|
+
?v math:notEqualTo 0 .
|
|
209
|
+
}
|
|
210
|
+
=>
|
|
211
|
+
{
|
|
212
|
+
:C :nonzero "yes" .
|
|
213
|
+
} .
|
|
214
|
+
|
|
215
|
+
{
|
|
216
|
+
:C :a22 ?v .
|
|
217
|
+
?v math:notEqualTo 0 .
|
|
218
|
+
}
|
|
219
|
+
=>
|
|
220
|
+
{
|
|
221
|
+
:C :nonzero "yes" .
|
|
222
|
+
} .
|
|
223
|
+
|
|
224
|
+
{
|
|
225
|
+
:H :spectrumOk "yes" .
|
|
226
|
+
:X :involution "yes" .
|
|
227
|
+
:C :nonzero "yes" .
|
|
228
|
+
}
|
|
229
|
+
=>
|
|
230
|
+
{
|
|
231
|
+
:model :ok "yes" ;
|
|
232
|
+
:answer "In this toy matrix-mechanics model, the Hamiltonian has two discrete energy levels and does not commute with a second observable." .
|
|
233
|
+
} .
|
|
234
|
+
|
|
235
|
+
# -----------------------------------------------
|
|
236
|
+
# Presentation (ARC: Answer • Reason Why • Check)
|
|
237
|
+
# -----------------------------------------------
|
|
238
|
+
|
|
239
|
+
{
|
|
240
|
+
:model :answer ?txt .
|
|
241
|
+
}
|
|
242
|
+
=>
|
|
243
|
+
{
|
|
244
|
+
:s01 log:outputString "=== Answer ===\n" .
|
|
245
|
+
:s02 log:outputString ?txt .
|
|
246
|
+
:s03 log:outputString "\n\n=== Reason Why ===\n" .
|
|
247
|
+
} .
|
|
248
|
+
|
|
249
|
+
{
|
|
250
|
+
:H :a11 ?a11; :a12 ?a12; :a21 ?a21; :a22 ?a22 .
|
|
251
|
+
("H = [[" ?a11 "," ?a12 "],[" ?a21 "," ?a22 "]]\n")
|
|
252
|
+
string:concatenation ?line .
|
|
253
|
+
}
|
|
254
|
+
=>
|
|
255
|
+
{
|
|
256
|
+
:s04 log:outputString ?line .
|
|
257
|
+
} .
|
|
258
|
+
|
|
259
|
+
{
|
|
260
|
+
:X :a11 ?a11; :a12 ?a12; :a21 ?a21; :a22 ?a22 .
|
|
261
|
+
("X = [[" ?a11 "," ?a12 "],[" ?a21 "," ?a22 "]]\n")
|
|
262
|
+
string:concatenation ?line .
|
|
263
|
+
}
|
|
264
|
+
=>
|
|
265
|
+
{
|
|
266
|
+
:s05 log:outputString ?line .
|
|
267
|
+
} .
|
|
268
|
+
|
|
269
|
+
{
|
|
270
|
+
:HX :a11 ?a11; :a12 ?a12; :a21 ?a21; :a22 ?a22 .
|
|
271
|
+
("HX = [[" ?a11 "," ?a12 "],[" ?a21 "," ?a22 "]]\n")
|
|
272
|
+
string:concatenation ?line .
|
|
273
|
+
}
|
|
274
|
+
=>
|
|
275
|
+
{
|
|
276
|
+
:s06 log:outputString ?line .
|
|
277
|
+
} .
|
|
278
|
+
|
|
279
|
+
{
|
|
280
|
+
:XH :a11 ?a11; :a12 ?a12; :a21 ?a21; :a22 ?a22 .
|
|
281
|
+
("XH = [[" ?a11 "," ?a12 "],[" ?a21 "," ?a22 "]]\n")
|
|
282
|
+
string:concatenation ?line .
|
|
283
|
+
}
|
|
284
|
+
=>
|
|
285
|
+
{
|
|
286
|
+
:s07 log:outputString ?line .
|
|
287
|
+
} .
|
|
288
|
+
|
|
289
|
+
{
|
|
290
|
+
:C :a11 ?a11; :a12 ?a12; :a21 ?a21; :a22 ?a22 .
|
|
291
|
+
("[H,X] = [[" ?a11 "," ?a12 "],[" ?a21 "," ?a22 "]]\n")
|
|
292
|
+
string:concatenation ?line .
|
|
293
|
+
}
|
|
294
|
+
=>
|
|
295
|
+
{
|
|
296
|
+
:s08 log:outputString ?line .
|
|
297
|
+
} .
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
:model :ok "yes" .
|
|
301
|
+
}
|
|
302
|
+
=>
|
|
303
|
+
{
|
|
304
|
+
:s09 log:outputString "\n=== Check ===\n" .
|
|
305
|
+
} .
|
|
306
|
+
|
|
307
|
+
{
|
|
308
|
+
:H :spectrumOk "yes" .
|
|
309
|
+
}
|
|
310
|
+
=>
|
|
311
|
+
{
|
|
312
|
+
:s10 log:outputString "trace/determinant match energy levels: yes\n" .
|
|
313
|
+
} .
|
|
314
|
+
|
|
315
|
+
{
|
|
316
|
+
:X :involution "yes" .
|
|
317
|
+
}
|
|
318
|
+
=>
|
|
319
|
+
{
|
|
320
|
+
:s11 log:outputString "X^2 = I : yes\n" .
|
|
321
|
+
} .
|
|
322
|
+
|
|
323
|
+
{
|
|
324
|
+
:C :nonzero "yes" .
|
|
325
|
+
}
|
|
326
|
+
=>
|
|
327
|
+
{
|
|
328
|
+
:s12 log:outputString "[H,X] != 0 : yes\n" .
|
|
329
|
+
} .
|
|
330
|
+
|
|
331
|
+
# ------------------------------------------
|
|
332
|
+
# Checks (fail hard if the algebra is wrong)
|
|
333
|
+
# ------------------------------------------
|
|
334
|
+
|
|
335
|
+
{
|
|
336
|
+
:H :trace ?tr .
|
|
337
|
+
?tr math:notEqualTo 3 .
|
|
338
|
+
}
|
|
339
|
+
=> false .
|
|
340
|
+
|
|
341
|
+
{
|
|
342
|
+
:H :det ?det .
|
|
343
|
+
?det math:notEqualTo 2 .
|
|
344
|
+
}
|
|
345
|
+
=> false .
|
|
346
|
+
|
|
347
|
+
{
|
|
348
|
+
:XX :a11 ?v .
|
|
349
|
+
?v math:notEqualTo 1 .
|
|
350
|
+
}
|
|
351
|
+
=> false .
|
|
352
|
+
|
|
353
|
+
{
|
|
354
|
+
:XX :a12 ?v .
|
|
355
|
+
?v math:notEqualTo 0 .
|
|
356
|
+
}
|
|
357
|
+
=> false .
|
|
358
|
+
|
|
359
|
+
{
|
|
360
|
+
:XX :a21 ?v .
|
|
361
|
+
?v math:notEqualTo 0 .
|
|
362
|
+
}
|
|
363
|
+
=> false .
|
|
364
|
+
|
|
365
|
+
{
|
|
366
|
+
:XX :a22 ?v .
|
|
367
|
+
?v math:notEqualTo 1 .
|
|
368
|
+
}
|
|
369
|
+
=> false .
|
|
370
|
+
|
|
371
|
+
{
|
|
372
|
+
:C :a11 0; :a12 0; :a21 0; :a22 0 .
|
|
373
|
+
}
|
|
374
|
+
=> false .
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
=== Answer ===
|
|
2
|
+
In this toy matrix-mechanics model, the Hamiltonian has two discrete energy levels and does not commute with a second observable.
|
|
3
|
+
|
|
4
|
+
=== Reason Why ===
|
|
5
|
+
H = [[1,0],[0,2]]
|
|
6
|
+
X = [[0,1],[1,0]]
|
|
7
|
+
HX = [[0,1],[2,0]]
|
|
8
|
+
XH = [[0,2],[1,0]]
|
|
9
|
+
[H,X] = [[0,-1],[1,0]]
|
|
10
|
+
|
|
11
|
+
=== Check ===
|
|
12
|
+
trace/determinant match energy levels: yes
|
|
13
|
+
X^2 = I : yes
|
|
14
|
+
[H,X] != 0 : yes
|
package/eyeling.js
CHANGED
|
@@ -8679,6 +8679,23 @@ function forwardChainAndCollectLogQueryConclusions(
|
|
|
8679
8679
|
|
|
8680
8680
|
// (proof printing + log:outputString moved to lib/explain.js)
|
|
8681
8681
|
|
|
8682
|
+
function isUnsupportedRdfJsConversionError(err) {
|
|
8683
|
+
return (
|
|
8684
|
+
err instanceof TypeError &&
|
|
8685
|
+
typeof err.message === 'string' &&
|
|
8686
|
+
err.message.startsWith('Cannot convert N3-only term ')
|
|
8687
|
+
);
|
|
8688
|
+
}
|
|
8689
|
+
|
|
8690
|
+
function maybeTripleToRdfJsQuad(triple, rdfFactory, skipUnsupportedRdfJs) {
|
|
8691
|
+
try {
|
|
8692
|
+
return internalTripleToRdfJsQuad(triple, rdfFactory);
|
|
8693
|
+
} catch (err) {
|
|
8694
|
+
if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return null;
|
|
8695
|
+
throw err;
|
|
8696
|
+
}
|
|
8697
|
+
}
|
|
8698
|
+
|
|
8682
8699
|
function reasonStream(input, opts = {}) {
|
|
8683
8700
|
const {
|
|
8684
8701
|
baseIri = null,
|
|
@@ -8688,6 +8705,7 @@ function reasonStream(input, opts = {}) {
|
|
|
8688
8705
|
enforceHttps = false,
|
|
8689
8706
|
rdfjs = false,
|
|
8690
8707
|
dataFactory = null,
|
|
8708
|
+
skipUnsupportedRdfJs = false,
|
|
8691
8709
|
builtinModules = null,
|
|
8692
8710
|
} = opts;
|
|
8693
8711
|
|
|
@@ -8750,7 +8768,10 @@ function reasonStream(input, opts = {}) {
|
|
|
8750
8768
|
if (typeof onDerived === 'function') {
|
|
8751
8769
|
for (const qdf of queryDerived) {
|
|
8752
8770
|
const payload = { triple: tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
8753
|
-
if (rdfFactory)
|
|
8771
|
+
if (rdfFactory) {
|
|
8772
|
+
const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
8773
|
+
if (quad) payload.quad = quad;
|
|
8774
|
+
}
|
|
8754
8775
|
onDerived(payload);
|
|
8755
8776
|
}
|
|
8756
8777
|
}
|
|
@@ -8766,7 +8787,10 @@ function reasonStream(input, opts = {}) {
|
|
|
8766
8787
|
triple: tripleToN3(df.fact, prefixes),
|
|
8767
8788
|
df,
|
|
8768
8789
|
};
|
|
8769
|
-
if (rdfFactory)
|
|
8790
|
+
if (rdfFactory) {
|
|
8791
|
+
const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
8792
|
+
if (quad) payload.quad = quad;
|
|
8793
|
+
}
|
|
8770
8794
|
onDerived(payload);
|
|
8771
8795
|
}
|
|
8772
8796
|
},
|
|
@@ -8797,15 +8821,19 @@ function reasonStream(input, opts = {}) {
|
|
|
8797
8821
|
};
|
|
8798
8822
|
|
|
8799
8823
|
if (rdfFactory) {
|
|
8800
|
-
__out.closureQuads = closureTriples
|
|
8801
|
-
|
|
8824
|
+
__out.closureQuads = closureTriples
|
|
8825
|
+
.map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
|
|
8826
|
+
.filter(Boolean);
|
|
8827
|
+
__out.queryQuads = queryTriples
|
|
8828
|
+
.map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
|
|
8829
|
+
.filter(Boolean);
|
|
8802
8830
|
}
|
|
8803
8831
|
deref.setEnforceHttpsEnabled(__oldEnforceHttps);
|
|
8804
8832
|
return __out;
|
|
8805
8833
|
}
|
|
8806
8834
|
|
|
8807
8835
|
function reasonRdfJs(input, opts = {}) {
|
|
8808
|
-
const { dataFactory = null, ...restOpts } = opts || {};
|
|
8836
|
+
const { dataFactory = null, skipUnsupportedRdfJs = false, ...restOpts } = opts || {};
|
|
8809
8837
|
const rdfFactory = getDataFactory(dataFactory);
|
|
8810
8838
|
|
|
8811
8839
|
const queue = [];
|
|
@@ -8829,8 +8857,11 @@ function reasonRdfJs(input, opts = {}) {
|
|
|
8829
8857
|
...restOpts,
|
|
8830
8858
|
rdfjs: false,
|
|
8831
8859
|
onDerived: ({ df }) => {
|
|
8832
|
-
|
|
8833
|
-
|
|
8860
|
+
const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
8861
|
+
if (quad) {
|
|
8862
|
+
queue.push(quad);
|
|
8863
|
+
flush();
|
|
8864
|
+
}
|
|
8834
8865
|
},
|
|
8835
8866
|
});
|
|
8836
8867
|
} catch (e) {
|
package/index.d.ts
CHANGED
|
@@ -172,6 +172,7 @@ declare module 'eyeling' {
|
|
|
172
172
|
enforceHttps?: boolean;
|
|
173
173
|
rdfjs?: boolean;
|
|
174
174
|
dataFactory?: RdfJsDataFactory | null;
|
|
175
|
+
skipUnsupportedRdfJs?: boolean;
|
|
175
176
|
builtinModules?: string | string[];
|
|
176
177
|
onDerived?: (item: { triple: string; quad?: RdfJsQuad; df: any }) => void;
|
|
177
178
|
}
|
package/lib/engine.js
CHANGED
|
@@ -3198,6 +3198,23 @@ function forwardChainAndCollectLogQueryConclusions(
|
|
|
3198
3198
|
|
|
3199
3199
|
// (proof printing + log:outputString moved to lib/explain.js)
|
|
3200
3200
|
|
|
3201
|
+
function isUnsupportedRdfJsConversionError(err) {
|
|
3202
|
+
return (
|
|
3203
|
+
err instanceof TypeError &&
|
|
3204
|
+
typeof err.message === 'string' &&
|
|
3205
|
+
err.message.startsWith('Cannot convert N3-only term ')
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
function maybeTripleToRdfJsQuad(triple, rdfFactory, skipUnsupportedRdfJs) {
|
|
3210
|
+
try {
|
|
3211
|
+
return internalTripleToRdfJsQuad(triple, rdfFactory);
|
|
3212
|
+
} catch (err) {
|
|
3213
|
+
if (skipUnsupportedRdfJs && isUnsupportedRdfJsConversionError(err)) return null;
|
|
3214
|
+
throw err;
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3201
3218
|
function reasonStream(input, opts = {}) {
|
|
3202
3219
|
const {
|
|
3203
3220
|
baseIri = null,
|
|
@@ -3207,6 +3224,7 @@ function reasonStream(input, opts = {}) {
|
|
|
3207
3224
|
enforceHttps = false,
|
|
3208
3225
|
rdfjs = false,
|
|
3209
3226
|
dataFactory = null,
|
|
3227
|
+
skipUnsupportedRdfJs = false,
|
|
3210
3228
|
builtinModules = null,
|
|
3211
3229
|
} = opts;
|
|
3212
3230
|
|
|
@@ -3269,7 +3287,10 @@ function reasonStream(input, opts = {}) {
|
|
|
3269
3287
|
if (typeof onDerived === 'function') {
|
|
3270
3288
|
for (const qdf of queryDerived) {
|
|
3271
3289
|
const payload = { triple: tripleToN3(qdf.fact, prefixes), df: qdf };
|
|
3272
|
-
if (rdfFactory)
|
|
3290
|
+
if (rdfFactory) {
|
|
3291
|
+
const quad = maybeTripleToRdfJsQuad(qdf.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
3292
|
+
if (quad) payload.quad = quad;
|
|
3293
|
+
}
|
|
3273
3294
|
onDerived(payload);
|
|
3274
3295
|
}
|
|
3275
3296
|
}
|
|
@@ -3285,7 +3306,10 @@ function reasonStream(input, opts = {}) {
|
|
|
3285
3306
|
triple: tripleToN3(df.fact, prefixes),
|
|
3286
3307
|
df,
|
|
3287
3308
|
};
|
|
3288
|
-
if (rdfFactory)
|
|
3309
|
+
if (rdfFactory) {
|
|
3310
|
+
const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
3311
|
+
if (quad) payload.quad = quad;
|
|
3312
|
+
}
|
|
3289
3313
|
onDerived(payload);
|
|
3290
3314
|
}
|
|
3291
3315
|
},
|
|
@@ -3316,15 +3340,19 @@ function reasonStream(input, opts = {}) {
|
|
|
3316
3340
|
};
|
|
3317
3341
|
|
|
3318
3342
|
if (rdfFactory) {
|
|
3319
|
-
__out.closureQuads = closureTriples
|
|
3320
|
-
|
|
3343
|
+
__out.closureQuads = closureTriples
|
|
3344
|
+
.map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
|
|
3345
|
+
.filter(Boolean);
|
|
3346
|
+
__out.queryQuads = queryTriples
|
|
3347
|
+
.map((t) => maybeTripleToRdfJsQuad(t, rdfFactory, skipUnsupportedRdfJs))
|
|
3348
|
+
.filter(Boolean);
|
|
3321
3349
|
}
|
|
3322
3350
|
deref.setEnforceHttpsEnabled(__oldEnforceHttps);
|
|
3323
3351
|
return __out;
|
|
3324
3352
|
}
|
|
3325
3353
|
|
|
3326
3354
|
function reasonRdfJs(input, opts = {}) {
|
|
3327
|
-
const { dataFactory = null, ...restOpts } = opts || {};
|
|
3355
|
+
const { dataFactory = null, skipUnsupportedRdfJs = false, ...restOpts } = opts || {};
|
|
3328
3356
|
const rdfFactory = getDataFactory(dataFactory);
|
|
3329
3357
|
|
|
3330
3358
|
const queue = [];
|
|
@@ -3348,8 +3376,11 @@ function reasonRdfJs(input, opts = {}) {
|
|
|
3348
3376
|
...restOpts,
|
|
3349
3377
|
rdfjs: false,
|
|
3350
3378
|
onDerived: ({ df }) => {
|
|
3351
|
-
|
|
3352
|
-
|
|
3379
|
+
const quad = maybeTripleToRdfJsQuad(df.fact, rdfFactory, skipUnsupportedRdfJs);
|
|
3380
|
+
if (quad) {
|
|
3381
|
+
queue.push(quad);
|
|
3382
|
+
flush();
|
|
3383
|
+
}
|
|
3353
3384
|
},
|
|
3354
3385
|
});
|
|
3355
3386
|
} catch (e) {
|
package/package.json
CHANGED
package/test/api.test.js
CHANGED
|
@@ -1706,6 +1706,60 @@ _:x :hates { _:foo :making :mess }.
|
|
|
1706
1706
|
assert.equal(tc.quads[0].graph.termType, 'DefaultGraph');
|
|
1707
1707
|
},
|
|
1708
1708
|
},
|
|
1709
|
+
{
|
|
1710
|
+
name: '63a RDF/JS export: reasonRdfJs can skip N3-only derived triples',
|
|
1711
|
+
async run() {
|
|
1712
|
+
const ex = 'http://example.org/';
|
|
1713
|
+
const input = `@prefix : <${ex}>.
|
|
1714
|
+
:a :p :b.
|
|
1715
|
+
{ :a :p :b. } => { :x :holds { :a :p :b. }. :x :ok :yes. }.`;
|
|
1716
|
+
const quads = [];
|
|
1717
|
+
for await (const quad of reasonRdfJs(input, { skipUnsupportedRdfJs: true })) {
|
|
1718
|
+
quads.push(quad);
|
|
1719
|
+
}
|
|
1720
|
+
this.quads = quads;
|
|
1721
|
+
return quads.map((q) => `${q.subject.value} ${q.predicate.value} ${q.object.value}`).join('\n');
|
|
1722
|
+
},
|
|
1723
|
+
expect: [/http:\/\/example\.org\/ok/],
|
|
1724
|
+
notExpect: [/http:\/\/example\.org\/holds/],
|
|
1725
|
+
check(outputIgnored, tc) {
|
|
1726
|
+
assert.equal(tc.quads.length, 1, 'Expected one yielded RDF/JS quad after skipping GraphTerm output');
|
|
1727
|
+
assert.equal(tc.quads[0].predicate.value, 'http://example.org/ok');
|
|
1728
|
+
assert.equal(tc.quads[0].object.value, 'http://example.org/yes');
|
|
1729
|
+
},
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
name: '63b RDF/JS export: reasonStream keeps N3 closure while omitting unsupported closureQuads',
|
|
1733
|
+
run() {
|
|
1734
|
+
const ex = 'http://example.org/';
|
|
1735
|
+
const input = `@prefix : <${ex}>.
|
|
1736
|
+
:a :p :b.
|
|
1737
|
+
{ :a :p :b. } => { :x :holds { :a :p :b. }. :x :ok :yes. }.`;
|
|
1738
|
+
const seen = [];
|
|
1739
|
+
const result = reasonStream(input, {
|
|
1740
|
+
rdfjs: true,
|
|
1741
|
+
skipUnsupportedRdfJs: true,
|
|
1742
|
+
includeInputFactsInClosure: false,
|
|
1743
|
+
onDerived: ({ triple, quad }) => seen.push({ triple, quad }),
|
|
1744
|
+
});
|
|
1745
|
+
this.seen = seen;
|
|
1746
|
+
this.result = result;
|
|
1747
|
+
return result.closureN3;
|
|
1748
|
+
},
|
|
1749
|
+
expect: [/:holds/, /:ok/],
|
|
1750
|
+
check(outputIgnored, tc) {
|
|
1751
|
+
assert.equal(tc.seen.length, 2, 'Expected both derived facts to reach onDerived');
|
|
1752
|
+
assert.equal(tc.seen.filter((x) => x.quad).length, 1, 'Expected only one RDF/JS quad in onDerived');
|
|
1753
|
+
assert.ok(Array.isArray(tc.result.closureQuads), 'Expected closureQuads array');
|
|
1754
|
+
assert.equal(
|
|
1755
|
+
tc.result.closureQuads.length,
|
|
1756
|
+
1,
|
|
1757
|
+
'Expected unsupported GraphTerm triple to be omitted from closureQuads',
|
|
1758
|
+
);
|
|
1759
|
+
assert.equal(tc.result.closureQuads[0].predicate.value, 'http://example.org/ok');
|
|
1760
|
+
assert.match(tc.result.closureN3, /:holds/, 'Expected N3 closure to retain quoted-formula triple');
|
|
1761
|
+
},
|
|
1762
|
+
},
|
|
1709
1763
|
{
|
|
1710
1764
|
name: '64 RDF/JS validation: named-graph input quads are rejected clearly',
|
|
1711
1765
|
expectError: true,
|
package/test/playground.test.js
CHANGED
|
@@ -125,10 +125,14 @@ function findChromium() {
|
|
|
125
125
|
const CODEMIRROR_STUB = String.raw`(function(){
|
|
126
126
|
if (window.CodeMirror) return;
|
|
127
127
|
|
|
128
|
+
function normalizeText(text){
|
|
129
|
+
return String(text || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
128
132
|
function posToIndex(text, line, ch){
|
|
129
133
|
line = Math.max(0, line|0);
|
|
130
134
|
ch = Math.max(0, ch|0);
|
|
131
|
-
const norm =
|
|
135
|
+
const norm = normalizeText(text);
|
|
132
136
|
const lines = norm.split('\n');
|
|
133
137
|
if (lines.length === 0) return 0;
|
|
134
138
|
if (line >= lines.length) line = lines.length - 1;
|
|
@@ -152,30 +156,76 @@ const CODEMIRROR_STUB = String.raw`(function(){
|
|
|
152
156
|
var code = document.createElement('div');
|
|
153
157
|
code.className = 'CodeMirror-code';
|
|
154
158
|
|
|
155
|
-
var pre = document.createElement('pre');
|
|
156
|
-
pre.textContent = textarea.value || '';
|
|
157
|
-
|
|
158
|
-
code.appendChild(pre);
|
|
159
159
|
sizer.appendChild(code);
|
|
160
160
|
scroll.appendChild(sizer);
|
|
161
161
|
wrapper.appendChild(scroll);
|
|
162
162
|
|
|
163
|
-
return { wrapper: wrapper, scroll: scroll,
|
|
163
|
+
return { wrapper: wrapper, scroll: scroll, sizer: sizer, code: code };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function makeLineHandle(lineNo, text){
|
|
167
|
+
var wrap = document.createElement('div');
|
|
168
|
+
wrap.className = 'CodeMirror-linewrap';
|
|
169
|
+
wrap.dataset.lineNumber = String(lineNo + 1);
|
|
170
|
+
|
|
171
|
+
var bg = document.createElement('div');
|
|
172
|
+
bg.className = 'CodeMirror-linebackground';
|
|
173
|
+
|
|
174
|
+
var pre = document.createElement('pre');
|
|
175
|
+
pre.className = 'CodeMirror-line';
|
|
176
|
+
pre.textContent = text && text.length ? text : ' ';
|
|
177
|
+
|
|
178
|
+
wrap.appendChild(bg);
|
|
179
|
+
wrap.appendChild(pre);
|
|
180
|
+
return { lineNo: lineNo, wrap: wrap, bg: bg, pre: pre };
|
|
164
181
|
}
|
|
165
182
|
|
|
183
|
+
window.__cmStubsById = window.__cmStubsById || Object.create(null);
|
|
184
|
+
|
|
166
185
|
window.CodeMirror = {
|
|
167
186
|
fromTextArea: function(textarea/*, opts*/){
|
|
168
187
|
var obj = mkWrapper(textarea);
|
|
188
|
+
var listeners = Object.create(null);
|
|
189
|
+
var lineHandles = [];
|
|
190
|
+
var cursor = { line: 0, ch: 0 };
|
|
191
|
+
|
|
192
|
+
function emit(name){
|
|
193
|
+
var hs = listeners[name] || [];
|
|
194
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
|
195
|
+
for (var i = 0; i < hs.length; i++) {
|
|
196
|
+
try { hs[i].apply(null, args); } catch(_) {}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getText(){
|
|
201
|
+
return normalizeText(textarea.value || '');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function getLines(){
|
|
205
|
+
return getText().split('\n');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function render(){
|
|
209
|
+
var lines = getLines();
|
|
210
|
+
obj.code.innerHTML = '';
|
|
211
|
+
lineHandles = [];
|
|
212
|
+
for (var i = 0; i < lines.length; i++) {
|
|
213
|
+
var h = makeLineHandle(i, lines[i]);
|
|
214
|
+
lineHandles.push(h);
|
|
215
|
+
obj.code.appendChild(h.wrap);
|
|
216
|
+
}
|
|
217
|
+
if (!lineHandles.length) {
|
|
218
|
+
var h0 = makeLineHandle(0, '');
|
|
219
|
+
lineHandles.push(h0);
|
|
220
|
+
obj.code.appendChild(h0.wrap);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
169
224
|
try {
|
|
170
225
|
textarea.style.display = 'none';
|
|
171
226
|
textarea.parentNode.insertBefore(obj.wrapper, textarea.nextSibling);
|
|
172
227
|
} catch(_) {}
|
|
173
228
|
|
|
174
|
-
function sync(){ obj.pre.textContent = textarea.value || ''; }
|
|
175
|
-
function getLines(){
|
|
176
|
-
return String(textarea.value || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
229
|
const doc = {
|
|
180
230
|
posFromIndex: function(i){
|
|
181
231
|
i = Math.max(0, i|0);
|
|
@@ -190,9 +240,9 @@ const CODEMIRROR_STUB = String.raw`(function(){
|
|
|
190
240
|
}
|
|
191
241
|
};
|
|
192
242
|
|
|
193
|
-
|
|
243
|
+
const api = {
|
|
194
244
|
getValue: function(){ return textarea.value || ''; },
|
|
195
|
-
setValue: function(v){ textarea.value = String(v == null ? '' : v);
|
|
245
|
+
setValue: function(v){ textarea.value = String(v == null ? '' : v); render(); emit('change', api, { origin: 'setValue' }); },
|
|
196
246
|
|
|
197
247
|
// Methods used by demo.html's streaming appender
|
|
198
248
|
getScrollerElement: function(){ return obj.scroll; },
|
|
@@ -202,22 +252,37 @@ const CODEMIRROR_STUB = String.raw`(function(){
|
|
|
202
252
|
const cur = String(textarea.value || '');
|
|
203
253
|
const idx = posToIndex(cur, pos && pos.line, pos && pos.ch);
|
|
204
254
|
textarea.value = cur.slice(0, idx) + String(text == null ? '' : text) + cur.slice(idx);
|
|
205
|
-
|
|
255
|
+
render();
|
|
256
|
+
emit('change', api, { origin: '+input' });
|
|
206
257
|
},
|
|
207
258
|
|
|
208
259
|
// Misc methods used by layout / resizing code
|
|
209
260
|
refresh: function(){},
|
|
210
261
|
setSize: function(){},
|
|
211
262
|
setOption: function(){},
|
|
212
|
-
on: function(){},
|
|
263
|
+
on: function(name, fn){ if (!listeners[name]) listeners[name] = []; listeners[name].push(fn); },
|
|
213
264
|
operation: function(fn){ try{ fn(); } catch(_){} },
|
|
214
265
|
getWrapperElement: function(){ return obj.wrapper; },
|
|
215
266
|
getScrollInfo: function(){ return { height: 0, clientHeight: 0, top: 0 }; },
|
|
216
267
|
defaultTextHeight: function(){ return 17; },
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
268
|
+
lineCount: function(){ return lineHandles.length; },
|
|
269
|
+
getLineHandle: function(n){ return (n >= 0 && n < lineHandles.length) ? lineHandles[n] : null; },
|
|
270
|
+
scrollIntoView: function(){},
|
|
271
|
+
setCursor: function(pos){ cursor = { line: pos && pos.line || 0, ch: pos && pos.ch || 0 }; },
|
|
272
|
+
getCursor: function(){ return { line: cursor.line, ch: cursor.ch }; },
|
|
273
|
+
|
|
274
|
+
// Error highlighting hooks
|
|
275
|
+
addLineClass: function(handle, where, cls){
|
|
276
|
+
if (!handle || !cls) return handle;
|
|
277
|
+
if (where === 'background' && handle.bg) handle.bg.classList.add(cls);
|
|
278
|
+
if (handle.wrap) handle.wrap.classList.add(cls);
|
|
279
|
+
return handle;
|
|
280
|
+
},
|
|
281
|
+
removeLineClass: function(handle, where, cls){
|
|
282
|
+
if (!handle || !cls) return;
|
|
283
|
+
if (where === 'background' && handle.bg) handle.bg.classList.remove(cls);
|
|
284
|
+
if (handle.wrap) handle.wrap.classList.remove(cls);
|
|
285
|
+
},
|
|
221
286
|
clearGutter: function(){},
|
|
222
287
|
setGutterMarker: function(){},
|
|
223
288
|
|
|
@@ -225,6 +290,11 @@ const CODEMIRROR_STUB = String.raw`(function(){
|
|
|
225
290
|
getDoc: function(){ return doc; },
|
|
226
291
|
doc: doc
|
|
227
292
|
};
|
|
293
|
+
|
|
294
|
+
render();
|
|
295
|
+
textarea.__cmInstance = api;
|
|
296
|
+
window.__cmStubsById[textarea.id || ('cm-' + Object.keys(window.__cmStubsById).length)] = api;
|
|
297
|
+
return api;
|
|
228
298
|
}
|
|
229
299
|
};
|
|
230
300
|
})();`;
|
|
@@ -499,73 +569,167 @@ async function main() {
|
|
|
499
569
|
assert.ok(!nav.errorText, `demo.html navigation failed: ${nav.errorText}`);
|
|
500
570
|
await loadFired;
|
|
501
571
|
|
|
502
|
-
|
|
503
|
-
await cdp.send(
|
|
504
|
-
'Runtime.evaluate',
|
|
505
|
-
{
|
|
506
|
-
expression: `document.getElementById('run-btn') && document.getElementById('run-btn').click();`,
|
|
507
|
-
returnByValue: true,
|
|
508
|
-
},
|
|
509
|
-
sessionId,
|
|
510
|
-
);
|
|
511
|
-
|
|
512
|
-
// Wait for completion and capture output.
|
|
513
|
-
// The demo reports completion with status strings like:
|
|
514
|
-
// "Done. Derived: …", "Done (paused). …", or "Done. (Run N)".
|
|
515
|
-
let last = { status: '', output: '' };
|
|
516
|
-
const deadline = Date.now() + 60000;
|
|
517
|
-
|
|
518
|
-
while (Date.now() < deadline) {
|
|
519
|
-
// Fail fast on runtime exceptions (often indicates a broken CodeMirror stub or worker init).
|
|
520
|
-
if (exceptions.length) {
|
|
521
|
-
throw new Error(`Uncaught exception in demo.html: ${JSON.stringify(exceptions[0] || {})}`);
|
|
522
|
-
}
|
|
523
|
-
|
|
572
|
+
async function evalInPage(expression) {
|
|
524
573
|
const r = await cdp.send(
|
|
525
574
|
'Runtime.evaluate',
|
|
526
575
|
{
|
|
527
|
-
expression
|
|
528
|
-
const s = document.getElementById('status');
|
|
529
|
-
const o = document.getElementById('output-editor');
|
|
530
|
-
return { status: s ? String(s.textContent || '') : '', output: o ? String(o.value || '') : '' };
|
|
531
|
-
})()`,
|
|
576
|
+
expression,
|
|
532
577
|
returnByValue: true,
|
|
578
|
+
awaitPromise: true,
|
|
533
579
|
},
|
|
534
580
|
sessionId,
|
|
535
581
|
);
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const st = last && typeof last.status === 'string' ? last.status : '';
|
|
582
|
+
return r && r.result ? r.result.value : undefined;
|
|
583
|
+
}
|
|
539
584
|
|
|
540
|
-
|
|
541
|
-
if (
|
|
542
|
-
throw new Error(`
|
|
543
|
-
Output:
|
|
544
|
-
${last.output || ''}`);
|
|
585
|
+
function failFastOnExceptions() {
|
|
586
|
+
if (exceptions.length) {
|
|
587
|
+
throw new Error(`Uncaught exception in demo.html: ${JSON.stringify(exceptions[0] || {})}`);
|
|
545
588
|
}
|
|
589
|
+
}
|
|
546
590
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
591
|
+
async function getPlaygroundState() {
|
|
592
|
+
return (
|
|
593
|
+
(await evalInPage(`(() => {
|
|
594
|
+
const statusEl = document.getElementById('status');
|
|
595
|
+
const outputTa = document.getElementById('output-editor');
|
|
596
|
+
const inputCm = window.__cmStubsById && (window.__cmStubsById['n3-editor'] || window.__cmStubsById['input-editor']);
|
|
597
|
+
const outputCm = window.__cmStubsById && window.__cmStubsById['output-editor'];
|
|
598
|
+
const inputWrapper = inputCm && typeof inputCm.getWrapperElement === 'function' ? inputCm.getWrapperElement() : null;
|
|
599
|
+
const highlighted = inputWrapper
|
|
600
|
+
? Array.from(inputWrapper.querySelectorAll('.CodeMirror-linebackground.cm-error-line')).map((el) => {
|
|
601
|
+
const wrap = el.parentElement;
|
|
602
|
+
const pre = wrap && wrap.querySelector('pre');
|
|
603
|
+
return {
|
|
604
|
+
line: wrap && wrap.dataset && wrap.dataset.lineNumber ? Number(wrap.dataset.lineNumber) : null,
|
|
605
|
+
text: pre ? String(pre.textContent || '') : '',
|
|
606
|
+
};
|
|
607
|
+
})
|
|
608
|
+
: [];
|
|
609
|
+
return {
|
|
610
|
+
status: statusEl ? String(statusEl.textContent || '') : '',
|
|
611
|
+
output: outputCm && typeof outputCm.getValue === 'function'
|
|
612
|
+
? String(outputCm.getValue() || '')
|
|
613
|
+
: (outputTa ? String(outputTa.value || '') : ''),
|
|
614
|
+
highlighted,
|
|
615
|
+
};
|
|
616
|
+
})()`)) || { status: '', output: '', highlighted: [] }
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async function setProgram(text) {
|
|
621
|
+
const payload = JSON.stringify(String(text));
|
|
622
|
+
await evalInPage(`(() => {
|
|
623
|
+
const cm = window.__cmStubsById && (window.__cmStubsById['n3-editor'] || window.__cmStubsById['input-editor']);
|
|
624
|
+
if (cm && typeof cm.setValue === 'function') {
|
|
625
|
+
cm.setValue(${payload});
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
const ta = document.getElementById('n3-editor') || document.getElementById('input-editor');
|
|
629
|
+
if (!ta) throw new Error('n3-editor textarea not found');
|
|
630
|
+
ta.value = ${payload};
|
|
631
|
+
return true;
|
|
632
|
+
})()`);
|
|
633
|
+
}
|
|
554
634
|
|
|
555
|
-
|
|
635
|
+
async function clickRun() {
|
|
636
|
+
await evalInPage(`(() => {
|
|
637
|
+
const btn = document.getElementById('run-btn');
|
|
638
|
+
if (!btn) throw new Error('run-btn not found');
|
|
639
|
+
btn.click();
|
|
640
|
+
return true;
|
|
641
|
+
})()`);
|
|
556
642
|
}
|
|
557
643
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
644
|
+
async function waitForState(label, predicate, timeoutMs = 60000) {
|
|
645
|
+
const deadline = Date.now() + timeoutMs;
|
|
646
|
+
let last = { status: '', output: '', highlighted: [] };
|
|
647
|
+
while (Date.now() < deadline) {
|
|
648
|
+
failFastOnExceptions();
|
|
649
|
+
last = await getPlaygroundState();
|
|
650
|
+
if (predicate(last)) return last;
|
|
651
|
+
await sleep(100);
|
|
652
|
+
}
|
|
653
|
+
throw new Error(`Timed out waiting for ${label}. Last state:
|
|
654
|
+
${JSON.stringify(last, null, 2)}`);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const DEFAULT_PROGRAM_EXPECTS = [
|
|
658
|
+
[/Socrates/i, 'Expected output to mention Socrates'],
|
|
659
|
+
[/Mortal/i, 'Expected output to mention Mortal'],
|
|
660
|
+
];
|
|
661
|
+
const syntaxErrorProgram = `@prefix : <#> .
|
|
662
|
+
:alice :name "Ada" .
|
|
663
|
+
^
|
|
664
|
+
`;
|
|
665
|
+
const fuseProgram = fs.readFileSync(path.join(ROOT, 'examples', 'fuse.n3'), 'utf8');
|
|
666
|
+
const outputStringProgram = `@prefix : <#> .
|
|
667
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
668
|
+
:report log:outputString "Hello from output string\nLine 2\n" .
|
|
669
|
+
`;
|
|
670
|
+
|
|
671
|
+
// 1) Baseline smoke test: the default program runs to completion.
|
|
672
|
+
await clickRun();
|
|
673
|
+
const baseline = await waitForState(
|
|
674
|
+
'default program completion',
|
|
675
|
+
(st) =>
|
|
676
|
+
String(st.status || '')
|
|
562
677
|
.trim()
|
|
563
678
|
.startsWith('Done'),
|
|
564
|
-
|
|
679
|
+
60000,
|
|
565
680
|
);
|
|
566
|
-
assert.ok(
|
|
567
|
-
assert.match(
|
|
568
|
-
|
|
681
|
+
assert.ok(typeof baseline.output === 'string' && baseline.output.length > 0, 'Expected non-empty output');
|
|
682
|
+
for (const [re, msg] of DEFAULT_PROGRAM_EXPECTS) assert.match(baseline.output, re, msg);
|
|
683
|
+
ok('playground runs the default Socrates program');
|
|
684
|
+
|
|
685
|
+
// 2) N3 syntax errors should be shown in Output and highlight the offending line.
|
|
686
|
+
await setProgram(syntaxErrorProgram);
|
|
687
|
+
await clickRun();
|
|
688
|
+
const syntaxErr = await waitForState(
|
|
689
|
+
'syntax error reporting',
|
|
690
|
+
(st) => String(st.status || '').trim() === 'Error.' && /syntax error/i.test(String(st.output || '')),
|
|
691
|
+
20000,
|
|
692
|
+
);
|
|
693
|
+
assert.match(syntaxErr.output, /Syntax error in input\.n3:3:1:/i, 'Expected line/column in syntax error output');
|
|
694
|
+
assert.match(syntaxErr.output, /\n\^\s*$/m, 'Expected caret line in syntax error output');
|
|
695
|
+
assert.equal(syntaxErr.highlighted[0].line, 3, 'Expected line 3 to be highlighted');
|
|
696
|
+
assert.equal(syntaxErr.highlighted[0].text, '^', 'Expected highlighted line text to match the broken line');
|
|
697
|
+
ok('playground shows syntax errors in Output and highlights the offending line');
|
|
698
|
+
|
|
699
|
+
// 3) Inference fuse output should be visible in the Output pane.
|
|
700
|
+
await setProgram(fuseProgram);
|
|
701
|
+
await clickRun();
|
|
702
|
+
const fuse = await waitForState(
|
|
703
|
+
'inference fuse reporting',
|
|
704
|
+
(st) =>
|
|
705
|
+
String(st.status || '')
|
|
706
|
+
.trim()
|
|
707
|
+
.startsWith('Done') && /Inference fuse triggered/i.test(String(st.output || '')),
|
|
708
|
+
30000,
|
|
709
|
+
);
|
|
710
|
+
assert.match(fuse.output, /Inference fuse triggered\./i, 'Expected fuse message in Output');
|
|
711
|
+
assert.match(fuse.output, /Fired rule:/i, 'Expected fired rule explanation in Output');
|
|
712
|
+
assert.match(fuse.output, /Matched instance:/i, 'Expected matched instance in Output');
|
|
713
|
+
ok('playground clearly shows inference fuse output');
|
|
714
|
+
|
|
715
|
+
// 4) log:outputString should render as clean text, not raw triples.
|
|
716
|
+
await setProgram(outputStringProgram);
|
|
717
|
+
await clickRun();
|
|
718
|
+
const rendered = await waitForState(
|
|
719
|
+
'log:outputString rendering',
|
|
720
|
+
(st) =>
|
|
721
|
+
String(st.status || '')
|
|
722
|
+
.trim()
|
|
723
|
+
.startsWith('Done') && /Hello from output string/.test(String(st.output || '')),
|
|
724
|
+
20000,
|
|
725
|
+
);
|
|
726
|
+
assert.match(rendered.output, /^Hello from output string\nLine 2\n?$/m, 'Expected rendered outputString text');
|
|
727
|
+
assert.doesNotMatch(
|
|
728
|
+
rendered.output,
|
|
729
|
+
/:report\s+log:outputString\s+"|# Derived triples/i,
|
|
730
|
+
'Expected clean rendered output without raw triples',
|
|
731
|
+
);
|
|
732
|
+
ok('playground renders log:outputString cleanly in Output');
|
|
569
733
|
|
|
570
734
|
// Ensure no uncaught runtime exceptions.
|
|
571
735
|
assert.equal(exceptions.length, 0, `Uncaught exceptions in demo.html: ${JSON.stringify(exceptions[0] || {})}`);
|
|
@@ -578,8 +742,6 @@ ${last.output || ''}`);
|
|
|
578
742
|
try {
|
|
579
743
|
await cdp.send('Browser.close');
|
|
580
744
|
} catch (_) {}
|
|
581
|
-
|
|
582
|
-
ok('demo.html loads and runs the default program');
|
|
583
745
|
} finally {
|
|
584
746
|
await cleanup();
|
|
585
747
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|