eyelang 1.1.19 → 1.2.0

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
@@ -302,10 +302,8 @@ Use `holds/2` when you want to match the member term directly, for example `name
302
302
 
303
303
  ## Example catalog
304
304
 
305
- Each example has a checked golden output in `examples/output`.
306
-
307
- | Example | What it demonstrates | Golden output |
308
- |---|---|---|
305
+ | Example | Description | Golden output |
306
+ | --- | --- | --- |
309
307
  | [`access-control-policy.pl`](../examples/access-control-policy.pl) | Evaluates role and condition based access decisions. | [`output/access-control-policy.pl`](../examples/output/access-control-policy.pl) |
310
308
  | [`ackermann.pl`](../examples/ackermann.pl) | Computes Ackermann-style hyperoperation values. | [`output/ackermann.pl`](../examples/output/ackermann.pl) |
311
309
  | [`age.pl`](../examples/age.pl) | Checks whether people meet age thresholds. | [`output/age.pl`](../examples/output/age.pl) |
@@ -385,6 +383,7 @@ Each example has a checked golden output in `examples/output`.
385
383
  | [`gdpr-compliance.pl`](../examples/gdpr-compliance.pl) | Checks GDPR-style processing compliance. | [`output/gdpr-compliance.pl`](../examples/output/gdpr-compliance.pl) |
386
384
  | [`good-cobbler.pl`](../examples/good-cobbler.pl) | Demonstrates term-level structure with a good-cobbler statement. | [`output/good-cobbler.pl`](../examples/output/good-cobbler.pl) |
387
385
  | [`gps.pl`](../examples/gps.pl) | Finds and verifies route paths. | [`output/gps.pl`](../examples/output/gps.pl) |
386
+ | [`graph.pl`](../examples/graph.pl) | Derives transitive paths over French-city road links while showing the productive recursive rule order. | [`output/graph.pl`](../examples/output/graph.pl) |
388
387
  | [`graph-reachability.pl`](../examples/graph-reachability.pl) | Derives reachable nodes in a graph. | [`output/graph-reachability.pl`](../examples/output/graph-reachability.pl) |
389
388
  | [`gray-code-counter.pl`](../examples/gray-code-counter.pl) | Generates Gray-code counter states. | [`output/gray-code-counter.pl`](../examples/output/gray-code-counter.pl) |
390
389
  | [`greatest-lower-bound-uniqueness.pl`](../examples/greatest-lower-bound-uniqueness.pl) | Shows uniqueness of greatest lower bounds in a finite order instance. | [`output/greatest-lower-bound-uniqueness.pl`](../examples/output/greatest-lower-bound-uniqueness.pl) |
@@ -452,10 +451,12 @@ Each example has a checked golden output in `examples/output`.
452
451
  | [`turing.pl`](../examples/turing.pl) | Simulates a binary-increment Turing machine. | [`output/turing.pl`](../examples/output/turing.pl) |
453
452
  | [`vector-similarity.pl`](../examples/vector-similarity.pl) | Computes dot product, norm, and cosine similarity. | [`output/vector-similarity.pl`](../examples/output/vector-similarity.pl) |
454
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) |
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) |
458
458
  | [`zebra.pl`](../examples/zebra.pl) | Solves the zebra logic puzzle. | [`output/zebra.pl`](../examples/output/zebra.pl) |
459
+
459
460
  ## Golden outputs, tests, and conformance
460
461
 
461
462
  Golden answer outputs live in [`examples/output`](../examples/output). `npm run test:eyelang` covers the eyelang integration check, conformance cases, regression checks, runnable examples, and proof-output examples. A curated proof-output suite for `.pl` examples lives in [`examples/proof`](../examples/proof). Example tests pin `local_time/1` to `2026-05-30` so date-dependent examples stay deterministic. Regenerate them after an intentional output or explanation change:
@@ -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` or `odrl_permission`.
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`.
123
123
 
124
124
  ### 3.4 Variables
125
125
 
@@ -138,12 +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. This follows the usual Prolog unquoted-atom shape; names such as `a-b` or `http://example` MUST 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. 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:
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
150
  'a-b'
