eyeling 1.34.0 → 1.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -774,18 +774,19 @@ The Alma RDF Message Log example intentionally keeps the message log as a URL, b
|
|
|
774
774
|
|
|
775
775
|
## Built-ins
|
|
776
776
|
|
|
777
|
-
Eyeling implements SWAP-style built-
|
|
778
|
-
|
|
779
|
-
| Namespace | Built-ins |
|
|
780
|
-
|
|
781
|
-
| `crypto:` | `sha`, `md5`, `sha256`, `sha512` |
|
|
782
|
-
| `math:` | `equalTo`, `notEqualTo`, `greaterThan`, `lessThan`, `notLessThan`, `notGreaterThan`, `sum`, `product`, `difference`, `quotient`, `integerQuotient`, `remainder`, `rounded`, `exponentiation`, `absoluteValue`, `acos`, `asin`, `atan`, `sin`, `cos`, `tan`, `sinh`, `cosh`, `tanh`, `degrees`, `negation` |
|
|
783
|
-
| `time:` | `day`, `hour`, `minute`, `month`, `second`, `timeZone`, `year`, `localTime` |
|
|
784
|
-
| `list:` | `append`, `first`, `rest`, `iterate`, `last`, `memberAt`, `remove`, `member`, `in`, `length`, `notMember`, `reverse`, `sort`, `map`, `firstRest` |
|
|
785
|
-
| `rdf:` | `first`, `rest` |
|
|
786
|
-
| `log:` | `equalTo`, `notEqualTo`, `conjunction`, `conclusion`, `content`, `semantics`, `semanticsOrError`, `parsedAsN3`, `rawType`, `dtlit`, `langlit`, `implies`, `impliedBy`, `query`, `includes`, `notIncludes`, `collectAllIn`, `forAllIn`, `skolem`, `uri`, `trace`, `outputString` |
|
|
787
|
-
| `string:` | `concatenation`, `contains`, `containsIgnoringCase`, `endsWith`, `startsWith`, `equalIgnoringCase`, `notEqualIgnoringCase`, `greaterThan`, `lessThan`, `notGreaterThan`, `notLessThan`, `matches`, `notMatches`, `replace`, `scrape`, `format`, `length`, `charAt`, `setCharAt` |
|
|
788
|
-
| `dt:` | `datatype`, `lexicalForm`, `language`, `validForDatatype`, `invalidForDatatype`, `sameValueAs`, `differentValueFrom`, `canonicalLiteral` |
|
|
777
|
+
Eyeling implements **104 public SWAP-style built-in predicates** for the N3 engine across these namespaces. The authoritative machine-readable catalog is [`eyeling-builtins.ttl`](eyeling-builtins.ttl); this README lists the public names so users do not need to infer support from examples or internal dispatch code.
|
|
778
|
+
|
|
779
|
+
| Namespace | Count | Built-ins |
|
|
780
|
+
|---|---:|---|
|
|
781
|
+
| `crypto:` | 4 | `sha`, `md5`, `sha256`, `sha512` |
|
|
782
|
+
| `math:` | 26 | `equalTo`, `notEqualTo`, `greaterThan`, `lessThan`, `notLessThan`, `notGreaterThan`, `sum`, `product`, `difference`, `quotient`, `integerQuotient`, `remainder`, `rounded`, `exponentiation`, `absoluteValue`, `acos`, `asin`, `atan`, `sin`, `cos`, `tan`, `sinh`, `cosh`, `tanh`, `degrees`, `negation` |
|
|
783
|
+
| `time:` | 8 | `day`, `hour`, `minute`, `month`, `second`, `timeZone`, `year`, `localTime` |
|
|
784
|
+
| `list:` | 15 | `append`, `first`, `rest`, `iterate`, `last`, `memberAt`, `remove`, `member`, `in`, `length`, `notMember`, `reverse`, `sort`, `map`, `firstRest` |
|
|
785
|
+
| `rdf:` | 2 | `first`, `rest` |
|
|
786
|
+
| `log:` | 22 | `equalTo`, `notEqualTo`, `conjunction`, `conclusion`, `content`, `semantics`, `semanticsOrError`, `parsedAsN3`, `rawType`, `dtlit`, `langlit`, `implies`, `impliedBy`, `query`, `includes`, `notIncludes`, `collectAllIn`, `forAllIn`, `skolem`, `uri`, `trace`, `outputString` |
|
|
787
|
+
| `string:` | 19 | `concatenation`, `contains`, `containsIgnoringCase`, `endsWith`, `startsWith`, `equalIgnoringCase`, `notEqualIgnoringCase`, `greaterThan`, `lessThan`, `notGreaterThan`, `notLessThan`, `matches`, `notMatches`, `replace`, `scrape`, `format`, `length`, `charAt`, `setCharAt` |
|
|
788
|
+
| `dt:` | 8 | `datatype`, `lexicalForm`, `language`, `validForDatatype`, `invalidForDatatype`, `sameValueAs`, `differentValueFrom`, `canonicalLiteral` |
|
|
789
|
+
| **Total** | **104** | |
|
|
789
790
|
|
|
790
791
|
The catalog marks each built-in with a coarse kind: `ex:Test`, `ex:Function`, `ex:Relation`, `ex:Generator`, `ex:IO`, `ex:Meta`, or `ex:SideEffect`.
|
|
791
792
|
|
|
@@ -818,20 +819,21 @@ Formula-aware built-ins make Eyeling useful for meta-reasoning. `log:includes`,
|
|
|
818
819
|
|
|
819
820
|
### eyelang built-ins
|
|
820
821
|
|
|
821
|
-
The eyelang engine has its own built-in registry under `lib/eyelang/builtins/`. These are separate from the N3 namespaces above and are called as ordinary eyelang predicates. See the [eyelang language reference](docs/eyelang-language-reference.md#9-standard-built-in-predicates) for the portable profile. The bundled implementation currently registers:
|
|
822
|
-
|
|
823
|
-
| Family | Built-ins |
|
|
824
|
-
|
|
825
|
-
| Core and host | `eq/2`, `neq/2`, `local_time/1`, `difference/3` |
|
|
826
|
-
| Arithmetic and comparison | `neg/2`, `abs/2`, `sin/2`, `cos/2`, `asin/2`, `acos/2`, `rounded/2`, `log/2`, `add/3`, `sub/3`, `mul/3`, `div/3`, `mod/3`, `min/3`, `pow/3`, `lt/2`, `gt/2`, `le/2`, `ge/2`, `between/3`, `smallest_divisor_from/3` |
|
|
827
|
-
| Strings | `str_concat/3`, `contains/2`, `matches/2`, `matches/3`, `not_matches/2` |
|
|
828
|
-
| Lists | `append/3`, `nth0/3`, `set_nth0/4`, `rest/2`, `member/2`, `select/3`, `not_member/2`, `reverse/2`, `length/2`, `sort/2` |
|
|
829
|
-
| Aggregation | `findall/3`, `countall/2`, `sumall/3`, `aggregate_min/5`, `aggregate_max/5` |
|
|
830
|
-
| Control | `not/1`, `once/1` |
|
|
831
|
-
| Context terms | `holds/2`, `holds/3` |
|
|
832
|
-
| Search and optimization helpers | `n_queens/2`, `weighted_hamiltonian_cycle/4`, `weighted_hamiltonian_path/4`, `hamiltonian_cycle/3`, `fixed_length_cycle/4`, `bounded_path/5`, `cnf_model/3`, `qm_prime_implicants/4`, `qm_minimal_cover/4` |
|
|
833
|
-
| Numeric extension helpers | `extended_gcd/5`, `collatz_trajectory/2`, `kaprekar_steps/2`, `goldbach_pair/3` |
|
|
834
|
-
| Matrix helpers | `matrix_sum/2`, `matrix_multiply/2`, `cholesky_decomposition/2`, `determinant/2`, `matrix_inv_triang/2`, `matrix_inversion/2` |
|
|
822
|
+
The eyelang engine has its own built-in registry under `lib/eyelang/builtins/`. These are separate from the N3 namespaces above and are called as ordinary eyelang predicates. See the [eyelang language reference](docs/eyelang-language-reference.md#9-standard-built-in-predicates) for the portable profile. The bundled implementation currently registers 68 name/arity entries across 66 predicate names:
|
|
823
|
+
|
|
824
|
+
| Family | Count | Built-ins |
|
|
825
|
+
|---|---:|---|
|
|
826
|
+
| Core and host | 4 | `eq/2`, `neq/2`, `local_time/1`, `difference/3` |
|
|
827
|
+
| Arithmetic and comparison | 21 | `neg/2`, `abs/2`, `sin/2`, `cos/2`, `asin/2`, `acos/2`, `rounded/2`, `log/2`, `add/3`, `sub/3`, `mul/3`, `div/3`, `mod/3`, `min/3`, `pow/3`, `lt/2`, `gt/2`, `le/2`, `ge/2`, `between/3`, `smallest_divisor_from/3` |
|
|
828
|
+
| Strings | 5 | `str_concat/3`, `contains/2`, `matches/2`, `matches/3`, `not_matches/2` |
|
|
829
|
+
| Lists | 10 | `append/3`, `nth0/3`, `set_nth0/4`, `rest/2`, `member/2`, `select/3`, `not_member/2`, `reverse/2`, `length/2`, `sort/2` |
|
|
830
|
+
| Aggregation | 5 | `findall/3`, `countall/2`, `sumall/3`, `aggregate_min/5`, `aggregate_max/5` |
|
|
831
|
+
| Control | 2 | `not/1`, `once/1` |
|
|
832
|
+
| Context terms | 2 | `holds/2`, `holds/3` |
|
|
833
|
+
| Search and optimization helpers | 9 | `n_queens/2`, `weighted_hamiltonian_cycle/4`, `weighted_hamiltonian_path/4`, `hamiltonian_cycle/3`, `fixed_length_cycle/4`, `bounded_path/5`, `cnf_model/3`, `qm_prime_implicants/4`, `qm_minimal_cover/4` |
|
|
834
|
+
| Numeric extension helpers | 4 | `extended_gcd/5`, `collatz_trajectory/2`, `kaprekar_steps/2`, `goldbach_pair/3` |
|
|
835
|
+
| Matrix helpers | 6 | `matrix_sum/2`, `matrix_multiply/2`, `cholesky_decomposition/2`, `determinant/2`, `matrix_inv_triang/2`, `matrix_inversion/2` |
|
|
836
|
+
| **Total** | **68** | |
|
|
835
837
|
|
|
836
838
|
## Custom built-ins
|
|
837
839
|
|
|
@@ -1128,6 +1130,7 @@ The repository contains more than two hundred N3 examples under `examples/`, eye
|
|
|
1128
1130
|
| `examples/string-builtins-tests.n3` | String built-ins. |
|
|
1129
1131
|
| `examples/math-builtins-tests.n3` | Numeric built-ins. |
|
|
1130
1132
|
| `examples/rdf-messages.n3` | RDF Message Log replay. |
|
|
1133
|
+
| `examples/context-schema-audit.n3` | Quoted-context schema validation with `log:includes` and list arity checks. |
|
|
1131
1134
|
|
|
1132
1135
|
### Running all examples through tests
|
|
1133
1136
|
|
package/docs/eyelang-guide.md
CHANGED
|
@@ -261,7 +261,7 @@ best(Cycle, Cost) :-
|
|
|
261
261
|
weighted_hamiltonian_cycle(edge, Cities, Cycle, Cost).
|
|
262
262
|
```
|
|
263
263
|
|
|
264
|
-
The reusable search and numeric helpers include `n_queens/2`, Hamiltonian path/cycle helpers, `bounded_path/5`, `cnf_model/3`, Quine-McCluskey helpers, number-theory helpers such as `extended_gcd/5`, and matrix helpers such as `matrix_multiply/2`. These helpers are extension builtins of this implementation; [the eyelang language reference](eyelang-language-reference.md) defines the portable core and standard builtin profile.
|
|
264
|
+
The reusable search and numeric helpers include `n_queens/2`, Hamiltonian path/cycle helpers, `bounded_path/5`, `cnf_model/3`, Quine-McCluskey helpers, number-theory helpers such as `extended_gcd/5`, and matrix helpers such as `matrix_multiply/2`. These helpers are extension builtins of this implementation; [the eyelang language reference](eyelang-language-reference.md) defines the portable core and standard builtin profile. The complete bundled implementation list is kept in the top-level [README built-ins section](../README.md#built-ins-1), and the regression suite checks that table against the actual runtime registry.
|
|
265
265
|
|
|
266
266
|
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.
|
|
267
267
|
|
|
@@ -300,6 +300,8 @@ Use `holds/2` when you want to match the member term directly, for example `name
|
|
|
300
300
|
|
|
301
301
|
`matches/3` can create context data from named regular-expression captures, which is useful when text logs or messages need to become facts before later rules inspect them with `holds/2` or `holds/3`. See [`observability-log-correlation.pl`](../examples/eyelang/observability-log-correlation.pl) for a complete log-correlation example.
|
|
302
302
|
|
|
303
|
+
The N3 counterpart of the context schema audit lives at [`examples/context-schema-audit.n3`](../examples/context-schema-audit.n3) with golden output in [`examples/output/context-schema-audit.md`](../examples/output/context-schema-audit.md).
|
|
304
|
+
|
|
303
305
|
|
|
304
306
|
## Example catalog
|
|
305
307
|
|
|
@@ -450,6 +450,8 @@ The first goal can yield `holds((name(alice, "Alice"), knows(alice, bob)), name(
|
|
|
450
450
|
|
|
451
451
|
`holds/3` is the appropriate form for schema-style introspection because it exposes the predicate name and all arguments without assuming a fixed arity. For example, a single rule can inspect `heartbeat`, `source(sensor17)`, `temperature(sensor17, 38)`, and `signature(sensor17, sha256, Hash, Time)` as `heartbeat/0`, `source/1`, `temperature/2`, and `signature/4`; see [`context-schema-audit.pl`](../examples/eyelang/context-schema-audit.pl).
|
|
452
452
|
|
|
453
|
+
The N3 example [`context-schema-audit.n3`](../examples/context-schema-audit.n3) shows the same idea in quoted graph form: members are encoded as predicates with RDF-list argument objects, then `log:includes` and `list:length` expose `Name + Arity` for schema checking.
|
|
454
|
+
|
|
453
455
|
### 9.10 Search control
|
|
454
456
|
|
|
455
457
|
| Built-in | Meaning |
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Context schema audit
|
|
2
|
+
#
|
|
3
|
+
# This is the N3 counterpart of the eyelang context-schema-audit example. Each
|
|
4
|
+
# message carries a quoted context graph whose members encode an event name as
|
|
5
|
+
# the predicate and its arguments as an RDF list. A generic rule uses
|
|
6
|
+
# log:includes to inspect those quoted contexts, list:length to compute arity,
|
|
7
|
+
# and schema rules to flag unexpected or wrong-arity members.
|
|
8
|
+
|
|
9
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
10
|
+
@prefix list: <http://www.w3.org/2000/10/swap/list#>.
|
|
11
|
+
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
12
|
+
@prefix : <http://example.org/#>.
|
|
13
|
+
|
|
14
|
+
:msg_ok :context {
|
|
15
|
+
:entry :heartbeat ().
|
|
16
|
+
:entry :source (:sensor17).
|
|
17
|
+
:entry :temperature (:sensor17 38).
|
|
18
|
+
:entry :gps (:sensor17 51 4).
|
|
19
|
+
:entry :signature (:sensor17 :sha256 "9f86d081" "2026-06-18T09:30:00Z").
|
|
20
|
+
}.
|
|
21
|
+
|
|
22
|
+
:msg_bad :context {
|
|
23
|
+
:entry :heartbeat ().
|
|
24
|
+
:entry :source (:sensor18).
|
|
25
|
+
:entry :temperature (:sensor18 99).
|
|
26
|
+
:entry :gps (:sensor18 51).
|
|
27
|
+
:entry :tampered (:sensor18).
|
|
28
|
+
}.
|
|
29
|
+
|
|
30
|
+
:heartbeat :allowedArity 0.
|
|
31
|
+
:source :allowedArity 1.
|
|
32
|
+
:temperature :allowedArity 2.
|
|
33
|
+
:gps :allowedArity 3.
|
|
34
|
+
:signature :allowedArity 4.
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
?Message :context ?Context.
|
|
38
|
+
?Context log:includes { :entry ?Name ?Args }.
|
|
39
|
+
?Args list:length ?Arity.
|
|
40
|
+
}
|
|
41
|
+
=>
|
|
42
|
+
{
|
|
43
|
+
?Message :contextShape (?Name ?Arity).
|
|
44
|
+
}.
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
?Message :contextShape (?Name ?Arity).
|
|
48
|
+
?Name :allowedArity ?AllowedArity.
|
|
49
|
+
?Arity math:notEqualTo ?AllowedArity.
|
|
50
|
+
}
|
|
51
|
+
=>
|
|
52
|
+
{
|
|
53
|
+
?Message :schemaViolation (?Name ?Arity).
|
|
54
|
+
}.
|
|
55
|
+
|
|
56
|
+
{
|
|
57
|
+
?Message :contextShape (?Name ?Arity).
|
|
58
|
+
1 log:notIncludes { ?Name :allowedArity ?AnyArity. }.
|
|
59
|
+
}
|
|
60
|
+
=>
|
|
61
|
+
{
|
|
62
|
+
?Message :schemaViolation (?Name ?Arity).
|
|
63
|
+
}.
|
|
64
|
+
|
|
65
|
+
{
|
|
66
|
+
:msg_bad :schemaViolation (:gps 2).
|
|
67
|
+
:msg_bad :schemaViolation (:tampered 1).
|
|
68
|
+
}
|
|
69
|
+
=>
|
|
70
|
+
{
|
|
71
|
+
:report log:outputString "# Context schema audit\n\n## Source files\n\n- [N3 rules](../context-schema-audit.n3)\n\n## Entailment\nThe audit inspects quoted message contexts and finds two schema violations in `msg_bad`: `gps/2` should have arity 3, and `tampered/1` is not an allowed context member.\n\n## Explanation\nEach context member is encoded as a quoted triple whose predicate is the member name and whose object is the argument list. The generic audit rule uses `log:includes` to bind the member name and argument list, then `list:length` to compute the arity. Allowed shapes are declared once as `:allowedArity` facts, so the same rule can validate zero-, unary-, binary-, ternary-, and four-argument members without writing a separate rule per predicate.\n".
|
|
72
|
+
}.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Context schema audit
|
|
2
|
+
|
|
3
|
+
## Source files
|
|
4
|
+
|
|
5
|
+
- [N3 rules](../context-schema-audit.n3)
|
|
6
|
+
|
|
7
|
+
## Entailment
|
|
8
|
+
The audit inspects quoted message contexts and finds two schema violations in `msg_bad`: `gps/2` should have arity 3, and `tampered/1` is not an allowed context member.
|
|
9
|
+
|
|
10
|
+
## Explanation
|
|
11
|
+
Each context member is encoded as a quoted triple whose predicate is the member name and whose object is the argument list. The generic audit rule uses `log:includes` to bind the member name and argument list, then `list:length` to compute the arity. Allowed shapes are declared once as `:allowedArity` facts, so the same rule can validate zero-, unary-, binary-, ternary-, and four-argument members without writing a separate rule per predicate.
|
package/package.json
CHANGED
|
@@ -196,6 +196,18 @@ why(
|
|
|
196
196
|
assertEqual(readmeExamples.join('\n'), examples.join('\n'), 'README example catalog');
|
|
197
197
|
},
|
|
198
198
|
},
|
|
199
|
+
{
|
|
200
|
+
name: 'README mirrors the eyelang builtin registry',
|
|
201
|
+
run: () => {
|
|
202
|
+
const actual = registeredBuiltinNames();
|
|
203
|
+
const documented = readmeBuiltinNames();
|
|
204
|
+
assertEqual(documented.join('\n'), actual.join('\n'), 'README builtin catalog');
|
|
205
|
+
|
|
206
|
+
const { entries, names } = readmeBuiltinSummary();
|
|
207
|
+
assertEqual(entries, actual.length, 'README builtin entry count');
|
|
208
|
+
assertEqual(names, new Set(actual.map((item) => item.split('/')[0])).size, 'README builtin name count');
|
|
209
|
+
},
|
|
210
|
+
},
|
|
199
211
|
{
|
|
200
212
|
name: 'stdin input is accepted',
|
|
201
213
|
run: () => {
|
|
@@ -499,6 +511,27 @@ function readmeCatalogExampleNames() {
|
|
|
499
511
|
.sort();
|
|
500
512
|
}
|
|
501
513
|
|
|
514
|
+
function registeredBuiltinNames() {
|
|
515
|
+
return [...createDefaultRegistry().defs.keys()].sort();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function readmeBuiltinNames() {
|
|
519
|
+
const readme = fs.readFileSync(path.join(packageRoot, 'README.md'), 'utf8');
|
|
520
|
+
const section = between(readme, '### eyelang built-ins', '## Custom built-ins');
|
|
521
|
+
return [...section.matchAll(/`([A-Za-z_][A-Za-z0-9_]*)\/(\d+)`/g)]
|
|
522
|
+
.map((match) => `${match[1]}/${match[2]}`)
|
|
523
|
+
.filter((name, index, names) => names.indexOf(name) === index)
|
|
524
|
+
.sort();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function readmeBuiltinSummary() {
|
|
528
|
+
const readme = fs.readFileSync(path.join(packageRoot, 'README.md'), 'utf8');
|
|
529
|
+
const section = between(readme, '### eyelang built-ins', '## Custom built-ins');
|
|
530
|
+
const match = section.match(/currently registers (\d+) name\/arity entries across (\d+) predicate names/);
|
|
531
|
+
if (match == null) throw new Error('README builtin summary not found');
|
|
532
|
+
return { entries: Number(match[1]), names: Number(match[2]) };
|
|
533
|
+
}
|
|
534
|
+
|
|
502
535
|
function playgroundExampleNames() {
|
|
503
536
|
const html = fs.readFileSync(path.join(root, 'playground.html'), 'utf8');
|
|
504
537
|
const match = html.match(/const EXAMPLES = \[(.*?)\];/s);
|