eyelang 1.2.1 → 1.2.2

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 CHANGED
@@ -451,7 +451,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
451
451
  | [`turing.pl`](../examples/turing.pl) | Simulates a binary-increment Turing machine. | [`output/turing.pl`](../examples/output/turing.pl) |
452
452
  | [`vector-similarity.pl`](../examples/vector-similarity.pl) | Computes dot product, norm, and cosine similarity. | [`output/vector-similarity.pl`](../examples/output/vector-similarity.pl) |
453
453
  | [`vulnerability-impact.pl`](../examples/vulnerability-impact.pl) | Analyzes vulnerable transitive dependencies and urgent patch impact. | [`output/vulnerability-impact.pl`](../examples/output/vulnerability-impact.pl) |
454
- | [`web-names.pl`](../examples/web-names.pl) | Uses compact `web(Space, Local)` terms as readable global names and expands selected names to URIs. | [`output/web-names.pl`](../examples/output/web-names.pl) |
454
+ | [`web-names.pl`](../examples/web-names.pl) | Uses compact ISO-compatible `web(Space, Local)` terms as readable global identifiers and expands selected terms to URIs. | [`output/web-names.pl`](../examples/output/web-names.pl) |
455
455
  | [`weighted-interval-scheduling.pl`](../examples/weighted-interval-scheduling.pl) | Selects the best non-overlapping weighted intervals with memoized dynamic programming. | [`output/weighted-interval-scheduling.pl`](../examples/output/weighted-interval-scheduling.pl) |
456
456
  | [`witch.pl`](../examples/witch.pl) | Derives the classic “burn the witch” rule chain. | [`output/witch.pl`](../examples/output/witch.pl) |
457
457
  | [`wolf-goat-cabbage.pl`](../examples/wolf-goat-cabbage.pl) | Solves the wolf-goat-cabbage river crossing. | [`output/wolf-goat-cabbage.pl`](../examples/output/wolf-goat-cabbage.pl) |
@@ -119,7 +119,7 @@ The punctuation tokens are:
119
119
  ( ) [ ] , | . :-
120
120
  ```
121
121
 
122
- A colon outside `:-` is not part of the language. Namespace-like names SHOULD be written as plain atom constants such as `person_type`, `odrl_permission`, or dotted atom constants such as `org.schema`.
122
+ A colon outside `:-` is not part of the language. Namespace-like names SHOULD be written as ISO-compatible atom constants such as `person_type`, `odrl_permission`, or quoted atoms such as `'org.schema'`.
123
123
 
124
124
  ### 3.4 Variables
125
125
 
@@ -138,15 +138,15 @@ Each `_` anonymous variable occurrence is fresh.
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. For compact globally scoped names, eyelang also permits dot-separated plain atom segments, such as `be.ugent`, `org.schema`, or `eyereasoner.github`. A dot ends a clause only when it is not followed by another lowercase-starting segment. Names such as `a-b` or `http://example` MUST still be quoted if they are meant as one atom constant:
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 in the ISO-compatible subset; 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` or `http://example` MUST also be quoted if they are meant as one atom constant:
142
142
 
143
143
  ```prolog
144
144
  pat
145
145
  type
146
146
  case_123
147
- be.ugent
148
- org.schema
149
- eyereasoner.github
147
+ 'be.ugent'
148
+ 'org.schema'
149
+ 'eyereasoner.github'
150
150
  'a-b'
151
151
  'http://example'
152
152
  ```