148
151
  'http://example'
149
152
  ```
@@ -655,7 +658,7 @@ eyelang source is intended to be a subset of familiar Prolog term and Horn-claus
655
658
  - no variables in functor or predicate position;
656
659
  - no occurs check in unification.
657
660
 
658
- Programs intended to be portable to eyelang SHOULD avoid ISO-specific syntax and keep terms explicit. Atom names that are not plain lowercase-starting names or graphic atom tokens SHOULD be written as quoted atoms, for example `'a-b'`.
661
+ Programs intended to be portable to eyelang SHOULD avoid ISO-specific syntax and keep terms explicit. Atom names that are not plain lowercase-starting names, dot-separated plain names, or graphic atom tokens SHOULD be written as quoted atoms, for example `'a-b'`.
659
662
 
660
663
  ## 16. Examples
661
664
 
@@ -8,7 +8,8 @@
8
8
  % The example derives arithmetic identities, polar conversions, powers, roots,
9
9
  % exponential/trigonometric functions, and distance/normalization results from
10
10
  % a small complex-number toolkit.
11
- materialize(is, 2).
11
+ materialize(complex_power, 4).
12
+ materialize(complex_function, 4).
12
13
 
13
14
  % Program structure: facts set up the scenario, and rules derive the materialized conclusions.
14
15
  pi(3.141592653589793).
@@ -110,17 +111,23 @@ complex_dial(X, Y, T, Tp) :-
110
111
  mul(Pi, 2, Z1),
111
112
  sub(Z1, T, Tp).
112
113
 
113
- is(test, (
114
- complex_exponentiation(input(exponentiation, [-1, 0], [0.5, 0]), C1),
115
- complex_exponentiation(input(exponentiation, [2.718281828459045, 0], [0, 3.141592653589793]), C2),
116
- complex_exponentiation(input(exponentiation, [0, 1], [0, 1]), C3),
117
- complex_exponentiation(input(exponentiation, [2.718281828459045, 0], [-1.57079632679, 0]), C4),
118
- complex_asin(input(asin, [2, 0]), C5),
119
- complex_acos(input(acos, [2, 0]), C6)
120
- )) :-
121
- complex_exponentiation([-1, 0], [0.5, 0], C1),
122
- complex_exponentiation([2.718281828459045, 0], [0, 3.141592653589793], C2),
123
- complex_exponentiation([0, 1], [0, 1], C3),
124
- complex_exponentiation([2.718281828459045, 0], [-1.57079632679, 0], C4),
125
- complex_asin([2, 0], C5),
126
- complex_acos([2, 0], C6).
114
+ % Named result rows keep the example output readable. Each row records the
115
+ % operation name, the input value(s), and the computed complex result rather
116
+ % than packing all assertions into one large nested term.
117
+ complex_power(sqrt_minus_one, [-1, 0], [0.5, 0], Result) :-
118
+ complex_exponentiation([-1, 0], [0.5, 0], Result).
119
+
120
+ complex_power(e_to_i_pi, [2.718281828459045, 0], [0, 3.141592653589793], Result) :-
121
+ complex_exponentiation([2.718281828459045, 0], [0, 3.141592653589793], Result).
122
+
123
+ complex_power(i_to_i, [0, 1], [0, 1], Result) :-
124
+ complex_exponentiation([0, 1], [0, 1], Result).
125
+
126
+ complex_power(e_to_minus_pi_over_two, [2.718281828459045, 0], [-1.57079632679, 0], Result) :-
127
+ complex_exponentiation([2.718281828459045, 0], [-1.57079632679, 0], Result).
128
+
129
+ complex_function(asin, two, [2, 0], Result) :-
130
+ complex_asin([2, 0], Result).
131
+
132
+ complex_function(acos, two, [2, 0], Result) :-
133
+ complex_acos([2, 0], Result).
@@ -0,0 +1,35 @@
1
+ % Transitive graph paths over a small map of French cities.
2
+ %
3
+ % The base relation is directed: oneway(A, B) means there is a road from A
4
+ % to B. The derived relation path(A, B) is the transitive closure: B is
5
+ % reachable from A by one or more directed legs.
6
+ %
7
+ % The recursive rule is written in a productive, right-recursive form:
8
+ %
9
+ % path(A, C) :- oneway(A, B), path(B, C).
10
+ %
11
+ % That order matters in a goal-directed reasoner. A left-recursive closure
12
+ % rule such as `path(A, C) :- path(A, B), path(B, C).` starts by asking for
13
+ % the same open relation it is currently proving, so eyelang's recursion guard
14
+ % must stop it to avoid an infinite loop. The result is under-generation: only
15
+ % direct edges are printed. Starting with the concrete generator `oneway/2`
16
+ % first makes each recursive step smaller and yields the complete closure.
17
+
18
+ materialize(path, 2).
19
+
20
+ oneway(paris, orleans).
21
+ oneway(paris, chartres).
22
+ oneway(paris, amiens).
23
+ oneway(orleans, blois).
24
+ oneway(orleans, bourges).
25
+ oneway(blois, tours).
26
+ oneway(chartres, lemans).
27
+ oneway(lemans, angers).
28
+ oneway(lemans, tours).
29
+ oneway(angers, nantes).
30
+
31
+ path(A, B) :-
32
+ oneway(A, B).
33
+ path(A, C) :-
34
+ oneway(A, B),
35
+ path(B, C).
@@ -1 +1,6 @@
1
- is(test, (complex_exponentiation(input(exponentiation, [-1, 0], [0.5, 0]), [6.123233995736766e-17, 1.0]), complex_exponentiation(input(exponentiation, [2.718281828459045, 0], [0, 3.141592653589793]), [-1.0, 1.2246467991473532e-16]), complex_exponentiation(input(exponentiation, [0, 1], [0, 1]), [0.20787957635076193, 0.0]), complex_exponentiation(input(exponentiation, [2.718281828459045, 0], [-1.57079632679, 0]), [0.20787957635177984, 0.0]), complex_asin(input(asin, [2, 0]), [1.5707963267948966, 1.3169578969248166]), complex_acos(input(acos, [2, 0]), [0.0, -1.3169578969248166]))).
1
+ complex_power(sqrt_minus_one, [-1, 0], [0.5, 0], [6.123233995736766e-17, 1.0]).
2
+ complex_power(e_to_i_pi, [2.718281828459045, 0], [0, 3.141592653589793], [-1.0, 1.2246467991473532e-16]).
3
+ complex_power(i_to_i, [0, 1], [0, 1], [0.20787957635076193, 0.0]).
4
+ complex_power(e_to_minus_pi_over_two, [2.718281828459045, 0], [-1.57079632679, 0], [0.20787957635177984, 0.0]).
5
+ complex_function(asin, two, [2, 0], [1.5707963267948966, 1.3169578969248166]).
6
+ complex_function(acos, two, [2, 0], [0.0, -1.3169578969248166]).
@@ -0,0 +1,21 @@
1
+ path(paris, orleans).
2
+ path(paris, chartres).
3
+ path(paris, amiens).
4
+ path(orleans, blois).
5
+ path(orleans, bourges).
6
+ path(blois, tours).
7
+ path(chartres, lemans).
8
+ path(lemans, angers).
9
+ path(lemans, tours).
10
+ path(angers, nantes).
11
+ path(paris, blois).
12
+ path(paris, bourges).
13
+ path(paris, tours).
14
+ path(paris, lemans).
15
+ path(paris, angers).
16
+ path(paris, nantes).
17
+ path(orleans, tours).
18
+ path(chartres, angers).
19
+ path(chartres, tours).
20
+ path(chartres, nantes).
21
+ path(lemans, nantes).
@@ -0,0 +1,10 @@
1
+ web_uri(web(be.ugent, jos), "https://data.ugent.be/id/jos").
2
+ web_uri(web(com.example, jos), "https://example.com/id/jos").
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, jos), web(be.ugent, idlab)).
8
+ affiliated_with(web(be.ugent, jos), web(be.ugent, ugent)).
9
+ project_contact(web(eyereasoner.github, eyelang), web(be.ugent, jos), "josderoo@gmail.com").
10
+ same_local_name(web(be.ugent, jos), web(com.example, jos), jos).
@@ -0,0 +1,83 @@
1
+ % Compact web-style names without full URIs or global prefix tables.
2
+ %
3
+ % RDF-style URIs are globally meaningful but long, while QNames such as
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.
8
+ %
9
+ % The important property is that the complete term is self-contained. The local
10
+ % name jos can safely occur in two spaces: web(be.ugent, jos) and
11
+ % web(com.example, jos) are different Herbrand terms, so there is no hidden
12
+ % prefix context and no accidental collision. Tooling can still expand selected
13
+ % web/2 terms to full URI strings when a base is known.
14
+
15
+ materialize(web_uri, 2).
16
+ materialize(affiliated_with, 2).
17
+ materialize(project_contact, 3).
18
+ materialize(same_local_name, 3).
19
+
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/").
25
+
26
+ % A tiny graph using globally scoped web names as ordinary eyelang terms.
27
+ triple(web(be.ugent, jos), web(org.schema, name), "Jos De Roo").
28
+ triple(web(be.ugent, jos), web(org.schema, email), "josderoo@gmail.com").
29
+ triple(web(be.ugent, jos), web(org.schema, affiliation), web(be.ugent, idlab)).
30
+ triple(web(be.ugent, idlab), web(org.schema, parentOrganization), web(be.ugent, ugent)).
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, jos)).
35
+
36
+ % Same local spelling, different global identity.
37
+ triple(web(com.example, jos), web(org.schema, name), "Another Jos in another space").
38
+
39
+ % Keep URI expansion explicit and optional: reasoning uses web/2 terms, while
40
+ % web_uri/2 is only a presentation bridge for selected names.
41
+ published_name(web(be.ugent, jos)).
42
+ published_name(web(com.example, jos)).
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
+
48
+ web_uri(web(Space, Local), URI) :-
49
+ published_name(web(Space, Local)),
50
+ space_base(Space, Base),
51
+ atom_string(Local, LocalText),
52
+ str_concat(Base, LocalText, URI).
53
+
54
+ % Organization membership follows parentOrganization links transitively.
55
+ parent_organization(Unit, Org) :-
56
+ triple(Unit, web(org.schema, parentOrganization), Org).
57
+ parent_organization(Unit, Org) :-
58
+ triple(Unit, web(org.schema, parentOrganization), Mid),
59
+ parent_organization(Mid, Org).
60
+
61
+ affiliated_with(Person, Org) :-
62
+ triple(Person, web(org.schema, affiliation), Org).
63
+ affiliated_with(Person, Org) :-
64
+ triple(Person, web(org.schema, affiliation), Unit),
65
+ parent_organization(Unit, Org).
66
+
67
+ % A project contact is derived by joining the project's maintainer with the
68
+ % maintainer's email. The join works because both facts use the same complete
69
+ % web(be.ugent, jos) term.
70
+ project_contact(Project, Person, Email) :-
71
+ triple(Project, web(org.schema, maintainer), Person),
72
+ triple(Person, web(org.schema, email), Email).
73
+
74
+ % Demonstrate that local names are not global names. This reports the deliberate
75
+ % local-name collision without treating the two people as equal.
76
+ local_name(Entity, Local) :-
77
+ triple(Entity, web(org.schema, name), _),
78
+ eq(Entity, web(_, Local)).
79
+
80
+ same_local_name(A, B, Local) :-
81
+ local_name(A, Local),
82
+ local_name(B, Local),
83
+ lt(A, B).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "1.1.19",
3
+ "version": "1.2.0",
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/playground.html CHANGED
@@ -434,156 +434,158 @@
434
434
 
435
435
  <script type="module">
436
436
  const EXAMPLES = [
437
- "access-control-policy",
438
- "ackermann",
439
- "age",
440
- "aliases-and-namespaces",
441
- "alignment-demo",
442
- "allen-interval-calculus",
443
- "ancestor",
444
- "animal",
445
- "annotation",
446
- "auroracare",
447
- "backward",
448
- "basic-monadic",
449
- "bayes-diagnosis",
450
- "bayes-therapy",
451
- "beam-deflection",
452
- "binomial-vandermonde",
453
- "blocks-world-planning",
454
- "bmi",
455
- "braking-safety-worlds",
456
- "buck-converter-design",
457
- "cache-performance",
458
- "canary-release",
459
- "cat-koko",
460
- "catalan-convolution",
461
- "chart-parser",
462
- "clinical-trial-screening",
463
- "collatz-1000",
464
- "combinatorics-findall-sort",
465
- "competitive-enzyme-kinetics",
466
- "complex",
467
- "composition-of-injective-functions-is-injective",
468
- "context-association",
469
- "context-schema-audit",
470
- "continued-fraction-sqrt2",
471
- "control-system",
472
- "critical-path-schedule",
473
- "cyclic-path",
474
- "d3-group",
475
- "dairy-energy-balance",
476
- "data-negotiation",
477
- "deep-taxonomy-10",
478
- "deep-taxonomy-100",
479
- "deep-taxonomy-1000",
480
- "deep-taxonomy-10000",
481
- "deep-taxonomy-100000",
482
- "delfour",
483
- "deontic-logic",
484
- "derived-backward-rule",
485
- "derived-rule",
486
- "diamond-property",
487
- "dijkstra",
488
- "dijkstra-findall-sort",
489
- "dijkstra-risk-path",
490
- "dining-philosophers",
491
- "dog",
492
- "dpv-odrl-purpose-mapping",
493
- "drone-corridor-planner",
494
- "easter-computus",
495
- "electrical-rc-filter",
496
- "epidemic-policy",
497
- "equivalence-classes-overlap-implies-same-class",
498
- "eulerian-path",
499
- "ev-range-worlds",
500
- "existential-rule",
501
- "exoplanet-validation-worlds",
502
- "expression-eval",
503
- "family-cousins",
504
- "fastpow",
505
- "fft8-numeric",
506
- "fibonacci",
507
- "field-nitrogen-balance",
508
- "flandor",
509
- "floating-point",
510
- "four-color-map",
511
- "fundamental-theorem-arithmetic",
512
- "gd-step-certified",
513
- "gdpr-compliance",
514
- "good-cobbler",
515
- "gps",
516
- "graph-reachability",
517
- "gray-code-counter",
518
- "greatest-lower-bound-uniqueness",
519
- "group-inverse-uniqueness",
520
- "hamiltonian-path",
521
- "hamming-code",
522
- "hanoi",
523
- "heat-loss",
524
- "heron-theorem",
525
- "ideal-gas-law",
526
- "illegitimate-reasoning",
527
- "integer-partitions",
528
- "intuitionistic-logic-kripke",
529
- "job-shop-scheduling",
530
- "knapsack-optimization",
531
- "knowledge-engineering-alignment-flow",
532
- "law-of-cosines",
533
- "least-squares-regression",
534
- "linear-logic-resources",
535
- "list-collection",
536
- "lldm",
537
- "manufacturing-quality-control",
538
- "matrix-chain-order",
539
- "microgrid-dispatch",
540
- "missionaries-cannibals",
541
- "modal-logic-kripke",
542
- "modular-exponentiation",
543
- "monkey-bananas",
544
- "n-queens-8",
545
- "network-sla",
546
- "newton-raphson",
547
- "nixon-diamond",
548
- "observability-log-correlation",
549
- "odrl-dpv-fpv-trust-flow",
550
- "odrl-dpv-healthcare-risk-ranked",
551
- "odrl-dpv-risk-ranked",
552
- "orbital-transfer-design",
553
- "path-discovery",
554
- "peano-arithmetic",
555
- "peasant",
556
- "pell-equation",
557
- "pendulum-period",
558
- "polynomial",
559
- "proof-contrapositive",
560
- "quadratic-formula",
561
- "radioactive-decay",
562
- "reusable-builtins",
563
- "riemann-hypothesis",
564
- "security-incident-correlation",
565
- "send-more-money",
566
- "service-impact",
567
- "sieve",
568
- "skolem-functions",
569
- "socket-age",
570
- "socket-family",
571
- "socrates",
572
- "stable-marriage",
573
- "statistics-summary",
574
- "stirling-bell-numbers",
575
- "sudoku-4x4",
576
- "superdense-coding",
577
- "term-tools",
578
- "totient-summatory",
579
- "trust-flow-provenance-threshold",
580
- "turing",
581
- "vector-similarity",
582
- "vulnerability-impact",
583
- "weighted-interval-scheduling",
584
- "witch",
585
- "wolf-goat-cabbage",
586
- "zebra"
437
+ "access-control-policy",
438
+ "ackermann",
439
+ "age",
440
+ "aliases-and-namespaces",
441
+ "alignment-demo",
442
+ "allen-interval-calculus",
443
+ "ancestor",
444
+ "animal",
445
+ "annotation",
446
+ "auroracare",
447
+ "backward",
448
+ "basic-monadic",
449
+ "bayes-diagnosis",
450
+ "bayes-therapy",
451
+ "beam-deflection",
452
+ "binomial-vandermonde",
453
+ "blocks-world-planning",
454
+ "bmi",
455
+ "braking-safety-worlds",
456
+ "buck-converter-design",
457
+ "cache-performance",
458
+ "canary-release",
459
+ "cat-koko",
460
+ "catalan-convolution",
461
+ "chart-parser",
462
+ "clinical-trial-screening",
463
+ "collatz-1000",
464
+ "combinatorics-findall-sort",
465
+ "competitive-enzyme-kinetics",
466
+ "complex",
467
+ "composition-of-injective-functions-is-injective",
468
+ "context-association",
469
+ "context-schema-audit",
470
+ "continued-fraction-sqrt2",
471
+ "control-system",
472
+ "critical-path-schedule",
473
+ "cyclic-path",
474
+ "d3-group",
475
+ "dairy-energy-balance",
476
+ "data-negotiation",
477
+ "deep-taxonomy-10",
478
+ "deep-taxonomy-100",
479
+ "deep-taxonomy-1000",
480
+ "deep-taxonomy-10000",
481
+ "deep-taxonomy-100000",
482
+ "delfour",
483
+ "deontic-logic",
484
+ "derived-backward-rule",
485
+ "derived-rule",
486
+ "diamond-property",
487
+ "dijkstra",
488
+ "dijkstra-findall-sort",
489
+ "dijkstra-risk-path",
490
+ "dining-philosophers",
491
+ "dog",
492
+ "dpv-odrl-purpose-mapping",
493
+ "drone-corridor-planner",
494
+ "easter-computus",
495
+ "electrical-rc-filter",
496
+ "epidemic-policy",
497
+ "equivalence-classes-overlap-implies-same-class",
498
+ "eulerian-path",
499
+ "ev-range-worlds",
500
+ "existential-rule",
501
+ "exoplanet-validation-worlds",
502
+ "expression-eval",
503
+ "family-cousins",
504
+ "fastpow",
505
+ "fft8-numeric",
506
+ "fibonacci",
507
+ "field-nitrogen-balance",
508
+ "flandor",
509
+ "floating-point",
510
+ "four-color-map",
511
+ "fundamental-theorem-arithmetic",
512
+ "gd-step-certified",
513
+ "gdpr-compliance",
514
+ "good-cobbler",
515
+ "gps",
516
+ "graph",
517
+ "graph-reachability",
518
+ "gray-code-counter",
519
+ "greatest-lower-bound-uniqueness",
520
+ "group-inverse-uniqueness",
521
+ "hamiltonian-path",
522
+ "hamming-code",
523
+ "hanoi",
524
+ "heat-loss",
525
+ "heron-theorem",
526
+ "ideal-gas-law",
527
+ "illegitimate-reasoning",
528
+ "integer-partitions",
529
+ "intuitionistic-logic-kripke",
530
+ "job-shop-scheduling",
531
+ "knapsack-optimization",
532
+ "knowledge-engineering-alignment-flow",
533
+ "law-of-cosines",
534
+ "least-squares-regression",
535
+ "linear-logic-resources",
536
+ "list-collection",
537
+ "lldm",
538
+ "manufacturing-quality-control",
539
+ "matrix-chain-order",
540
+ "microgrid-dispatch",
541
+ "missionaries-cannibals",
542
+ "modal-logic-kripke",
543
+ "modular-exponentiation",
544
+ "monkey-bananas",
545
+ "n-queens-8",
546
+ "network-sla",
547
+ "newton-raphson",
548
+ "nixon-diamond",
549
+ "observability-log-correlation",
550
+ "odrl-dpv-fpv-trust-flow",
551
+ "odrl-dpv-healthcare-risk-ranked",
552
+ "odrl-dpv-risk-ranked",
553
+ "orbital-transfer-design",
554
+ "path-discovery",
555
+ "peano-arithmetic",
556
+ "peasant",
557
+ "pell-equation",
558
+ "pendulum-period",
559
+ "polynomial",
560
+ "proof-contrapositive",
561
+ "quadratic-formula",
562
+ "radioactive-decay",
563
+ "reusable-builtins",
564
+ "riemann-hypothesis",
565
+ "security-incident-correlation",
566
+ "send-more-money",
567
+ "service-impact",
568
+ "sieve",
569
+ "skolem-functions",
570
+ "socket-age",
571
+ "socket-family",
572
+ "socrates",
573
+ "stable-marriage",
574
+ "statistics-summary",
575
+ "stirling-bell-numbers",
576
+ "sudoku-4x4",
577
+ "superdense-coding",
578
+ "term-tools",
579
+ "totient-summatory",
580
+ "trust-flow-provenance-threshold",
581
+ "turing",
582
+ "vector-similarity",
583
+ "vulnerability-impact",
584
+ "web-names",
585
+ "weighted-interval-scheduling",
586
+ "witch",
587
+ "wolf-goat-cabbage",
588
+ "zebra"
587
589
  ];
588
590
  const FALLBACK_SOURCE = `materialize(answer, 1).
