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-ins 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.
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
 
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.34.0",
3
+ "version": "1.34.2",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -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);