@@ -1,10 +1,10 @@
1
- web_uri(web(be.ugent, josd), "https://data.ugent.be/id/josd").
2
- web_uri(web(com.example, josd), "https://example.com/id/josd").
3
- web_uri(web(be.ugent, idlab), "https://data.ugent.be/id/idlab").
4
- web_uri(web(be.ugent, ugent), "https://data.ugent.be/id/ugent").
5
- web_uri(web(eyereasoner.github, eyelang), "https://github.com/eyereasoner/eyelang").
6
- web_uri(web(org.schema, maintainer), "https://schema.org/maintainer").
7
- affiliated_with(web(be.ugent, josd), web(be.ugent, idlab)).
8
- affiliated_with(web(be.ugent, josd), web(be.ugent, ugent)).
9
- project_contact(web(eyereasoner.github, eyelang), web(be.ugent, josd), "josderoo@gmail.com").
10
- same_local_name(web(be.ugent, josd), web(com.example, josd), josd).
1
+ web_uri(web('be.ugent', josd), "https://data.ugent.be/id/josd").
2
+ web_uri(web('com.example', josd), "https://example.com/id/josd").
3
+ web_uri(web('be.ugent', idlab), "https://data.ugent.be/id/idlab").
4
+ web_uri(web('be.ugent', ugent), "https://data.ugent.be/id/ugent").
5
+ web_uri(web('eyereasoner.github', eyelang), "https://github.com/eyereasoner/eyelang").
6
+ web_uri(web('org.schema', maintainer), "https://schema.org/maintainer").
7
+ affiliated_with(web('be.ugent', josd), web('be.ugent', idlab)).
8
+ affiliated_with(web('be.ugent', josd), web('be.ugent', ugent)).
9
+ project_contact(web('eyereasoner.github', eyelang), web('be.ugent', josd), "josderoo@gmail.com").
10
+ same_local_name(web('be.ugent', josd), web('com.example', josd), josd).
@@ -2,13 +2,13 @@
2
2
  %
3
3
  % RDF-style URIs are globally meaningful but long, while QNames such as
4
4
  % schema:Person depend on an external prefix declaration. This example uses an
5
- % eyelang-native alternative: web(Space, Local). The first argument is a dotted
6
- % atom that names a vocabulary, organization, or authority; the second argument
7
- % is the local name inside that space.
5
+ % eyelang-native alternative: web(Space, Local). The first argument is an
6
+ % ISO-compatible quoted atom naming a vocabulary, organization, or authority;
7
+ % the second argument is the local name inside that space.
8
8
  %
9
9
  % The important property is that the complete term is self-contained. The local
10
- % name josd can safely occur in two spaces: web(be.ugent, josd) and
11
- % web(com.example, josd) are different Herbrand terms, so there is no hidden
10
+ % name josd can safely occur in two spaces: web('be.ugent', josd) and
11
+ % web('com.example', josd) are different Herbrand terms, so there is no hidden
12
12
  % prefix context and no accidental collision. Tooling can still expand selected
13
13
  % web/2 terms to full URI strings when a base is known.
14
14
 
@@ -18,32 +18,32 @@ materialize(project_contact, 3).
18
18
  materialize(same_local_name, 3).
19
19
 
20
20
  % Optional URI bases for spaces that we want to publish or display.
21
- space_base(be.ugent, "https://data.ugent.be/id/").
22
- space_base(com.example, "https://example.com/id/").
23
- space_base(eyereasoner.github, "https://github.com/eyereasoner/").
24
- space_base(org.schema, "https://schema.org/").
21
+ space_base('be.ugent', "https://data.ugent.be/id/").
22
+ space_base('com.example', "https://example.com/id/").
23
+ space_base('eyereasoner.github', "https://github.com/eyereasoner/").
24
+ space_base('org.schema', "https://schema.org/").
25
25
 
26
- % A tiny graph using globally scoped web names as ordinary eyelang terms.
27
- triple(web(be.ugent, josd), web(org.schema, name), "Jos De Roo").
28
- triple(web(be.ugent, josd), web(org.schema, email), "josderoo@gmail.com").
29
- triple(web(be.ugent, josd), web(org.schema, affiliation), web(be.ugent, idlab)).
30
- triple(web(be.ugent, idlab), web(org.schema, parentOrganization), web(be.ugent, ugent)).
26
+ % A tiny graph using globally scoped web terms as ordinary eyelang terms.
27
+ triple(web('be.ugent', josd), web('org.schema', name), "Jos De Roo").
28
+ triple(web('be.ugent', josd), web('org.schema', email), "josderoo@gmail.com").
29
+ triple(web('be.ugent', josd), web('org.schema', affiliation), web('be.ugent', idlab)).
30
+ triple(web('be.ugent', idlab), web('org.schema', parentOrganization), web('be.ugent', ugent)).
31
31
 
