eyeling 1.33.4 → 1.33.5

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.
Files changed (95) hide show
  1. package/README.md +1 -0
  2. package/{examples/eyelang/SPEC.md → docs/eyelang-language-reference.md} +6 -10
  3. package/examples/eyelang/README.md +11 -25
  4. package/lib/eyelang/builtins/arithmetic.js +1 -3
  5. package/lib/eyelang/builtins/formula.js +0 -16
  6. package/lib/eyelang/builtins/lists.js +0 -5
  7. package/lib/eyelang/builtins/registry.js +1 -2
  8. package/lib/eyelang/builtins/search.js +1 -61
  9. package/lib/eyelang/builtins/strings.js +10 -14
  10. package/package.json +2 -1
  11. package/test/eyelang/conformance/README.md +7 -7
  12. package/test/eyelang/conformance/cases/core/001_fact_output.pl +1 -1
  13. package/test/eyelang/conformance/cases/core/002_rule_recursion.pl +1 -1
  14. package/test/eyelang/conformance/cases/core/003_terms_and_readback.pl +1 -1
  15. package/test/eyelang/conformance/cases/core/004_conjunction_and_parentheses.pl +1 -1
  16. package/test/eyelang/conformance/cases/core/005_list_deconstruction.pl +1 -1
  17. package/test/eyelang/conformance/cases/core/006_comma_formula_data.pl +1 -1
  18. package/test/eyelang/conformance/cases/core/007_anonymous_variables.pl +1 -1
  19. package/test/eyelang/conformance/cases/core/008_graphic_atoms.pl +1 -1
  20. package/test/eyelang/conformance/cases/core/009_comments_and_whitespace.pl +1 -1
  21. package/test/eyelang/conformance/cases/core/010_variable_scope_and_reuse.pl +1 -1
  22. package/test/eyelang/conformance/cases/core/011_predicate_arity.pl +1 -1
  23. package/test/eyelang/conformance/cases/core/012_nested_compound_unification.pl +1 -1
  24. package/test/eyelang/conformance/cases/core/013_multiple_clauses_order.pl +1 -1
  25. package/test/eyelang/conformance/cases/core/014_failure_filters_answers.pl +1 -1
  26. package/test/eyelang/conformance/cases/core/015_improper_list_unification.pl +1 -1
  27. package/test/eyelang/conformance/cases/core/016_zero_arity_compound.pl +1 -1
  28. package/test/eyelang/conformance/cases/core/017_three_step_recursion.pl +1 -1
  29. package/test/eyelang/conformance/cases/core/018_quoted_atom_readback.pl +1 -1
  30. package/test/eyelang/conformance/cases/core/019_parenthesized_three_conjuncts.pl +1 -1
  31. package/test/eyelang/conformance/cases/core/020_nested_list_terms.pl +1 -1
  32. package/test/eyelang/conformance/cases/core/021_repeated_variable_head.pl +1 -1
  33. package/test/eyelang/conformance/cases/core/022_rule_head_structure.pl +1 -1
  34. package/test/eyelang/conformance/cases/core/023_quoted_escapes_readback.pl +1 -1
  35. package/test/eyelang/conformance/cases/core/024_numeric_literal_readback.pl +1 -1
  36. package/test/eyelang/conformance/cases/core/025_body_parentheses_with_formula_data.pl +1 -1
  37. package/test/eyelang/conformance/cases/core/026_underscore_named_variable_reuse.pl +1 -1
  38. package/test/eyelang/conformance/cases/extension/001_default_derived_output.pl +1 -1
  39. package/test/eyelang/conformance/cases/extension/002_materialize_focus.pl +1 -1
  40. package/test/eyelang/conformance/cases/extension/003_arithmetic_and_comparison.pl +1 -2
  41. package/test/eyelang/conformance/cases/extension/004_strings_and_atoms.pl +1 -3
  42. package/test/eyelang/conformance/cases/extension/005_lists_aggregation_ordering.pl +1 -1
  43. package/test/eyelang/conformance/cases/extension/006_formula_terms.pl +1 -2
  44. package/test/eyelang/conformance/cases/extension/007_negation_once_generators.pl +1 -1
  45. package/test/eyelang/conformance/cases/extension/008_equality_and_inequality.pl +1 -1
  46. package/test/eyelang/conformance/cases/extension/009_list_relations.pl +1 -2
  47. package/test/eyelang/conformance/cases/extension/010_append_splits.pl +1 -1
  48. package/test/eyelang/conformance/cases/extension/011_matching_and_comparison.pl +1 -1
  49. package/test/eyelang/conformance/cases/extension/012_memoize_declaration.pl +1 -1
  50. package/test/eyelang/conformance/cases/extension/013_numeric_functions.pl +1 -1
  51. package/test/eyelang/conformance/cases/extension/014_between_enumeration.pl +1 -1
  52. package/test/eyelang/conformance/cases/extension/015_smallest_divisor.pl +1 -1
  53. package/test/eyelang/conformance/cases/extension/016_negation_filter.pl +1 -1
  54. package/test/eyelang/conformance/cases/extension/017_once_user_predicate.pl +1 -1
  55. package/test/eyelang/conformance/cases/extension/018_findall_user_goal.pl +1 -1
  56. package/test/eyelang/conformance/cases/extension/019_sort_deduplicates_atoms.pl +1 -1
  57. package/test/eyelang/conformance/cases/extension/020_append_bound_prefix_suffix.pl +1 -1
  58. package/test/eyelang/conformance/cases/extension/021_nth0_index_generation.pl +1 -1
  59. package/test/eyelang/conformance/cases/extension/022_set_nth0_edges.pl +1 -1
  60. package/test/eyelang/conformance/cases/extension/023_select_duplicate_occurrences.pl +1 -1
  61. package/test/eyelang/conformance/cases/extension/024_not_member_filter.pl +1 -1
  62. package/test/eyelang/conformance/cases/extension/026_nested_formula_terms.pl +1 -2
  63. package/test/eyelang/conformance/cases/extension/027_materialize_excludes_source_fact.pl +1 -1
  64. package/test/eyelang/conformance/cases/extension/028_numeric_and_lexical_comparison.pl +1 -1
  65. package/test/eyelang/conformance/cases/extension/029_string_matching_filters.pl +1 -1
  66. package/test/eyelang/conformance/cases/extension/030_string_and_atom_concat.pl +1 -2
  67. package/test/eyelang/conformance/cases/extension/035_date_difference.pl +1 -1
  68. package/test/eyelang/conformance/cases/extension/036_extended_gcd.pl +1 -1
  69. package/test/eyelang/conformance/cases/extension/037_collatz_trajectory.pl +1 -1
  70. package/test/eyelang/conformance/cases/extension/038_kaprekar_steps.pl +1 -1
  71. package/test/eyelang/conformance/cases/extension/039_goldbach_pair.pl +1 -1
  72. package/test/eyelang/conformance/cases/extension/040_matrix_operations.pl +1 -1
  73. package/test/eyelang/conformance/cases/extension/042_n_queens_small.pl +1 -1
  74. package/test/eyelang/conformance/cases/extension/043_cnf_model.pl +1 -1
  75. package/test/eyelang/conformance/cases/extension/045_alphametic_sum_small.pl +1 -1
  76. package/test/eyelang/conformance/cases/extension/046_bounded_subset.pl +1 -1
  77. package/test/eyelang/conformance/expected/extension/003_arithmetic_and_comparison.out +0 -1
  78. package/test/eyelang/conformance/expected/extension/004_strings_and_atoms.out +0 -2
  79. package/test/eyelang/conformance/expected/extension/006_formula_terms.out +0 -2
  80. package/test/eyelang/conformance/expected/extension/009_list_relations.out +0 -1
  81. package/test/eyelang/conformance/expected/extension/026_nested_formula_terms.out +0 -3
  82. package/test/eyelang/conformance/expected/extension/030_string_and_atom_concat.out +0 -1
  83. package/test/packlist.test.js +1 -0
  84. package/examples/eyelang/exact-cover-sudoku.pl +0 -113
  85. package/examples/eyelang/output/exact-cover-sudoku.pl +0 -3
  86. package/examples/eyelang/output/sudoku.pl +0 -2
  87. package/examples/eyelang/sudoku.pl +0 -20
  88. package/lib/eyelang/builtins/sudoku.js +0 -141
  89. package/test/eyelang/conformance/cases/extension/025_is_list_filter.pl +0 -5
  90. package/test/eyelang/conformance/cases/extension/025_is_list_filter.query +0 -1
  91. package/test/eyelang/conformance/cases/extension/041_atom_range_generators.pl +0 -5
  92. package/test/eyelang/conformance/cases/extension/044_cover9_filter.pl +0 -6
  93. package/test/eyelang/conformance/expected/extension/025_is_list_filter.out +0 -1
  94. package/test/eyelang/conformance/expected/extension/041_atom_range_generators.out +0 -8
  95. package/test/eyelang/conformance/expected/extension/044_cover9_filter.out +0 -2