589
591
  answer(ok) :- eq(ok, ok).
package/src/parser.js CHANGED
@@ -23,6 +23,10 @@ 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
  function isVariableStart(text) {
27
31
  const code = text.charCodeAt(0);
28
32
  return code === 95 || (code >= 65 && code <= 90);
@@ -151,7 +155,18 @@ class Parser {
151
155
  if (isPlainAtomStartCode(ch.charCodeAt(0))) {
152
156
  const start = this.pos;
153
157
  this.take();
154
- while (isNameContinueCode(this.peek().charCodeAt(0))) 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
170
  return { type: TOK.ATOM, text: this.source.slice(start, this.pos), line };
156
171
  }
157
172
 
@@ -316,7 +331,7 @@ const SIMPLE_NUMBER = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
316
331
  const FAST_BINARY_FACT = /^([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\.$/;
317
332
  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*\)\.$/;
318
333
  const SIMPLE_VARIABLE = /^[_A-Z][A-Za-z0-9_]*$/;
319
- const SIMPLE_ATOM = /^[a-z][A-Za-z0-9_]*$/;
334
+ const SIMPLE_ATOM = /^[a-z][A-Za-z0-9_]*(?:\.[a-z][A-Za-z0-9_]*)*$/;
320
335
  const GRAPHIC_ATOM = /^[#$&*+\-\/<=>?@^~\\]+$/;
321
336
 
322
337
  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_]*$/.test(name)) return false;
141
+ if (/^[a-z][A-Za-z0-9_]*(?:\.[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
  }
@@ -476,6 +476,13 @@ function whiteBoxCases() {
476
476
  assertEqual(termToString(goal, new Env(), true), 'member(X, [a, b])', 'goal');
477
477
  },
478
478
  },
479
+ {
480
+ name: 'parser preserves dotted atom constants for web-style names',
481
+ run: () => {
482
+ const clauses = parseProgramText('p(web(be.ugent, jos), org.schema).\n');
483
+ assertEqual(termToString(clauses[0].head, new Env(), true), 'p(web(be.ugent, jos), org.schema)', 'head');
484
+ },
485
+ },
479
486
  {
480
487
  name: 'list construction round-trips through properListItems',
481
488
  run: () => {