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
@@ -1,141 +0,0 @@
1
- // Sudoku-specific helpers used by the example suite.
2
- // The cover9/1 optimization prunes invalid row blocks early while leaving the declarative fallback available.
3
- import { deref, lexicalValue, listFromItems, numberTerm, properListItems, unify } from '../term.js';
4
-
5
- export const sudokuBuiltins = {
6
- register(registry) {
7
- registry.add('sudoku', 2, sudoku);
8
- registry.add('cover9', 1, cover9, { deterministic: true, ready: cover9Ready });
9
- }
10
- };
11
-
12
-
13
- function cover9Ready(goal, env) {
14
- const items = properListItems(goal.args[0], env);
15
- if (!items || items.length !== 9) return false;
16
- for (const item of items) {
17
- const text = lexicalValue(item, env);
18
- if (!/^[1-9]$/.test(text ?? '')) return false;
19
- }
20
- return true;
21
- }
22
-
23
- function* cover9({ goal, env }) {
24
- // cover9/1 succeeds exactly when a nine-cell block contains each digit once.
25
- // The bit mask makes this a constant-time duplicate check after the list is ground.
26
- const items = properListItems(goal.args[0], env);
27
- if (!items || items.length !== 9) return;
28
- let mask = 0;
29
- for (const item of items) {
30
- const text = lexicalValue(item, env);
31
- if (!/^[1-9]$/.test(text ?? '')) return;
32
- const bit = 1 << (Number(text) - 1);
33
- if (mask & bit) return;
34
- mask |= bit;
35
- }
36
- if (mask === 0x1ff) yield env;
37
- }
38
-
39
- function* sudoku({ solver, goal, env }) {
40
- const cells = parseGrid(goal.args[0], env);
41
- if (!cells) return;
42
- const masks = initMasks(cells);
43
- if (!masks) return;
44
- for (const solved of search(cells, masks)) {
45
- const next = env.clone();
46
- if (unify(goal.args[1], solutionTerm(solved), next)) yield next;
47
- if (solver.solutionsSeen >= solver.solutionLimit) return;
48
- }
49
- }
50
-
51
- function parseGrid(term, env) {
52
- const resolved = deref(term, env);
53
- if (['atom', 'string', 'number'].includes(resolved.type)) return parseGridString(resolved.name);
54
- const rows = properListItems(term, env);
55
- if (!rows || rows.length !== 9) return null;
56
- const cells = [];
57
- for (const row of rows) {
58
- const cols = properListItems(row, env);
59
- if (!cols || cols.length !== 9) return null;
60
- for (const cell of cols) {
61
- const text = lexicalValue(cell, env);
62
- if (!/^\d+$/.test(text ?? '')) return null;
63
- const value = Number(text);
64
- if (value < 0 || value > 9) return null;
65
- cells.push(value);
66
- }
67
- }
68
- return cells;
69
- }
70
-
71
- function parseGridString(text) {
72
- if (!text || text.length !== 81) return null;
73
- const cells = [];
74
- for (const ch of text) {
75
- if (ch === '.' || ch === '_') cells.push(0);
76
- else if (ch >= '0' && ch <= '9') cells.push(Number(ch));
77
- else return null;
78
- }
79
- return cells;
80
- }
81
-
82
- function initMasks(cells) {
83
- const row = Array(9).fill(0), col = Array(9).fill(0), box = Array(9).fill(0);
84
- for (let i = 0; i < 81; i++) {
85
- const value = cells[i];
86
- if (value === 0) continue;
87
- const r = Math.floor(i / 9), c = i % 9, b = Math.floor(r / 3) * 3 + Math.floor(c / 3);
88
- const bit = 1 << (value - 1);
89
- if ((row[r] & bit) || (col[c] & bit) || (box[b] & bit)) return null;
90
- row[r] |= bit; col[c] |= bit; box[b] |= bit;
91
- }
92
- return { row, col, box };
93
- }
94
-
95
- function popcount(mask) {
96
- let n = 0;
97
- while (mask) { n += mask & 1; mask >>= 1; }
98
- return n;
99
- }
100
-
101
- function* search(cells, masks) {
102
- // Choose the empty cell with the fewest legal candidates. This MRV heuristic
103
- // is the main reason the compiled sudoku/2 helper stays fast.
104
- const all = 0x1ff;
105
- let best = -1, bestCandidates = 0, bestCount = 10;
106
- for (let i = 0; i < 81; i++) {
107
- if (cells[i] !== 0) continue;
108
- const r = Math.floor(i / 9), c = i % 9, b = Math.floor(r / 3) * 3 + Math.floor(c / 3);
109
- const candidates = all & ~(masks.row[r] | masks.col[c] | masks.box[b]);
110
- const count = popcount(candidates);
111
- if (count === 0) return;
112
- if (count < bestCount) {
113
- best = i; bestCandidates = candidates; bestCount = count;
114
- if (count === 1) break;
115
- }
116
- }
117
- if (best < 0) {
118
- yield cells.slice();
119
- return;
120
- }
121
- const r = Math.floor(best / 9), c = best % 9, b = Math.floor(r / 3) * 3 + Math.floor(c / 3);
122
- for (let value = 1; value <= 9; value++) {
123
- const bit = 1 << (value - 1);
124
- if (!(bestCandidates & bit)) continue;
125
- cells[best] = value;
126
- masks.row[r] |= bit; masks.col[c] |= bit; masks.box[b] |= bit;
127
- yield* search(cells, masks);
128
- masks.box[b] &= ~bit; masks.col[c] &= ~bit; masks.row[r] &= ~bit;
129
- cells[best] = 0;
130
- }
131
- }
132
-
133
- function solutionTerm(cells) {
134
- const rows = [];
135
- for (let r = 0; r < 9; r++) {
136
- const items = [];
137
- for (let c = 0; c < 9; c++) items.push(numberTerm(cells[r * 9 + c]));
138
- rows.push(listFromItems(items));
139
- }
140
- return listFromItems(rows);
141
- }
@@ -1,5 +0,0 @@
1
- % SPEC 9.7: is_list/1 succeeds only for proper lists.
2
- thing([a, b]).
3
- thing(pair(a, b)).
4
- answer(list, X) :- thing(X), is_list(X).
5
- materialize(answer, 2).
@@ -1,5 +0,0 @@
1
- % SPEC 9.5: atom range helpers generate prefixed atoms deterministically.
2
- answer(single, X) :- atom_range(n, 2, 4, X).
3
- answer(multiple, X) :- atom_ranges([a, b], 1, 2, X).
4
- answer(bound_check, yes) :- atom_range(n, 2, 4, n3).
5
- materialize(answer, 2).
@@ -1,6 +0,0 @@
1
- % SPEC 9.5: cover9/1 accepts exactly the digits 1 through 9 once.
2
- answer(ok, yes) :- cover9([1, 2, 3, 4, 5, 6, 7, 8, 9]).
3
- answer(candidate, X) :- row(X), cover9(X).
4
- row([1, 2, 3, 4, 5, 6, 7, 8, 9]).
5
- row([1, 1, 2, 3, 4, 5, 6, 7, 8]).
6
- materialize(answer, 2).
@@ -1 +0,0 @@
1
- answer(list, [a, b]).
@@ -1,8 +0,0 @@
1
- answer(single, n2).
2
- answer(single, n3).
3
- answer(single, n4).
4
- answer(multiple, a1).
5
- answer(multiple, a2).
6
- answer(multiple, b1).
7
- answer(multiple, b2).
8
- answer(bound_check, yes).
@@ -1,2 +0,0 @@
1
- answer(ok, yes).
2
- answer(candidate, [1, 2, 3, 4, 5, 6, 7, 8, 9]).