package/README.md CHANGED
@@ -1153,6 +1153,7 @@ Everything else should be treated as internal unless explicitly documented.
1153
1153
  ├── bin/ CLI executable shim
1154
1154
  ├── lib/ Source modules
1155
1155
  ├── dist/browser/ Browser bundle and ESM wrapper
1156
+ ├── docs/ Project documentation, including the eyelang language reference
1156
1157
  ├── examples/ N3 examples, RDF message inputs, and generated decks
1157
1158
  ├── spec/ RDF 1.2 parser test adapter
1158
1159
  ├── test/ API, built-in, store, example, package, playground, and stream tests
@@ -1,4 +1,4 @@
1
- # eyelang Language Specification
1
+ # eyelang Language Reference
2
2
 
3
3
  ## Table of contents
4
4
 
@@ -77,7 +77,7 @@ A **term** is a variable, atom constant, string, number, list, or compound term.
77
77
 
78
78
  An **atom constant** is a symbolic scalar term, such as `pat`, `type`, or `'atom with spaces'`. It is a term and may appear as an argument, list element, functor name, or predicate name.
79
79
 
80
- An **atomic formula** is a predicate application such as `parent(pat, jan)` or `status(case1, accepted)`. It is the unit of truth in a Herbrand interpretation. In some logic-programming literature atomic formulas are called "atoms"; this specification avoids that shorthand. Whenever the noun "atom" appears here outside a built-in name such as `formula_atom/2`, it means **atom constant**, not atomic formula.
80
+ An **atomic formula** is a predicate application such as `parent(pat, jan)` or `status(case1, accepted)`. It is the unit of truth in a Herbrand interpretation. In some logic-programming literature atomic formulas are called "atoms"; this specification avoids that shorthand. Whenever the noun "atom" appears here outside the phrase "atomic formula", it means **atom constant**.
81
81
 
82
82
  This distinction is normative: `pat` is an atom constant and can appear as a term argument; `parent(pat, jan)` is an atomic formula and can appear as a fact, rule head, or goal. A compound term such as `pair(pat, jan)` has the same surface shape as an atomic formula, but its role is determined by context: as data it is a compound term, and as a clause head or goal it is an atomic formula with predicate symbol `pair/2`.
83
83
 
@@ -252,7 +252,7 @@ Parenthesized comma terms may be goals or data:
252
252
  (name(alice, "Alice"), knows(alice, bob))
