eyeling 1.33.4 → 1.33.6
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/README.md +50 -105
- package/docs/eyelang-guide.md +551 -0
- package/{examples/eyelang/SPEC.md → docs/eyelang-language-reference.md} +6 -10
- package/lib/eyelang/builtins/arithmetic.js +1 -3
- package/lib/eyelang/builtins/formula.js +0 -16
- package/lib/eyelang/builtins/lists.js +0 -5
- package/lib/eyelang/builtins/registry.js +1 -2
- package/lib/eyelang/builtins/search.js +1 -61
- package/lib/eyelang/builtins/strings.js +10 -14
- package/package.json +2 -1
- package/test/eyelang/conformance/README.md +7 -7
- package/test/eyelang/conformance/cases/core/001_fact_output.pl +1 -1
- package/test/eyelang/conformance/cases/core/002_rule_recursion.pl +1 -1
- package/test/eyelang/conformance/cases/core/003_terms_and_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/004_conjunction_and_parentheses.pl +1 -1
- package/test/eyelang/conformance/cases/core/005_list_deconstruction.pl +1 -1
- package/test/eyelang/conformance/cases/core/006_comma_formula_data.pl +1 -1
- package/test/eyelang/conformance/cases/core/007_anonymous_variables.pl +1 -1
- package/test/eyelang/conformance/cases/core/008_graphic_atoms.pl +1 -1
- package/test/eyelang/conformance/cases/core/009_comments_and_whitespace.pl +1 -1
- package/test/eyelang/conformance/cases/core/010_variable_scope_and_reuse.pl +1 -1
- package/test/eyelang/conformance/cases/core/011_predicate_arity.pl +1 -1
- package/test/eyelang/conformance/cases/core/012_nested_compound_unification.pl +1 -1
- package/test/eyelang/conformance/cases/core/013_multiple_clauses_order.pl +1 -1
- package/test/eyelang/conformance/cases/core/014_failure_filters_answers.pl +1 -1
- package/test/eyelang/conformance/cases/core/015_improper_list_unification.pl +1 -1
- package/test/eyelang/conformance/cases/core/016_zero_arity_compound.pl +1 -1
- package/test/eyelang/conformance/cases/core/017_three_step_recursion.pl +1 -1
- package/test/eyelang/conformance/cases/core/018_quoted_atom_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/019_parenthesized_three_conjuncts.pl +1 -1
- package/test/eyelang/conformance/cases/core/020_nested_list_terms.pl +1 -1
- package/test/eyelang/conformance/cases/core/021_repeated_variable_head.pl +1 -1
- package/test/eyelang/conformance/cases/core/022_rule_head_structure.pl +1 -1
- package/test/eyelang/conformance/cases/core/023_quoted_escapes_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/024_numeric_literal_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/025_body_parentheses_with_formula_data.pl +1 -1
- package/test/eyelang/conformance/cases/core/026_underscore_named_variable_reuse.pl +1 -1
- package/test/eyelang/conformance/cases/extension/001_default_derived_output.pl +1 -1
- package/test/eyelang/conformance/cases/extension/002_materialize_focus.pl +1 -1
- package/test/eyelang/conformance/cases/extension/003_arithmetic_and_comparison.pl +1 -2
- package/test/eyelang/conformance/cases/extension/004_strings_and_atoms.pl +1 -3
- package/test/eyelang/conformance/cases/extension/005_lists_aggregation_ordering.pl +1 -1
- package/test/eyelang/conformance/cases/extension/006_formula_terms.pl +1 -2
- package/test/eyelang/conformance/cases/extension/007_negation_once_generators.pl +1 -1
- package/test/eyelang/conformance/cases/extension/008_equality_and_inequality.pl +1 -1
- package/test/eyelang/conformance/cases/extension/009_list_relations.pl +1 -2
- package/test/eyelang/conformance/cases/extension/010_append_splits.pl +1 -1
- package/test/eyelang/conformance/cases/extension/011_matching_and_comparison.pl +1 -1
- package/test/eyelang/conformance/cases/extension/012_memoize_declaration.pl +1 -1
- package/test/eyelang/conformance/cases/extension/013_numeric_functions.pl +1 -1
- package/test/eyelang/conformance/cases/extension/014_between_enumeration.pl +1 -1
- package/test/eyelang/conformance/cases/extension/015_smallest_divisor.pl +1 -1
- package/test/eyelang/conformance/cases/extension/016_negation_filter.pl +1 -1
- package/test/eyelang/conformance/cases/extension/017_once_user_predicate.pl +1 -1
- package/test/eyelang/conformance/cases/extension/018_findall_user_goal.pl +1 -1
- package/test/eyelang/conformance/cases/extension/019_sort_deduplicates_atoms.pl +1 -1
- package/test/eyelang/conformance/cases/extension/020_append_bound_prefix_suffix.pl +1 -1
- package/test/eyelang/conformance/cases/extension/021_nth0_index_generation.pl +1 -1
- package/test/eyelang/conformance/cases/extension/022_set_nth0_edges.pl +1 -1
- package/test/eyelang/conformance/cases/extension/023_select_duplicate_occurrences.pl +1 -1
- package/test/eyelang/conformance/cases/extension/024_not_member_filter.pl +1 -1
- package/test/eyelang/conformance/cases/extension/026_nested_formula_terms.pl +1 -2
- package/test/eyelang/conformance/cases/extension/027_materialize_excludes_source_fact.pl +1 -1
- package/test/eyelang/conformance/cases/extension/028_numeric_and_lexical_comparison.pl +1 -1
- package/test/eyelang/conformance/cases/extension/029_string_matching_filters.pl +1 -1
- package/test/eyelang/conformance/cases/extension/030_string_and_atom_concat.pl +1 -2
- package/test/eyelang/conformance/cases/extension/035_date_difference.pl +1 -1
- package/test/eyelang/conformance/cases/extension/036_extended_gcd.pl +1 -1
- package/test/eyelang/conformance/cases/extension/037_collatz_trajectory.pl +1 -1
- package/test/eyelang/conformance/cases/extension/038_kaprekar_steps.pl +1 -1
- package/test/eyelang/conformance/cases/extension/039_goldbach_pair.pl +1 -1
- package/test/eyelang/conformance/cases/extension/040_matrix_operations.pl +1 -1
- package/test/eyelang/conformance/cases/extension/042_n_queens_small.pl +1 -1
- package/test/eyelang/conformance/cases/extension/043_cnf_model.pl +1 -1
- package/test/eyelang/conformance/cases/extension/045_alphametic_sum_small.pl +1 -1
- package/test/eyelang/conformance/cases/extension/046_bounded_subset.pl +1 -1
- package/test/eyelang/conformance/expected/extension/003_arithmetic_and_comparison.out +0 -1
- package/test/eyelang/conformance/expected/extension/004_strings_and_atoms.out +0 -2
- package/test/eyelang/conformance/expected/extension/006_formula_terms.out +0 -2
- package/test/eyelang/conformance/expected/extension/009_list_relations.out +0 -1
- package/test/eyelang/conformance/expected/extension/026_nested_formula_terms.out +0 -3
- package/test/eyelang/conformance/expected/extension/030_string_and_atom_concat.out +0 -1
- package/test/eyelang/run-regression.mjs +2 -2
- package/test/packlist.test.js +2 -0
- package/examples/eyelang/INTEGRATION.md +0 -20
- package/examples/eyelang/README.md +0 -567
- package/examples/eyelang/exact-cover-sudoku.pl +0 -113
- package/examples/eyelang/output/exact-cover-sudoku.pl +0 -3
- package/examples/eyelang/output/sudoku.pl +0 -2
- package/examples/eyelang/sudoku.pl +0 -20
- package/lib/eyelang/builtins/sudoku.js +0 -141
- package/test/eyelang/conformance/cases/extension/025_is_list_filter.pl +0 -5
- package/test/eyelang/conformance/cases/extension/025_is_list_filter.query +0 -1
- package/test/eyelang/conformance/cases/extension/041_atom_range_generators.pl +0 -5
- package/test/eyelang/conformance/cases/extension/044_cover9_filter.pl +0 -6
- package/test/eyelang/conformance/expected/extension/025_is_list_filter.out +0 -1
- package/test/eyelang/conformance/expected/extension/041_atom_range_generators.out +0 -8
- package/test/eyelang/conformance/expected/extension/044_cover9_filter.out +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# eyelang Language
|
|
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
|
|
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. `
|
|
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
|
-
| `
|
|
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.
|
|
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
|
|
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
|
|
|
@@ -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', '
|
|
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,
|
|
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,
|
|
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
|
|
1
|
+
// String builtins.
|
|
2
2
|
// They mostly project from already-ground terms to avoid guessing string domains.
|
|
3
|
-
import {
|
|
3
|
+
import { lexicalValue, stringTerm, unify } from '../term.js';
|
|
4
4
|
|
|
5
5
|
export const stringBuiltins = {
|
|
6
6
|
register(registry) {
|
|
7
|
-
|
|
8
|
-
for (const name of ['contains', '
|
|
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(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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.
|
|
3
|
+
"version": "1.33.6",
|
|
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 [
|
|
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
|
-
|
|
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.
|
|
30
|
-
node test/run-conformance.
|
|
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
|
|
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
|
|
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
|
-
%
|
|
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
|
-
%
|
|
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
|
-
%
|
|
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)]).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
%
|
|
1
|
+
% Reference 5.3, 6, 7.1: structured rule heads destructure matching goals.
|
|
2
2
|
unpack(pair(X, Y), X, Y).
|
|
3
3
|
answer(first, A) :- unpack(pair(alpha, beta), A, _).
|
|
4
4
|
answer(second, B) :- unpack(pair(alpha, beta), _, B).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
%
|
|
1
|
+
% Reference 9.2, 9.3: arithmetic and comparison built-ins.
|
|
2
2
|
answer(sum, X) :- add(2, 3, X).
|
|
3
3
|
answer(diff, X) :- sub(7, 4, X).
|
|
4
4
|
answer(product, X) :- mul(6, 7, X).
|
|
@@ -6,7 +6,6 @@ answer(integer_division, X) :- div(7, 2, X).
|
|
|
6
6
|
answer(remainder, X) :- mod(7, 2, X).
|
|
7
7
|
answer(power, X) :- pow(2, 8, X).
|
|
8
8
|
answer(minimum, X) :- min(3, 9, X).
|
|
9
|
-
answer(maximum, X) :- max(3, 9, X).
|
|
10
9
|
answer(less_than, true) :- lt(3, 9).
|
|
11
10
|
answer(greater_equal, true) :- ge(9, 9).
|
|
12
11
|
materialize(answer, 2).
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
%
|
|
2
|
-
answer(atom_concat, X) :- atom_concat(eye, lang, X).
|
|
1
|
+
% Reference 9.6: atom and string built-ins.
|
|
3
2
|
answer(str_concat, X) :- str_concat("eye", "lang", X).
|
|
4
3
|
answer(contains, true) :- contains("eyelang", "lang").
|
|
5
|
-
answer(not_contains, true) :- not_contains("eyelang", "cat").
|
|
6
4
|
materialize(answer, 2).
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
%
|
|
1
|
+
% Reference 9.9: formula_binary/4 over comma formula data.
|
|
2
2
|
formula((name(alice, "Alice"), knows(alice, bob))).
|
|
3
|
-
answer(atom, A) :- formula(F), formula_atom(F, A).
|
|
4
3
|
answer(binary, exposed(S, P, O)) :- formula(F), formula_binary(F, S, P, O).
|
|
5
4
|
materialize(answer, 2).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
%
|
|
1
|
+
% Reference 9.1: eq/2 unifies terms and neq/2 succeeds on non-unifiable terms.
|
|
2
2
|
answer(eq_variable, X) :- eq(X, pair(a, [b, c])).
|
|
3
3
|
answer(eq_nested, true) :- eq(pair(X, X), pair(same, same)).
|
|
4
4
|
answer(neq_atoms, true) :- neq(alice, bob).
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
%
|
|
1
|
+
% Reference 9.7: rest/2, select/3, and not_member/2.
|
|
2
2
|
answer(rest, X) :- rest([a, b, c], X).
|
|
3
3
|
answer(select, selected(X, R)) :- select(X, [a, b], R).
|
|
4
4
|
answer(not_member, true) :- not_member(c, [a, b]).
|
|
5
|
-
answer(is_list, true) :- is_list([a, b]).
|
|
6
5
|
materialize(answer, 2).
|