eyelang 1.3.0 → 1.3.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/docs/guide.md +1 -1
- package/docs/language-reference.md +14 -3
- package/package.json +1 -1
- package/src/parser.js +28 -0
- package/src/term.js +9 -0
- package/test/conformance/cases/103_angle_iri_atoms.eye +7 -0
- package/test/conformance/expected/103_angle_iri_atoms.eye +3 -0
- package/test/run-regression.mjs +21 -0
package/docs/guide.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This guide introduces Eyelang, a small Horn-clause language and engine whose source syntax is Prolog-like but deliberately its own compact language for rules, goals, answers, and proofs. Eyelang works over ordinary terms, lists, arithmetic, strings, and finite search. Run it with the `eyelang` CLI, or use `node bin/eyelang.js` when working directly from a source checkout.
|
|
4
4
|
|
|
5
|
-
Programs write relations directly, for example `ancestor(pat, emma)` or `status(case1, accepted)`. Eyelang output is ordinary Eyelang syntax: by default, the CLI materializes selected answer facts and prints those facts only. Pass `--proof` (or `-p`) when you also want each answer followed by a `why/2` explanation fact that records the proof. Programs may add `materialize/2` declarations such as `materialize(answer, 2).` to focus output on selected predicates.
|
|
5
|
+
Programs write relations directly, for example `ancestor(pat, emma)` or `status(case1, accepted)`. Absolute IRI atoms can be written explicitly with angle brackets, for example `<https://schema.org/name>`, when a program needs web identifiers without prefix declarations. Eyelang output is ordinary Eyelang syntax: by default, the CLI materializes selected answer facts and prints those facts only. Pass `--proof` (or `-p`) when you also want each answer followed by a `why/2` explanation fact that records the proof. Programs may add `materialize/2` declarations such as `materialize(answer, 2).` to focus output on selected predicates.
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
For the normative language definition, including lexical syntax, terms, clauses, goals, built-ins, `table/2`, `materialize/2`, and conformance boundaries, read the [Eyelang language reference](language-reference.md).
|
|
@@ -138,7 +138,7 @@ Each `?_` anonymous variable occurrence is fresh. A bare `_` is not a variable i
|
|
|
138
138
|
|
|
139
139
|
### 3.5 Atom constants
|
|
140
140
|
|
|
141
|
-
A plain atom constant starts with a lowercase ASCII letter and is followed by zero or more ASCII letters, digits, or underscores. A dot is not part of a plain atom; dotted web spaces such as `'be.ugent'` or `'org.schema'` MUST be quoted if they are meant as one atom constant. Names such as `a-b`
|
|
141
|
+
A plain atom constant starts with a lowercase ASCII letter and is followed by zero or more ASCII letters, digits, or underscores. A dot is not part of a plain atom; dotted web spaces such as `'be.ugent'` or `'org.schema'` MUST be quoted if they are meant as one atom constant. Names such as `a-b` MUST also be quoted if they are meant as one atom constant:
|
|
142
142
|
|
|
143
143
|
```eyelang
|
|
144
144
|
pat
|
|
@@ -148,9 +148,18 @@ case_123
|
|
|
148
148
|
'org.schema'
|
|
149
149
|
'eyereasoner.github'
|
|
150
150
|
'a-b'
|
|
151
|
-
'http://example'
|
|
152
151
|
```
|
|
153
152
|
|
|
153
|
+
Absolute IRI atom constants MAY also be written between angle brackets. The content must be an absolute IRI-like lexical value with a scheme such as `https:` or `urn:`. Eyelang stores the content as the atom value and prints absolute IRI atoms with angle brackets on read-back:
|
|
154
|
+
|
|
155
|
+
```eyelang
|
|
156
|
+
<https://example.org/alice>
|
|
157
|
+
<urn:example:bob>
|
|
158
|
+
triple(<https://example.org/alice>, <https://schema.org/name>, "Alice").
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Quoted absolute IRI atoms remain valid input, but read-back normalizes them to angle-bracket syntax. For example, `'https://example.org/alice'` prints as `<https://example.org/alice>`.
|
|
162
|
+
|
|
154
163
|
A quoted atom constant is enclosed in single quotes. A single quote inside a quoted atom constant is represented by doubling it:
|
|
155
164
|
|
|
156
165
|
```eyelang
|
|
@@ -165,6 +174,8 @@ A graphic atom constant is one or more graphic characters from this set:
|
|
|
165
174
|
#$&*+-/<=>?@^~\
|
|
166
175
|
```
|
|
167
176
|
|
|
177
|
+
Angle-bracket IRI syntax is recognized only for absolute IRI-like contents. Graphic atoms such as `<=>`, `<`, and `>=` remain graphic atoms.
|
|
178
|
+
|
|
168
179
|
### 3.6 Strings
|
|
169
180
|
|
|
170
181
|
A string is enclosed in double quotes. The implementation supports common escapes such as `\n`, `\t`, `\"`, and `\\`.
|
|
@@ -659,7 +670,7 @@ eyelang source is intended to be familiar to Prolog readers, but eyelang is not
|
|
|
659
670
|
- no variables in functor or predicate position;
|
|
660
671
|
- no occurs check in unification.
|
|
661
672
|
|
|
662
|
-
Programs intended to be portable to eyelang SHOULD use `?` variables, avoid ISO-specific syntax, and keep terms explicit. Atom names that are not plain lowercase-starting names
|
|
673
|
+
Programs intended to be portable to eyelang SHOULD use `?` variables, avoid ISO-specific syntax, and keep terms explicit. Atom names that are not plain lowercase-starting names, graphic atom tokens, or angle-bracket absolute IRI atoms SHOULD be written as quoted atoms, for example `'a-b'`.
|
|
663
674
|
|
|
664
675
|
## 16. Examples
|
|
665
676
|
|
package/package.json
CHANGED
package/src/parser.js
CHANGED
|
@@ -7,6 +7,22 @@ const TOK = {
|
|
|
7
7
|
LPAREN: '(', RPAREN: ')', LBRACKET: '[', RBRACKET: ']', COMMA: ',', BAR: '|', DOT: '.', IF: ':-'
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
function isAbsoluteIriText(text) {
|
|
11
|
+
return /^[A-Za-z][A-Za-z0-9+.-]*:[^\s<>"'{}|\\^`]*$/.test(text);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function hasAngleIriToken(source, pos) {
|
|
15
|
+
if (source[pos] !== '<') return false;
|
|
16
|
+
let text = '';
|
|
17
|
+
for (let i = pos + 1; i < source.length; i++) {
|
|
18
|
+
const ch = source[i];
|
|
19
|
+
if (ch === '>') return text.length > 0 && isAbsoluteIriText(text);
|
|
20
|
+
if (ch === '\n' || ch === '\r' || /\s/.test(ch) || ch === '<') return false;
|
|
21
|
+
text += ch;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
10
26
|
function isWhitespaceCode(code) {
|
|
11
27
|
return code === 32 || code === 9 || code === 10 || code === 13 || code === 12 || code === 11;
|
|
12
28
|
}
|
|
@@ -97,6 +113,18 @@ class Parser {
|
|
|
97
113
|
}
|
|
98
114
|
if (ch === ':') throw new Error('colon names are not supported; use name or prefix_name');
|
|
99
115
|
|
|
116
|
+
if (hasAngleIriToken(this.source, this.pos)) {
|
|
117
|
+
this.take(); // <
|
|
118
|
+
let text = '';
|
|
119
|
+
while (true) {
|
|
120
|
+
if (!this.peek()) throw new Error(`parse line ${line}: unterminated IRI`);
|
|
121
|
+
const value = this.take();
|
|
122
|
+
if (value === '>') break;
|
|
123
|
+
text += value;
|
|
124
|
+
}
|
|
125
|
+
return { type: TOK.ATOM, text, line };
|
|
126
|
+
}
|
|
127
|
+
|
|
100
128
|
if (ch === '"' || ch === "'") {
|
|
101
129
|
const quote = this.take();
|
|
102
130
|
let text = '';
|
package/src/term.js
CHANGED
|
@@ -135,6 +135,14 @@ export function termIsGround(term, env = new Env()) {
|
|
|
135
135
|
|
|
136
136
|
const graphicAtomChars = new Set('#$&*+-/<=>?@^~\\'.split(''));
|
|
137
137
|
|
|
138
|
+
function isAbsoluteIriText(text) {
|
|
139
|
+
return /^[A-Za-z][A-Za-z0-9+.-]*:[^\s<>"'{}|\\^`]*$/.test(text);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function writeAngleIri(name) {
|
|
143
|
+
return `<${name}>`;
|
|
144
|
+
}
|
|
145
|
+
|
|
138
146
|
function atomNeedsQuotes(name) {
|
|
139
147
|
if (!name) return true;
|
|
140
148
|
if (name === '[]') return false;
|
|
@@ -156,6 +164,7 @@ function quoteAtom(name) {
|
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
function writeAtom(name) {
|
|
167
|
+
if (isAbsoluteIriText(name)) return writeAngleIri(name);
|
|
159
168
|
return atomNeedsQuotes(name) ? quoteAtom(name) : name;
|
|
160
169
|
}
|
|
161
170
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
% Reference 3.5: absolute IRIs may be written as angle-bracket atom constants.
|
|
2
|
+
materialize(iri_subject, 1).
|
|
3
|
+
materialize(iri_object, 1).
|
|
4
|
+
triple(<https://example.org/alice>, <https://schema.org/name>, "Alice").
|
|
5
|
+
triple(<urn:example:bob>, <https://schema.org/knows>, <https://example.org/alice>).
|
|
6
|
+
iri_subject(?iri) :- triple(?iri, ?_, ?_).
|
|
7
|
+
iri_object(?iri) :- triple(?_, <https://schema.org/knows>, ?iri).
|
package/test/run-regression.mjs
CHANGED
|
@@ -529,6 +529,27 @@ function whiteBoxCases() {
|
|
|
529
529
|
assertEqual(termToString(clauses[0].head, new Env(), true), "p(web('be.ugent', josd), 'org.schema')", 'head');
|
|
530
530
|
},
|
|
531
531
|
},
|
|
532
|
+
{
|
|
533
|
+
name: 'parser accepts angle-bracket absolute IRI atoms',
|
|
534
|
+
run: () => {
|
|
535
|
+
const clauses = parseProgramText('p(<https://example.org/alice>, <urn:example:bob>).\n');
|
|
536
|
+
assertEqual(termToString(clauses[0].head, new Env(), true), 'p(<https://example.org/alice>, <urn:example:bob>)', 'head');
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
name: 'readback prints absolute IRI atoms with angle brackets',
|
|
541
|
+
run: () => {
|
|
542
|
+
const clauses = parseProgramText("p('https://example.org/alice').\n");
|
|
543
|
+
assertEqual(termToString(clauses[0].head, new Env(), true), 'p(<https://example.org/alice>)', 'head');
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: 'angle IRI syntax does not steal graphic atom syntax',
|
|
548
|
+
run: () => {
|
|
549
|
+
const clauses = parseProgramText('p(<=>).\n');
|
|
550
|
+
assertEqual(termToString(clauses[0].head, new Env(), true), 'p(<=>)', 'head');
|
|
551
|
+
},
|
|
552
|
+
},
|
|
532
553
|
{
|
|
533
554
|
name: 'list construction round-trips through properListItems',
|
|
534
555
|
run: () => {
|