32
- triple(web(eyereasoner.github, eyelang), web(org.schema, name), "eyelang").
33
- triple(web(eyereasoner.github, eyelang), web(org.schema, codeRepository), "https://github.com/eyereasoner/eyelang").
34
- triple(web(eyereasoner.github, eyelang), web(org.schema, maintainer), web(be.ugent, josd)).
32
+ triple(web('eyereasoner.github', eyelang), web('org.schema', name), "eyelang").
33
+ triple(web('eyereasoner.github', eyelang), web('org.schema', codeRepository), "https://github.com/eyereasoner/eyelang").
34
+ triple(web('eyereasoner.github', eyelang), web('org.schema', maintainer), web('be.ugent', josd)).
35
35
 
36
36
  % Same local spelling, different global identity.
37
- triple(web(com.example, josd), web(org.schema, name), "Another JosD in another space").
37
+ triple(web('com.example', josd), web('org.schema', name), "Another JosD in another space").
38
38
 
39
39
  % Keep URI expansion explicit and optional: reasoning uses web/2 terms, while
40
40
  % web_uri/2 is only a presentation bridge for selected names.
41
- published_name(web(be.ugent, josd)).
42
- published_name(web(com.example, josd)).
43
- published_name(web(be.ugent, idlab)).
44
- published_name(web(be.ugent, ugent)).
45
- published_name(web(eyereasoner.github, eyelang)).
46
- published_name(web(org.schema, maintainer)).
41
+ published_name(web('be.ugent', josd)).
42
+ published_name(web('com.example', josd)).
43
+ published_name(web('be.ugent', idlab)).
44
+ published_name(web('be.ugent', ugent)).
45
+ published_name(web('eyereasoner.github', eyelang)).
46
+ published_name(web('org.schema', maintainer)).
47
47
 
48
48
  web_uri(web(Space, Local), URI) :-
49
49
  published_name(web(Space, Local)),
@@ -53,28 +53,28 @@ web_uri(web(Space, Local), URI) :-
53
53
 
54
54
  % Organization membership follows parentOrganization links transitively.
55
55
  parent_organization(Unit, Org) :-
56
- triple(Unit, web(org.schema, parentOrganization), Org).
56
+ triple(Unit, web('org.schema', parentOrganization), Org).
57
57
  parent_organization(Unit, Org) :-
58
- triple(Unit, web(org.schema, parentOrganization), Mid),
58
+ triple(Unit, web('org.schema', parentOrganization), Mid),
59
59
  parent_organization(Mid, Org).
60
60
 
61
61
  affiliated_with(Person, Org) :-
62
- triple(Person, web(org.schema, affiliation), Org).
62
+ triple(Person, web('org.schema', affiliation), Org).
63
63
  affiliated_with(Person, Org) :-
64
- triple(Person, web(org.schema, affiliation), Unit),
64
+ triple(Person, web('org.schema', affiliation), Unit),
65
65
  parent_organization(Unit, Org).
66
66
 
67
67
  % A project contact is derived by joining the project's maintainer with the
68
68
  % maintainer's email. The join works because both facts use the same complete
69
- % web(be.ugent, josd) term.
69
+ % web('be.ugent', josd) term.
70
70
  project_contact(Project, Person, Email) :-
71
- triple(Project, web(org.schema, maintainer), Person),
72
- triple(Person, web(org.schema, email), Email).
71
+ triple(Project, web('org.schema', maintainer), Person),
72
+ triple(Person, web('org.schema', email), Email).
73
73
 
74
74
  % Demonstrate that local names are not global names. This reports the deliberate
75
75
  % local-name collision without treating the two people as equal.
76
76
  local_name(Entity, Local) :-
77
- triple(Entity, web(org.schema, name), _),
77
+ triple(Entity, web('org.schema', name), _),
78
78
  eq(Entity, web(_, Local)).