253
253
  ```
254
254
 
255
- When a comma term appears as a goal, it is evaluated as conjunction. When it appears as data, it remains a term. `formula_atom/2` and `formula_binary/4` enumerate members inside such formula terms.
255
+ When a comma term appears as a goal, it is evaluated as conjunction. When it appears as data, it remains a term. `formula_binary/4` enumerates binary members inside such formula terms.
256
256
 
257
257
  ## 6. Clauses and predicates
258
258
 
@@ -368,7 +368,7 @@ Implementations MAY provide additional built-ins, but such built-ins are extensi
368
368
  | `div(A, B, C)` | `C = A / B`; integer inputs use integer division. |
369
369
  | `mod(A, B, C)` | Integer remainder. |
370
370
  | `pow(A, B, C)` | `C = A^B`. |
371
- | `max(A, B, C)`, `min(A, B, C)` | Numeric maximum or minimum. |
371
+ | `min(A, B, C)` | Numeric minimum. |
372
372
 
373
373
  ### 9.3 Comparison
374
374
 
@@ -399,10 +399,8 @@ Comparisons interpret numeric-looking terms numerically. Other scalar terms are
399
399
 
400
400
  | Built-in | Meaning |
401
401
  |---|---|
402
- | `atom_concat(A, B, C)` | Atom-constant concatenation. |
403
402
  | `str_concat(A, B, C)` | String concatenation. |
404
403
  | `contains(Text, Needle)` | Text contains `Needle`. |
405
- | `not_contains(Text, Needle)` | Text does not contain `Needle`. |
406
404
  | `matches(Text, Pattern)` | Text matches a simple implementation regex/search pattern. |
407
405
  | `not_matches(Text, Pattern)` | Negation of `matches/2`. |
408
406
 
@@ -419,7 +417,6 @@ Comparisons interpret numeric-looking terms numerically. Other scalar terms are
419
417
  | `not_member(X, List)` | Succeeds when `X` is not a member. |
420
418
  | `reverse(A, B)` | Reverses a proper list. |
421
419
  | `length(List, N)` | Proper-list length. |
422
- | `is_list(X)` | Succeeds when `X` is a proper list. |
423
420
 
424
421
  ### 9.8 Aggregation and ordering
425
422
 
@@ -434,11 +431,10 @@ Comparisons interpret numeric-looking terms numerically. Other scalar terms are
434
431
 
435
432
  ### 9.9 Formula terms
436
433
 
437
- Formula terms are data representations of atomic formulas and comma conjunctions. `formula_atom/2` uses "atom" in the logic-programming sense of atomic formula; it MUST NOT be confused with atom constants such as `alice` or `name`.
434
+ Formula terms are data representations of atomic formulas and comma conjunctions.
438
435
 
439
436
  | Built-in | Meaning |
440
437
  |---|---|
441
- | `formula_atom(Formula, Atom)` | Enumerates atomic-formula members inside comma formula data. The second argument denotes an atomic formula term, not necessarily an atom constant. |
442
438
  | `formula_binary(Formula, S, P, O)` | Enumerates binary formula members `P(S, O)`, exposing the functor as atom constant `P`. |
443
439
 
444
440
  Example:
@@ -470,7 +466,7 @@ An extension built-in SHOULD obey the same surface-language discipline as standa
470
466
  - it SHOULD document its intended modes, especially which arguments must be ground before it runs deterministically;
471
467
  - it MUST NOT change the meaning of ordinary facts, rules, unification, or standard built-ins.
472
468
 
473
- For example, an implementation may include extension modules for Sudoku solving, portfolio selection, number-theory algorithms, graph search, matrix operations, or alphametic puzzles. Those modules may be valuable and may make example programs much faster, but their predicate names, arities, algorithms, and modes are implementation-defined unless they are separately standardized.
469
+ For example, an implementation may include extension modules for portfolio selection, number-theory algorithms, graph search, matrix operations, or alphametic puzzles. Those modules may be valuable and may make example programs much faster, but their predicate names, arities, algorithms, and modes are implementation-defined unless they are separately standardized.
474
470
 
475
471
  An implementation that provides explanation output SHOULD make extension built-ins explainable at least as opaque successful or failed built-in calls, so that proof traces do not incorrectly report "no clauses" for a host-provided relation.
476
472
 
@@ -10,7 +10,7 @@ Programs write relations directly, for example `ancestor(pat, emma)` or `status(
10
10
 
11
11
  Try it in the [browser playground](https://eyereasoner.github.io/eyelang/playground). The playground includes run options equivalent to CLI `--stats` and `--proof`.
12
12
 
13
- For the normative language definition, including lexical syntax, terms, clauses, goals, built-ins, `memoize/2`, `materialize/2`, and conformance boundaries, read the [eyelang specification](SPEC.md).
13
+ For the normative language definition, including lexical syntax, terms, clauses, goals, built-ins, `memoize/2`, `materialize/2`, and conformance boundaries, read the [eyelang language reference](../../docs/eyelang-language-reference.md).
14
14
 
15
15
  ## Contents
16
16
 
@@ -243,7 +243,7 @@ The CLI is output-oriented and uses `materialize/2` to decide what to print. Emb
243
243
  Add `-s` or `--stats` when you want lightweight solver counters on stderr without changing stdout:
244
244
 
245
245
  ```sh
246
- bin/eyelang -s examples/sudoku.pl
246
+ bin/eyelang -s examples/eyelang/n-queens.pl
247
247
  ```
248
248
 
249
249
  The playground has matching `--stats` and `--proof` checkboxes, so browser runs can show the same counters or explanations like the CLI.
@@ -251,15 +251,11 @@ The playground has matching `--stats` and `--proof` checkboxes, so browser runs
251
251
 
252
252
  ### Builtins
253
253
 
254
- eyelang builtins are registered by name and arity in small modules under [`src/builtins`](src/builtins). This keeps the runtime portable to Node.js and the browser while giving each builtin family a clear boundary. Builtins are enabled by normal predicate calls.
254
+ eyelang builtins are registered by name and arity in small modules under [`lib/eyelang/builtins`](../../lib/eyelang/builtins). This keeps the runtime portable to Node.js and the browser while giving each builtin family a clear boundary. Builtins are enabled by normal predicate calls.
255
255
 
256
- The core builtin families cover unification, arithmetic, comparison, dates, strings, lists, aggregation, formula terms, and search control. Additional reusable finite-search helpers are available for examples that would otherwise need large amounts of repetitive generate-and-test code. These helpers are deliberately general relations rather than shortcuts tied to a particular example name. For example:
256
+ The core builtin families cover unification, arithmetic, comparison, dates, strings, lists, aggregation, formula terms, and search control. Additional reusable finite-search helpers are available only where bundled examples need them to avoid large amounts of repetitive generate-and-test code. These helpers are deliberately general relations rather than shortcuts tied to a particular example name. For example:
257
257
 
258
258
  ```prolog
259
- solution(Name, Rows) :-
260
- puzzle(Name, Grid),
261
- sudoku(Grid, Rows).
262
-
263
259
  answer(Queens) :-
264
260
  n_queens(8, Queens).
265
261
 
@@ -268,11 +264,9 @@ best(Cycle, Cost) :-
268
264
  weighted_hamiltonian_cycle(edge, Cities, Cycle, Cost).
269
265
  ```
270
266
 
271
- `sudoku/2` accepts either an 81-character string or a 9x9 list. Digits `1` to `9` are givens; `0`, `.`, and `_` mark blanks. It returns the solved 9x9 list.
272
-
273
- The reusable search and numeric helpers include `atom_range/4`, `atom_ranges/4`, `n_queens/2`, Hamiltonian path/cycle helpers, `cnf_model/3`, Quine-McCluskey helpers, bounded subset/path helpers, number-theory helpers such as `extended_gcd/5`, matrix helpers such as `matrix_multiply/2`, and `alphametic_sum/5`. These helpers are extension builtins of this implementation; [`SPEC.md`](SPEC.md) defines the portable core and standard builtin profile.
267
+ The reusable search and numeric helpers include `n_queens/2`, Hamiltonian path/cycle helpers, `cnf_model/3`, Quine-McCluskey helpers, bounded subset/path helpers, number-theory helpers such as `extended_gcd/5`, matrix helpers such as `matrix_multiply/2`, and `alphametic_sum/5`. These helpers are extension builtins of this implementation; [the eyelang language reference](../../docs/eyelang-language-reference.md) defines the portable core and standard builtin profile.
274
268
 
275
- To add a builtin, create or extend a module with `register(registry)` and call `registry.add(name, arity, handler, options)`. The default registry is assembled in [`src/builtins/registry.js`](src/builtins/registry.js). Builtins that are only safe for specific argument modes should provide a `ready` predicate and `fallbackWhenNotReady: true`, so user-defined clauses remain visible until the builtin is applicable.
269
+ To add a builtin, create or extend a module with `register(registry)` and call `registry.add(name, arity, handler, options)`. The default registry is assembled in [`lib/eyelang/builtins/registry.js`](../../lib/eyelang/builtins/registry.js). Builtins that are only safe for specific argument modes should provide a `ready` predicate and `fallbackWhenNotReady: true`, so user-defined clauses remain visible until the builtin is applicable.
276
270
 
277
271
 
278
272
  ## Aggregation helpers
@@ -298,13 +292,7 @@ best_cycle(Cycle, Cost) :-
298
292
 
299
293
  ## Formula data
300
294
 
301
- Comma terms can be data as well as conjunctions. eyelang provides relation-oriented formula utilities.
302
-
303
- `formula_atom(Formula, Atom)` enumerates atomic formula terms inside a comma formula:
304
-
305
- ```prolog
306
- formula_atom((name(alice, "Alice"), knows(alice, bob)), X).
307
- ```
295
+ Comma terms can be data as well as conjunctions. eyelang provides a relation-oriented formula utility.
308
296
 
309
297
  `formula_binary(Formula, S, P, O)` enumerates binary terms and exposes their functor as an atom constant:
310
298
 
@@ -380,7 +368,6 @@ The repository includes examples for recursion, graph reachability, finite searc
380
368
  | [`equivalence-classes-overlap-implies-same-class.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/equivalence-classes-overlap-implies-same-class.pl) | Packages the shared-member proof pattern for equivalence classes. | [`output/equivalence-classes-overlap-implies-same-class.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/equivalence-classes-overlap-implies-same-class.pl) |
381
369
  | [`eulerian-path.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/eulerian-path.pl) | Finds an Eulerian path using each edge once. | [`output/eulerian-path.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/eulerian-path.pl) |
382
370
  | [`ev-range-worlds.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/ev-range-worlds.pl) | Estimates electric-vehicle trip feasibility. | [`output/ev-range-worlds.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/ev-range-worlds.pl) |
383
- | [`exact-cover-sudoku.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/exact-cover-sudoku.pl) | Solves Sudoku via exact-cover-style constraints. | [`output/exact-cover-sudoku.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/exact-cover-sudoku.pl) |
384
371
  | [`existential-rule.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/existential-rule.pl) | Represents existential witnesses with explicit Skolem-style terms. | [`output/existential-rule.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/existential-rule.pl) |
385
372
  | [`exoplanet-validation-worlds.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/exoplanet-validation-worlds.pl) | Validates exoplanet candidates across worlds. | [`output/exoplanet-validation-worlds.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/exoplanet-validation-worlds.pl) |
386
373
  | [`expression-eval.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/expression-eval.pl) | Evaluates a small arithmetic expression tree. | [`output/expression-eval.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/expression-eval.pl) |
@@ -448,7 +435,6 @@ The repository includes examples for recursion, graph reachability, finite searc
448
435
  | [`socket-family.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/socket-family.pl) | Shows socket-declared family-source inputs and ancestry rules. | [`output/socket-family.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/socket-family.pl) |
449
436
  | [`socrates.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/socrates.pl) | Derives that Socrates is mortal. | [`output/socrates.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/socrates.pl) |
450
437
  | [`statistics-summary.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/statistics-summary.pl) | Computes population statistics for a sample. | [`output/statistics-summary.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/statistics-summary.pl) |
451
- | [`sudoku.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/sudoku.pl) | Solves generic 9x9 Sudoku strings through the sudoku/2 builtin. | [`output/sudoku.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/sudoku.pl) |
452
438
  | [`superdense-coding.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/superdense-coding.pl) | Models superdense-coding bit transmission. | [`output/superdense-coding.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/superdense-coding.pl) |
453
439
  | [`traveling-salesman.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/traveling-salesman.pl) | Finds an optimal traveling-salesman tour. | [`output/traveling-salesman.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/traveling-salesman.pl) |
454
440
  | [`turing.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/turing.pl) | Simulates a binary-increment Turing machine. | [`output/turing.pl`](https://github.com/eyereasoner/eyelang/blob/main/examples/output/turing.pl) |
@@ -483,7 +469,7 @@ Run the full test suite:
483
469
  npm test
484
470
  ```
485
471
 
486
- The test suite runs in this order: Conformance, Regression/API/White-box, Examples. Each section prints its own subtotal, followed by a suite-specific grand total. The suite checks the conformance cases derived from `SPEC.md`, supplemental regression/API/white-box checks, and every runnable example against its golden output.
472
+ The test suite runs in this order: Conformance, Regression/API/White-box, Examples. Each section prints its own subtotal, followed by a suite-specific grand total. The suite checks the conformance cases derived from the language reference, supplemental regression/API/white-box checks, and every runnable example against its golden output.
487
473
 
488
474
  Run only one suite when you are iterating:
489
475
 
@@ -493,7 +479,7 @@ npm run test:regression
493
479
  npm run test:examples
494
480
  ```
495
481
 
496
- The conformance suite lives in [`conformance/`](conformance/) and is split into `core` and `extension` profiles matching `SPEC.md`. Each case is a small program with an exact expected stdout file, and some internal conformance cases also include a goal file for testing the embeddable solver, so other implementations can reuse the same cases. The regression suite lives in [`test/run-regression.js`](test/run-regression.js) and covers CLI regressions, the public JavaScript API, and white-box invariants for parser, unification, and indexing behavior.
482
+ The conformance suite lives in [`conformance/`](conformance/) and is split into `core` and `extension` profiles matching the language reference. Each case is a small program with an exact expected stdout file, and some internal conformance cases also include a goal file for testing the embeddable solver, so other implementations can reuse the same cases. The regression suite lives in [`test/run-regression.js`](test/run-regression.js) and covers CLI regressions, the public JavaScript API, and white-box invariants for parser, unification, and indexing behavior.
497
483
 
498
484
  ## Development and release
499
485
 
@@ -510,13 +496,13 @@ node bin/eyelang --help
510
496
  Useful profiling smoke test:
511
497
 
512
498
  ```sh
513
- bin/eyelang -s examples/sudoku.pl > /dev/null
499
+ bin/eyelang -s examples/eyelang/n-queens.pl > /dev/null
514
500
  ```
515
501
 
516
502
  For a release:
517
503
 
518
504
  1. update `VERSION`;
519
- 2. update `README.md` and `SPEC.md`;
505
+ 2. update `README.md` and the language reference;
520
506
  3. regenerate golden outputs if behavior changed;
521
507
  4. run `npm test`;
522
508
  5. publish the repository with `playground.html` and `playground-worker.mjs` if publishing the playground. The playground includes controls equivalent to CLI `--stats` and `--proof`.
@@ -3,7 +3,7 @@
3
3
  import { compareIntegerText, deref, isDecimalInteger, lexicalValue, numberTerm, numberTextFromDouble, parseFiniteNumber, unify } from '../term.js';
4
4
 
5
5
  const unaryNames = ['neg', 'abs', 'sin', 'cos', 'asin', 'acos', 'rounded', 'log'];
6
- const binaryNames = ['add', 'sub', 'mul', 'div', 'mod', 'max', 'min', 'pow'];
6
+ const binaryNames = ['add', 'sub', 'mul', 'div', 'mod', 'min', 'pow'];
7
7
  const compareNames = ['lt', 'gt', 'le', 'ge'];
8
8
 
9
9
  export const arithmeticBuiltins = {
@@ -60,7 +60,6 @@ function binary(name) {
60
60
  else if (name === 'sub') out = (a - b).toString();
61
61
  else if (name === 'mul') out = (a * b).toString();
62
62
  else if (name === 'div') { if (b === 0n) return; out = (a / b).toString(); }
63
- else if (name === 'max') out = (a >= b ? a : b).toString();
64
63
  else if (name === 'min') out = (a <= b ? a : b).toString();
65
64
  else if (name === 'pow') { if (b < 0n) return; out = (a ** b).toString(); }
66
65
  } else if (name === 'mod') {
@@ -77,7 +76,6 @@ function binary(name) {
77
76
  else if (name === 'mul') value = a * b;
78
77
  else if (name === 'div') { if (b === 0) return; value = a / b; }
79
78
  else if (name === 'pow') value = Math.pow(a, b);
80
- else if (name === 'max') value = Math.max(a, b);
81
79
  else if (name === 'min') value = Math.min(a, b);
82
80
  out = numberTextFromDouble(value);
83
81
  }
@@ -4,26 +4,10 @@ import { atom, deref, isConjunction, unify } from '../term.js';
4
4
 
5
5
  export const formulaBuiltins = {
6
6
  register(registry) {
7
- registry.add('formula_atom', 2, formulaAtom);
8
7
  registry.add('formula_binary', 4, formulaBinary);
9
8
  }
10
9
  };
11
10
 
12
- function* emitFormulaAtoms(formula, target, env) {
13
- formula = deref(formula, env);
14
- if (isConjunction(formula)) {
15
- yield* emitFormulaAtoms(formula.args[0], target, env);
16
- yield* emitFormulaAtoms(formula.args[1], target, env);
17
- return;
18
- }
19
- if (formula.type !== 'compound') return;
20
- const next = env.clone();
21
- if (unify(target, formula, next)) yield next;
22
- }
23
-
24
- function* formulaAtom({ goal, env }) {
25
- yield* emitFormulaAtoms(goal.args[0], goal.args[1], env);
26
- }
27
11
 
28
12
  function* emitFormulaBinary(formula, subject, predicate, object, env) {
29
13
  formula = deref(formula, env);
@@ -13,7 +13,6 @@ export const listBuiltins = {
13
13
  registry.add('not_member', 2, notMember, { deterministic: true });
14
14
  registry.add('reverse', 2, reverse, { deterministic: true });
15
15
  registry.add('length', 2, lengthBuiltin, { deterministic: true });
16
- registry.add('is_list', 1, isList, { deterministic: true });
17
16
  registry.add('sort', 2, sortBuiltin, { deterministic: true });
18
17
  }
19
18
  };
@@ -133,10 +132,6 @@ function* lengthBuiltin({ goal, env }) {
133
132
  if (unify(goal.args[1], numberTerm(items.length), next)) yield next;
134
133
  }
135
134
 
136
- function* isList({ goal, env }) {
137
- const items = properListItems(goal.args[0], env);
138
- if (items) yield env;
139
- }
140
135
 
141
136
  function* sortBuiltin({ goal, env }) {
142
137
  const items = properListItems(goal.args[0], env);
@@ -7,7 +7,6 @@ import { listBuiltins } from './lists.js';
7
7
  import { aggregationBuiltins } from './aggregation.js';
8
8
  import { formulaBuiltins } from './formula.js';
9
9
  import { controlBuiltins } from './control.js';
10
- import { sudokuBuiltins } from './sudoku.js';
11
10
  import { portfolioBuiltins } from './portfolio.js';
12
11
  import { searchBuiltins } from './search.js';
13
12
  import { numberTheoryBuiltins } from './number-theory.js';
@@ -39,7 +38,7 @@ export class BuiltinRegistry {
39
38
 
40
39
  export function createDefaultRegistry() {
41
40
  const registry = new BuiltinRegistry();
42
- for (const mod of [coreBuiltins, arithmeticBuiltins, stringBuiltins, listBuiltins, aggregationBuiltins, formulaBuiltins, controlBuiltins, sudokuBuiltins, portfolioBuiltins, searchBuiltins, numberTheoryBuiltins, matrixBuiltins, alphameticBuiltins]) {
41
+ for (const mod of [coreBuiltins, arithmeticBuiltins, stringBuiltins, listBuiltins, aggregationBuiltins, formulaBuiltins, controlBuiltins, portfolioBuiltins, searchBuiltins, numberTheoryBuiltins, matrixBuiltins, alphameticBuiltins]) {
43
42
  mod.register(registry);
44
43
  }
45
44
  return registry;
@@ -1,14 +1,12 @@
1
1
  // Reusable finite-search builtins for examples that would otherwise spend most
2
2
  // of their time in small relational generators. These predicates are generic
3
- // entry points (graph, CNF, QMC, range, and n-queens helpers), not compiled
3
+ // entry points (graph, CNF, QMC, and n-queens helpers), not compiled
4
4
  // replacements for particular example predicate names.
5
5
  import { atom, compound, deref, listFromItems, numberTerm, properListItems, unify } from '../term.js';
6
6
  import { compareLexicalOrNumeric } from './arithmetic.js';
7
7
 
8
8
  export const searchBuiltins = {
9
9
  register(registry) {
10
- registry.add('atom_range', 4, atomRange, { fallbackWhenNotReady: true, ready: atomRangeReady });
11
- registry.add('atom_ranges', 4, atomRanges, { fallbackWhenNotReady: true, ready: atomRangesReady });
12
10
  registry.add('n_queens', 2, nQueens, { fallbackWhenNotReady: true, ready: firstIntReady });
13
11
  registry.add('weighted_hamiltonian_cycle', 4, weightedHamiltonianCycle, { fallbackWhenNotReady: true, ready: weightedGraphReady });
14
12
  registry.add('weighted_hamiltonian_path', 4, weightedHamiltonianPath, { fallbackWhenNotReady: true, ready: weightedGraphReady });
@@ -26,67 +24,9 @@ function graphReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== nu
26
24
  function weightedGraphReady(goal, env) { return graphReady(goal, env); }
27
25
  function fixedCycleReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== null && intTerm(goal.args[1], env) !== null; }
28
26
  function boundedPathReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== null && atomKey(deref(goal.args[1], env)) !== null && atomKey(deref(goal.args[2], env)) !== null && intTerm(goal.args[3], env) !== null; }
29
- function atomRangeReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== null && intTerm(goal.args[1], env) !== null && intTerm(goal.args[2], env) !== null; }
30
- function atomRangesReady(goal, env) { return atomList(goal.args[0], env) !== null && intTerm(goal.args[1], env) !== null && intTerm(goal.args[2], env) !== null; }
31
27
  function cnfReady(goal, env) { return atomList(goal.args[0], env) !== null && clauseList(goal.args[1], env) !== null; }
32
28
  function qmReady(goal, env) { return numberList(goal.args[0], env) !== null && numberList(goal.args[1], env) !== null && bitTable(goal.args[2], env) !== null; }
33
29
 
34
- function* atomRange({ goal, env }) {
35
- // atom_range(Prefix, Low, High, Atom) is a compact generator for atoms such
36
- // as n1, n2, ...; it replaces between/3 + atom_concat/3 in large generated
37
- // data sets without committing to any domain-specific predicate name.
38
- const prefix = atomKey(deref(goal.args[0], env));
39
- const low = intTerm(goal.args[1], env);
40
- const high = intTerm(goal.args[2], env);
41
- if (prefix == null || low == null || high == null) return;
42
- const output = deref(goal.args[3], env);
43
- if (output.type !== 'var') {
44
- const text = atomKey(output);
45
- const n = parsePrefixedInteger(prefix, text);
46
- if (n == null || n < low || n > high) return;
47
- yield env;
48
- return;
49
- }
50
- for (let i = low; i <= high; i++) {
51
- const next = env.clone();
52
- next.bind(output.name, atom(`${prefix}${i}`));
53
- yield next;
54
- }
55
- }
56
-
57
- function parsePrefixedInteger(prefix, text) {
58
- if (text == null || !text.startsWith(prefix)) return null;
59
- const suffix = text.slice(prefix.length);
60
- if (!/^\d+$/.test(suffix)) return null;
61
- const n = Number(suffix);
62
- return Number.isSafeInteger(n) ? n : null;
63
- }
64
-
65
- function* atomRanges({ goal, env }) {
66
- // atom_ranges([p, q], Low, High, Atom) is the multi-prefix companion to
67
- // atom_range/4. It is useful when several generated atom families feed the
68
- // same relation.
69
- const prefixes = atomList(goal.args[0], env);
70
- const low = intTerm(goal.args[1], env);
71
- const high = intTerm(goal.args[2], env);
72
- if (!prefixes || low == null || high == null) return;
73
- const output = deref(goal.args[3], env);
74
- if (output.type !== 'var') {
75
- const text = atomKey(output);
76
- for (const prefix of prefixes) {
77
- const n = parsePrefixedInteger(prefix, text);
78
- if (n != null && n >= low && n <= high) { yield env; return; }
79
- }
80
- return;
81
- }
82
- for (const prefix of prefixes) {
83
- for (let i = low; i <= high; i++) {
84
- const next = env.clone();
85
- next.bind(output.name, atom(`${prefix}${i}`));
86
- yield next;
87
- }
88
- }
89
- }
90
30
 
91
31
  function* nQueens({ goal, env }) {
92
32
  // n_queens(N, Solution) performs the same finite search as the declarative
@@ -1,24 +1,21 @@
1
- // String and atom conversion builtins.
1
+ // String builtins.
2
2
  // They mostly project from already-ground terms to avoid guessing string domains.
3
- import { atom, lexicalValue, stringTerm, unify } from '../term.js';
3
+ import { lexicalValue, stringTerm, unify } from '../term.js';
4
4
 
5
5
  export const stringBuiltins = {
6
6
  register(registry) {
7
- for (const name of ['atom_concat', 'str_concat']) registry.add(name, 3, concat(name), { deterministic: true });
8
- for (const name of ['contains', 'not_contains', 'matches', 'not_matches']) registry.add(name, 2, contains(name), { deterministic: true });
7
+ registry.add('str_concat', 3, concat, { deterministic: true });
8
+ for (const name of ['contains', 'matches', 'not_matches']) registry.add(name, 2, contains(name), { deterministic: true });
9
9
  }
10
10
  };
11
11
 
12
12
 
13
- function concat(name) {
14
- return function* ({ goal, env }) {
15
- const left = lexicalValue(goal.args[0], env);
16
- const right = lexicalValue(goal.args[1], env);
17
- if (left == null || right == null) return;
18
- const result = name === 'str_concat' ? stringTerm(left + right) : atom(left + right);
19
- const next = env.clone();
20
- if (unify(goal.args[2], result, next)) yield next;
21
- };
13
+ function* concat({ goal, env }) {
14
+ const left = lexicalValue(goal.args[0], env);
15
+ const right = lexicalValue(goal.args[1], env);
16
+ if (left == null || right == null) return;
17
+ const next = env.clone();
18
+ if (unify(goal.args[2], stringTerm(left + right), next)) yield next;
22
19
  }
23
20
 
24
21
  function contains(name) {
@@ -29,7 +26,6 @@ function contains(name) {
29
26
  const has = haystack.includes(needle);
30
27
  const matches = simpleAlternationMatch(haystack, needle);
31
28
  const pass = (name === 'contains' && has) ||
32
- (name === 'not_contains' && !has) ||
33
29
  (name === 'matches' && matches) ||
34
30
  (name === 'not_matches' && !matches);
35
31
  if (pass) yield env;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.33.4",
3
+ "version": "1.33.5",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -29,6 +29,7 @@
29
29
  "eyelang.d.ts",
30
30
  "bin",
31
31
  "dist",
32
+ "docs",
32
33
  "lib",
33
34
  "test",
34
35
  "tools",
@@ -1,6 +1,6 @@
1
1
  # eyelang conformance suite
2
2
 
3
- This directory contains the executable conformance cases for the eyelang language and reference engine. The normative language description is in [`../SPEC.md`](../SPEC.md).
3
+ This directory contains the executable conformance cases for the eyelang language and reference engine. The normative language description is in the [eyelang language reference](../../../docs/eyelang-language-reference.md).
4
4
 
5
5
  The suite is intentionally file-based so another implementation can run the same programs and compare exact standard output. A case consists of:
6
6
 
@@ -20,25 +20,25 @@ npm test
20
20
  Run only the conformance suite:
21
21
 
22
22
  ```sh
23
- npm run test:conformance
23
+ node test/eyelang/run-conformance.mjs
24
24
  ```
25
25
 
26
26
  Run a single conformance profile directly:
27
27
 
28
28
  ```sh
29
- node test/run-conformance.js core
30
- node test/run-conformance.js extension
29
+ node test/eyelang/run-conformance.mjs core
30
+ node test/eyelang/run-conformance.mjs extension
31
31
  ```
32
32
 
33
33
  The runner executes materialized programs in-process through the public JavaScript API so small conformance cases avoid measuring Node startup overhead.
34
34
 
35
35
  ## Profiles
36
36
 
37
- `core` covers the portable core language profile from `../SPEC.md`: lexical syntax, facts, definite clauses, first-order terms, lists, conjunction, structured unification through user predicates, left-to-right goal-directed proof search, materialized output, and read-back printing.
37
+ `core` covers the portable core language profile from the [eyelang language reference](../../../docs/eyelang-language-reference.md): lexical syntax, facts, definite clauses, first-order terms, lists, conjunction, structured unification through user predicates, left-to-right goal-directed proof search, materialized output, and read-back printing.
38
38
 
39
- `extension` covers the standard built-in and host behavior exercised by the current reference implementation: arithmetic, comparison, strings, list relations, aggregation, formula-term helpers, number-theory helpers, finite-search helpers, matrix helpers, `memoize/2`, `materialize/2`, and default derived output.
39
+ `extension` covers the standard built-in and host behavior exercised by the current reference implementation: arithmetic, comparison, strings, list relations, aggregation, formula-term helpers, number-theory helpers, finite-search helpers used by the example corpus, matrix helpers, `memoize/2`, `materialize/2`, and default derived output.
40
40
 
41
- The profile name `extension` is a test-suite grouping name. It does not mean that these cases are outside the eyelang specification; most of them correspond to the standard built-in profile and standard host profile in `../SPEC.md`.
41
+ The profile name `extension` is a test-suite grouping name. It does not mean that these cases are outside the eyelang language reference; most of them correspond to the standard built-in profile and standard host profile in the [eyelang language reference](../../../docs/eyelang-language-reference.md).
42
42
 
43
43
  ## Updating expected output
44
44
 
@@ -1,4 +1,4 @@
1
- % SPEC 6, 7, 11: facts can be exposed through a materialized derived predicate.
1
+ % Reference 6, 7, 11: facts can be exposed through a materialized derived predicate.
2
2
  materialize(parent, 2).
3
3
  base_parent(pat, jan).
4
4
  parent(X, Y) :- base_parent(X, Y).
@@ -1,4 +1,4 @@
1
- % SPEC 6, 7: definite clauses, conjunction, and recursive proof search.
1
+ % Reference 6, 7: definite clauses, conjunction, and recursive proof search.
2
2
  materialize(ancestor, 2).
3
3
  parent(pat, jan).
4
4
  parent(jan, emma).
@@ -1,4 +1,4 @@
1
- % SPEC 3, 5, 11: scalars, compounds, lists, and read-back printing.
1
+ % Reference 3, 5, 11: scalars, compounds, lists, and read-back printing.
2
2
  materialize(value, 2).
3
3
  raw_value(atom, pat).
4
4
  raw_value(quoted_atom, 'atom with spaces').
@@ -1,4 +1,4 @@
1
- % SPEC 5.5, 7: parenthesized comma terms are conjunctions in goal position.
1
+ % Reference 5.5, 7: parenthesized comma terms are conjunctions in goal position.
2
2
  p(a).
3
3
  q(a).
4
4
  ok(X, yes) :- (p(X), q(X)).
@@ -1,4 +1,4 @@
1
- % SPEC 5.4, 12.1: list syntax and unification in rule heads.
1
+ % Reference 5.4, 12.1: list syntax and unification in rule heads.
2
2
  first([X | _Rest], X).
3
3
  tail([_Head | Tail], Tail).
4
4
  answer(first, X) :- first([a, b, c], X).
@@ -1,4 +1,4 @@
1
- % SPEC 5.5: comma terms remain data outside goal position.
1
+ % Reference 5.5: comma terms remain data outside goal position.
2
2
  record((name(alice, "Alice"), knows(alice, bob))).
3
3
  answer(formula, F) :- record(F).
4
4
  materialize(answer, 2).
@@ -1,4 +1,4 @@
1
- % SPEC 3.4, 5.1, 7.1: each anonymous variable occurrence is fresh.
1
+ % Reference 3.4, 5.1, 7.1: each anonymous variable occurrence is fresh.
2
2
  pair(a, one).
3
3
  pair(b, two).
4
4
  answer(fresh, yes) :- pair(a, _), pair(b, _).
@@ -1,4 +1,4 @@
1
- % SPEC 3.5, 5.2, 11: graphic atom constants are scalar terms.
1
+ % Reference 3.5, 5.2, 11: graphic atom constants are scalar terms.
2
2
  materialize(value, 2).
3
3
  raw_value(hash, #).
4
4
  raw_value(arrow, =>).
@@ -1,4 +1,4 @@
1
- % SPEC 3.2, 3.6: comments are ignored outside quoted text and atoms.
1
+ % Reference 3.2, 3.6: comments are ignored outside quoted text and atoms.
2
2
  item(quoted_percent, "% not a comment"). % trailing comment
3
3
  item(quoted_atom, 'has % sign').
4
4
  answer(K, V) :- item(K, V).
@@ -1,4 +1,4 @@
1
- % SPEC 5.1, 7.1: variable occurrences are scoped per clause and reused within a clause.
1
+ % Reference 5.1, 7.1: variable occurrences are scoped per clause and reused within a clause.
2
2
  edge(a, b).
3
3
  edge(b, c).
4
4
  edge(c, d).
@@ -1,4 +1,4 @@
1
- % SPEC 6: predicate name and arity both identify a predicate group.
1
+ % Reference 6: predicate name and arity both identify a predicate group.
2
2
  p(a).
3
3
  p(a, b).
4
4
  answer(unary, X) :- p(X).
@@ -1,4 +1,4 @@
1
- % SPEC 5.3, 7.1: unification follows nested compound term structure.
1
+ % Reference 5.3, 7.1: unification follows nested compound term structure.
2
2
  fact(pair(a, nested(b, [c, d]))).
3
3
  answer(middle, X) :- fact(pair(a, nested(X, [c, d]))).
4
4
  answer(list_tail, T) :- fact(pair(a, nested(b, [c | T]))).
@@ -1,4 +1,4 @@
1
- % SPEC 6, 7: multiple clauses for one predicate are explored as alternatives.
1
+ % Reference 6, 7: multiple clauses for one predicate are explored as alternatives.
2
2
  color(red).
3
3
  color(blue).
4
4
  paint(X) :- color(X).
@@ -1,4 +1,4 @@
1
- % SPEC 7.2: a failing subgoal removes that candidate answer.
1
+ % Reference 7.2: a failing subgoal removes that candidate answer.
2
2
  candidate(a).
3
3
  candidate(b).
4
4
  allowed(a).
@@ -1,4 +1,4 @@
1
- % SPEC 5.4: improper list surface syntax unifies with head-tail structure.
1
+ % Reference 5.4: improper list surface syntax unifies with head-tail structure.
2
2
  cell([head | tail], head, tail).
3
3
  answer(list, L) :- cell(L, head, tail).
4
4
  answer(head, H) :- cell([H | tail], H, tail).
@@ -1,4 +1,4 @@
1
- % SPEC 4, 5.3: zero-arity compounds are written and matched with parentheses.
1
+ % Reference 4, 5.3: zero-arity compounds are written and matched with parentheses.
2
2
  status(nil(), ok).
3
3
  answer(value, X) :- status(X, ok).
4
4
  materialize(answer, 2).
@@ -1,4 +1,4 @@
1
- % SPEC 7: recursive definite clauses can derive paths beyond one step.
1
+ % Reference 7: recursive definite clauses can derive paths beyond one step.
2
2
  link(a, b).
3
3
  link(b, c).
4
4
  link(c, d).
@@ -1,4 +1,4 @@
1
- % SPEC 3.5, 11: quoted atoms preserve spaces, quotes, and the empty atom.
1
+ % Reference 3.5, 11: quoted atoms preserve spaces, quotes, and the empty atom.
2
2
  symbol('two words').
3
3
  symbol('needs''quote').
4
4
  symbol('').
@@ -1,4 +1,4 @@
1
- % SPEC 5.5, 7: parenthesized comma terms with more than two members are conjunctions as goals.
1
+ % Reference 5.5, 7: parenthesized comma terms with more than two members are conjunctions as goals.
2
2
  p(a).
3
3
  q(a).
4
4
  r(a).
@@ -1,4 +1,4 @@
1
- % SPEC 5.3, 5.4: lists may contain structured terms that unify positionally.
1
+ % Reference 5.3, 5.4: lists may contain structured terms that unify positionally.
2
2
  node([pair(a, b), pair(c, d)]).
3
3
  answer(first_key, X) :- node([pair(X, _), pair(c, d)]).
4
4
  answer(second_key, X) :- node([pair(a, b), pair(X, d)]).