79
79
 
80
80
  same_local_name(A, B, Local) :-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "A small Prolog-syntax-subset logic programming language for rules, goals, answers, and proofs.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/parser.js CHANGED
@@ -23,9 +23,6 @@ function isNameContinueCode(code) {
23
23
  return code === 95 || isAsciiLetterCode(code) || isDigitCode(code);
24
24
  }
25
25
 
26
- function isDottedAtomContinue(source, pos) {
27
- return source[pos] === '.' && isPlainAtomStartCode((source[pos + 1] ?? '').charCodeAt(0));
28
- }
29
26
 
30
27
  function isVariableStart(text) {
31
28
  const code = text.charCodeAt(0);
@@ -155,18 +152,7 @@ class Parser {
155
152
  if (isPlainAtomStartCode(ch.charCodeAt(0))) {
156
153
  const start = this.pos;
157
154
  this.take();
158
- while (true) {
159
- if (isNameContinueCode(this.peek().charCodeAt(0))) {
160
- this.take();
161
- continue;
162
- }
163
- if (isDottedAtomContinue(this.source, this.pos)) {
164
- this.take();
165
- this.take();
166
- continue;
167
- }
168
- break;
169
- }
155
+ while (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
170
156
  return { type: TOK.ATOM, text: this.source.slice(start, this.pos), line };
171
157
  }
172
158
 
@@ -331,7 +317,7 @@ const SIMPLE_NUMBER = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
331
317
  const FAST_BINARY_FACT = /^([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\.$/;
332
318
  const FAST_BINARY_RULE = /^([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\s*:-\s*([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\.$/;
333
319
  const SIMPLE_VARIABLE = /^[_A-Z][A-Za-z0-9_]*$/;
334
- const SIMPLE_ATOM = /^[a-z][A-Za-z0-9_]*(?:\.[a-z][A-Za-z0-9_]*)*$/;
320
+ const SIMPLE_ATOM = /^[a-z][A-Za-z0-9_]*$/;
335
321
  const GRAPHIC_ATOM = /^[#$&*+\-\/<=>?@^~\\]+$/;
336
322
 
337
323
  function parseClausesFastNoSource(source) {
package/src/term.js CHANGED
@@ -138,7 +138,7 @@ const graphicAtomChars = new Set('#$&*+-/<=>?@^~\\'.split(''));
138
138
  function atomNeedsQuotes(name) {
139
139
  if (!name) return true;
140
140
  if (name === '[]') return false;
141
- if (/^[a-z][A-Za-z0-9_]*(?:\.[a-z][A-Za-z0-9_]*)*$/.test(name)) return false;
141
+ if (/^[a-z][A-Za-z0-9_]*$/.test(name)) return false;
142
142
  for (const ch of name) if (!graphicAtomChars.has(ch)) return true;
143
143
  return false;
144
144
  }
@@ -477,10 +477,18 @@ function whiteBoxCases() {
477
477
  },
478
478
  },
479
479
  {
480
- name: 'parser preserves dotted atom constants for web-style terms',
480
+ name: 'parser rejects unquoted dotted atoms to stay ISO-compatible',
481
481
  run: () => {
482
- const clauses = parseProgramText('p(web(be.ugent, josd), org.schema).\n');
483
- assertEqual(termToString(clauses[0].head, new Env(), true), 'p(web(be.ugent, josd), org.schema)', 'head');
482
+ let threw = false;
483
+ try { parseProgramText('p(web(be.ugent, josd)).\n'); } catch (_) { threw = true; }
484
+ assertEqual(threw, true, 'unquoted dotted atoms must be quoted');
485
+ },
486
+ },
487
+ {
488
+ name: 'parser preserves quoted dotted atoms for web-style terms',
489
+ run: () => {
490
+ const clauses = parseProgramText("p(web('be.ugent', josd), 'org.schema').\n");
491
+ assertEqual(termToString(clauses[0].head, new Env(), true), "p(web('be.ugent', josd), 'org.schema')", 'head');
484
492
  },
485
493
  },
486
494
  {