@metta-ts/edsl 1.0.0
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/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +247 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MesTTo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @metta-ts/edsl
|
|
2
|
+
|
|
3
|
+
A typed TypeScript eDSL for [MeTTa TS](https://github.com/MesTTo/Meta-TypeScript-Talk). Write MeTTa with typed term builders and special-form combinators, or a tagged-template surface, and run it on the real interpreter. Any TypeScript value drops in as a grounded atom automatically.
|
|
4
|
+
|
|
5
|
+
It is a thin layer over [`@metta-ts/hyperon`](https://github.com/MesTTo/Meta-TypeScript-Talk/tree/main/packages/hyperon): every builder produces an ordinary atom that runs on the existing engine, so you get MeTTa's full semantics: rewrite rules, nondeterminism, pattern matching, and types.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @metta-ts/edsl
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { mettaDB, S, v, rel, iff, gt, lt, mul, sub, m, ValueAtom, type GroundedAtom } from "@metta-ts/edsl";
|
|
17
|
+
|
|
18
|
+
const db = mettaDB();
|
|
19
|
+
|
|
20
|
+
// Facts + a typed match query (rows are typed by the variables).
|
|
21
|
+
db.add(rel("Likes")(S.Ada, S.Coffee), rel("Likes")(S.Ada, S.Chocolate));
|
|
22
|
+
const thing = v<string>("thing");
|
|
23
|
+
db.query(rel("Likes")(S.Ada, thing), { thing }); // [{ thing: "Coffee" }, { thing: "Chocolate" }]
|
|
24
|
+
|
|
25
|
+
// Rewrite rules + grounded arithmetic, recursion, nondeterminism.
|
|
26
|
+
const x = v<number>("x");
|
|
27
|
+
db.rule(rel("fact")(x), iff(gt(x, 0), mul(x, rel("fact")(sub(x, 1))), 1));
|
|
28
|
+
db.evalJs(rel("fact")(5)); // [120]
|
|
29
|
+
|
|
30
|
+
// Pass a TypeScript object straight in (auto-grounded), operated on by a TS function.
|
|
31
|
+
db.op("balance-of", (args) => [ValueAtom((args[0] as GroundedAtom).jsValue<{ balance: number }>().balance)]);
|
|
32
|
+
const account = { owner: "Tom", balance: 100 };
|
|
33
|
+
db.evalJs(rel("balance-of")(account)); // [100]
|
|
34
|
+
db.evalJs(m`(balance-of ${account})`); // [100], same, via the template
|
|
35
|
+
|
|
36
|
+
// Async grounded ops, awaited.
|
|
37
|
+
db.asyncOp("fetch", async () => [ValueAtom(await getTemp())]);
|
|
38
|
+
await db.evalJsAsync(rel("fetch")());
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## The two surfaces
|
|
42
|
+
|
|
43
|
+
- Typed builders construct terms with static types: `S`/`v`/`e`/`rel`, `rule`/`decl`/`arrow`, the special forms `iff`/`caseOf`/`lett`/`letStar`/`matchSelf`/`superpose`/`collapse`/`empty`/`unify`, the grounded ops `add`/`sub`/`mul`/`div`/`mod`, `eq`/`gt`/`lt`/`ge`/`le`, `and`/`or`/`not`, `carAtom`/`cdrAtom`/`consAtom`, and `list`/`nil`. Builders compose, so nested patterns and repeated variables are just nested calls.
|
|
44
|
+
- The tagged template `m\`...\`` (and `mAll` for several atoms) runs the real parser, so it expresses every MeTTa form, and `${value}` auto-grounds (the easiest way to drop a TS object in).
|
|
45
|
+
|
|
46
|
+
## The runner
|
|
47
|
+
|
|
48
|
+
`mettaDB()` keeps MeTTa's two query mechanisms distinct: `query(pattern, vars)` does `match &self` over stored atoms and returns typed binding rows; `eval(atom)` (and `evalJs`, `evalAsync`, `evalJsAsync`) rewrites with the `=` rules and returns the nondeterministic results. `op`/`asyncOp` register TypeScript functions as grounded operations. `ground(x)` is the primitive behind auto-grounding.
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
[MIT](https://github.com/MesTTo/Meta-TypeScript-Talk/blob/main/LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Atom, VariableAtom, SymbolAtom, ExpressionAtom, MeTTa } from '@metta-ts/hyperon';
|
|
2
|
+
export { Atom, GroundedAtom, ValueAtom, atomToJs } from '@metta-ts/hyperon';
|
|
3
|
+
|
|
4
|
+
/** A typed variable. The phantom `T` is the JS type its binding unwraps to in a query result; it is a
|
|
5
|
+
* compile-time promise, while the runtime value is whatever unifies. */
|
|
6
|
+
type Var<T = unknown> = VariableAtom & {
|
|
7
|
+
readonly __varType?: T;
|
|
8
|
+
};
|
|
9
|
+
/** Anything a builder accepts in term position: an atom (incl. a {@link Var}), or a JS value to ground. */
|
|
10
|
+
type Term = Atom | number | string | boolean | bigint | object | null | undefined;
|
|
11
|
+
/** Extract a {@link Var}'s phantom type (defaults to `unknown`). */
|
|
12
|
+
type VarValue<X> = X extends Var<infer T> ? T : unknown;
|
|
13
|
+
/** A fresh typed variable: `v("x")` or `v<number>("n")`. */
|
|
14
|
+
declare const v: <T = unknown>(name: string) => Var<T>;
|
|
15
|
+
/** `S` builds a symbol two ways: as a function `S("foo")` or as a property `S.foo`. */
|
|
16
|
+
type SymbolBuilder = ((name: string) => SymbolAtom) & {
|
|
17
|
+
readonly [key: string]: SymbolAtom;
|
|
18
|
+
};
|
|
19
|
+
declare const S: SymbolBuilder;
|
|
20
|
+
/** Coerce a {@link Term} to an atom: atoms and variables pass through; every other JS value is grounded. */
|
|
21
|
+
declare function ground(x: Term): Atom;
|
|
22
|
+
/** A raw expression (tuple) from its items, each auto-grounded: `e(x, y, x)` builds `($x $y $x)`. Use it
|
|
23
|
+
* for patterns that are not headed by a functor, e.g. repeated-variable patterns or pair structures. */
|
|
24
|
+
declare const e: (...items: Term[]) => ExpressionAtom;
|
|
25
|
+
/** A functor builder: `rel("parent")` returns `(a, b) => (parent a b)`, auto-grounding each argument.
|
|
26
|
+
* Builders compose, so nested patterns like `rel("swap")(rel("Pair")(x, y))` are just function calls. */
|
|
27
|
+
declare function rel(name: string): (...args: Term[]) => ExpressionAtom;
|
|
28
|
+
/** The empty expression `()`, MeTTa's conventional empty/nil list. */
|
|
29
|
+
declare const nil: () => ExpressionAtom;
|
|
30
|
+
/** A Lisp-style cons list: `list([a, b, c])` builds `(:: a (:: b (:: c ())))`. Override the constructor
|
|
31
|
+
* and terminator symbols if your code uses different ones. */
|
|
32
|
+
declare function list(items: Term[], opts?: {
|
|
33
|
+
cons?: string;
|
|
34
|
+
nil?: Atom;
|
|
35
|
+
}): Atom;
|
|
36
|
+
|
|
37
|
+
/** A rewrite rule `(= head body)`. Define several with the same head for nondeterministic results. */
|
|
38
|
+
declare const rule: (head: Term, body: Term) => ExpressionAtom;
|
|
39
|
+
/** A type declaration `(: subject type)`. */
|
|
40
|
+
declare const decl: (subject: Term, type: Term) => ExpressionAtom;
|
|
41
|
+
/** A function type `(-> A B ... R)`. */
|
|
42
|
+
declare const arrow: (...types: Term[]) => ExpressionAtom;
|
|
43
|
+
/** `(if cond then else)`. Only the taken branch is evaluated. */
|
|
44
|
+
declare const iff: (cond: Term, then: Term, els: Term) => ExpressionAtom;
|
|
45
|
+
/** `(case scrutinee ((pat body) ...))`, sequential mutually-exclusive pattern matching. */
|
|
46
|
+
declare const caseOf: (scrutinee: Term, cases: ReadonlyArray<readonly [Term, Term]>) => ExpressionAtom;
|
|
47
|
+
/** `(let pattern value body)`: unify `value` against `pattern`, then evaluate `body`. */
|
|
48
|
+
declare const lett: (pattern: Term, value: Term, body: Term) => ExpressionAtom;
|
|
49
|
+
/** `(let* ((pat val) ...) body)`: sequential lets. */
|
|
50
|
+
declare const letStar: (bindings: ReadonlyArray<readonly [Term, Term]>, body: Term) => ExpressionAtom;
|
|
51
|
+
/** `(match space pattern template)`. Defaults to `&self`, the program's own space. */
|
|
52
|
+
declare const matchSelf: (pattern: Term, template: Term, space?: Term) => ExpressionAtom;
|
|
53
|
+
/** `(superpose (a b ...))`: a nondeterministic choice among the items. */
|
|
54
|
+
declare const superpose: (...items: Term[]) => ExpressionAtom;
|
|
55
|
+
/** `(collapse x)`: gather all nondeterministic results of `x` into a single expression. */
|
|
56
|
+
declare const collapse: (x: Term) => ExpressionAtom;
|
|
57
|
+
/** `(empty)`: no results, which prunes a branch. */
|
|
58
|
+
declare const empty: () => ExpressionAtom;
|
|
59
|
+
/** `(unify a b then else)`: low-level unification with then/else continuations. */
|
|
60
|
+
declare const unify: (a: Term, b: Term, then: Term, els: Term) => ExpressionAtom;
|
|
61
|
+
/** Arithmetic grounded operations. */
|
|
62
|
+
declare const add: (a: Term, b: Term) => ExpressionAtom;
|
|
63
|
+
declare const sub: (a: Term, b: Term) => ExpressionAtom;
|
|
64
|
+
declare const mul: (a: Term, b: Term) => ExpressionAtom;
|
|
65
|
+
declare const div: (a: Term, b: Term) => ExpressionAtom;
|
|
66
|
+
declare const mod: (a: Term, b: Term) => ExpressionAtom;
|
|
67
|
+
/** Comparison grounded operations (return `True`/`False`). */
|
|
68
|
+
declare const eq: (a: Term, b: Term) => ExpressionAtom;
|
|
69
|
+
declare const gt: (a: Term, b: Term) => ExpressionAtom;
|
|
70
|
+
declare const lt: (a: Term, b: Term) => ExpressionAtom;
|
|
71
|
+
declare const ge: (a: Term, b: Term) => ExpressionAtom;
|
|
72
|
+
declare const le: (a: Term, b: Term) => ExpressionAtom;
|
|
73
|
+
/** Boolean grounded operations. */
|
|
74
|
+
declare const and: (a: Term, b: Term) => ExpressionAtom;
|
|
75
|
+
declare const or: (a: Term, b: Term) => ExpressionAtom;
|
|
76
|
+
declare const not: (x: Term) => ExpressionAtom;
|
|
77
|
+
/** Expression/list grounded operations. */
|
|
78
|
+
declare const carAtom: (x: Term) => ExpressionAtom;
|
|
79
|
+
declare const cdrAtom: (x: Term) => ExpressionAtom;
|
|
80
|
+
declare const consAtom: (head: Term, tail: Term) => ExpressionAtom;
|
|
81
|
+
/** `(decons-atom expr)`: split a non-empty expression into `(head tail)`. */
|
|
82
|
+
declare const deconsAtom: (x: Term) => ExpressionAtom;
|
|
83
|
+
/** `(quote x)`: hold `x` as data so the interpreter does not evaluate it. */
|
|
84
|
+
declare const quote: (x: Term) => ExpressionAtom;
|
|
85
|
+
/** Type introspection. `getType` reports an atom's declared/inferred type; `getMetatype` reports its
|
|
86
|
+
* meta-type (`Symbol`/`Variable`/`Expression`/`Grounded`). */
|
|
87
|
+
declare const getType: (x: Term) => ExpressionAtom;
|
|
88
|
+
declare const getMetatype: (x: Term) => ExpressionAtom;
|
|
89
|
+
/** Assertions for eDSL tests. Each returns the unit atom `()` on success and an
|
|
90
|
+
* `(Error ...)` atom on failure, matching Hyperon's stdlib. `assertEqual` compares evaluated results;
|
|
91
|
+
* `assertAlphaEqual` compares up to a consistent renaming of variables. */
|
|
92
|
+
declare const assertEqual: (a: Term, b: Term) => ExpressionAtom;
|
|
93
|
+
declare const assertAlphaEqual: (a: Term, b: Term) => ExpressionAtom;
|
|
94
|
+
/** Set operations over the (collapsed) results of their arguments, deduplicating modulo equality.
|
|
95
|
+
* `unique` removes duplicates from one result set; `union`/`intersection`/`subtraction` combine two. */
|
|
96
|
+
declare const unique: (x: Term) => ExpressionAtom;
|
|
97
|
+
declare const union: (a: Term, b: Term) => ExpressionAtom;
|
|
98
|
+
declare const intersection: (a: Term, b: Term) => ExpressionAtom;
|
|
99
|
+
declare const subtraction: (a: Term, b: Term) => ExpressionAtom;
|
|
100
|
+
/** `(println! x)`: print `x` (a side effect); returns the unit atom `()`. */
|
|
101
|
+
declare const println: (x: Term) => ExpressionAtom;
|
|
102
|
+
/** `(sealed (vars...) body)`: alpha-rename `body`'s variables (except `vars`) to fresh names, so a
|
|
103
|
+
* template can be reused without variable capture. */
|
|
104
|
+
declare const sealed: (vars: ReadonlyArray<Term>, body: Term) => ExpressionAtom;
|
|
105
|
+
|
|
106
|
+
/** Parse a MeTTa template into the atoms it contains, with `${...}` holes auto-grounded. */
|
|
107
|
+
declare function mAll(strings: TemplateStringsArray, ...values: Term[]): Atom[];
|
|
108
|
+
/** Parse a MeTTa template into one top-level atom. Throws otherwise; use {@link mAll} for several. */
|
|
109
|
+
declare function m(strings: TemplateStringsArray, ...values: Term[]): Atom;
|
|
110
|
+
|
|
111
|
+
/** One typed binding row from a {@link MettaDB.query}: each requested variable mapped to its JS value. */
|
|
112
|
+
type Row<V extends Record<string, Var>> = {
|
|
113
|
+
[K in keyof V]: VarValue<V[K]>;
|
|
114
|
+
};
|
|
115
|
+
/** A typed MeTTa runner. Build it with {@link mettaDB}. */
|
|
116
|
+
declare class MettaDB {
|
|
117
|
+
/** The underlying hyperon runner, for anything the eDSL does not wrap. */
|
|
118
|
+
readonly metta: MeTTa;
|
|
119
|
+
/** Add atoms (facts, rules, type declarations) to the program space. JS values are auto-grounded. */
|
|
120
|
+
add(...atoms: Term[]): this;
|
|
121
|
+
/** Add a rewrite rule `(= head body)`. Call repeatedly with the same head for nondeterminism. */
|
|
122
|
+
rule(head: Term, body: Term): this;
|
|
123
|
+
/** Add a type declaration `(: subject type)`. */
|
|
124
|
+
declare(subject: Term, type: Term): this;
|
|
125
|
+
/** Evaluate an atom by rewriting, returning all (nondeterministic) result atoms. */
|
|
126
|
+
eval(atom: Term): Atom[];
|
|
127
|
+
/** Evaluate and return the first result atom, or `undefined` for no result. */
|
|
128
|
+
evalFirst(atom: Term): Atom | undefined;
|
|
129
|
+
/** Like {@link eval}, but each result unwrapped to a plain JS value (grounded -> value, symbol -> name,
|
|
130
|
+
* expression -> array). */
|
|
131
|
+
evalJs(atom: Term): unknown[];
|
|
132
|
+
/** Evaluate `(assertEqual actual expected)` and report whether it passed. A passing assert reduces to
|
|
133
|
+
* the unit atom `()`; a failing one to an `(Error ...)`. Use it for tests written in the eDSL. */
|
|
134
|
+
test(actual: Term, expected: Term): boolean;
|
|
135
|
+
/** Like {@link eval}, awaiting any async grounded operations reached during evaluation. */
|
|
136
|
+
evalAsync(atom: Term): Promise<Atom[]>;
|
|
137
|
+
/** Like {@link evalJs}, awaiting async grounded operations. */
|
|
138
|
+
evalJsAsync(atom: Term): Promise<unknown[]>;
|
|
139
|
+
/** `match &self pattern` over stored atoms, returning one typed row of JS bindings per match. */
|
|
140
|
+
query<V extends Record<string, Var>>(pattern: Term, vars: V): Array<Row<V>>;
|
|
141
|
+
/** Register a synchronous TypeScript function as a grounded operation callable from MeTTa. */
|
|
142
|
+
op(name: string, fn: (args: Atom[]) => Atom[]): this;
|
|
143
|
+
/** Register an async TypeScript function (I/O) as a grounded operation; await it via {@link evalAsync}. */
|
|
144
|
+
asyncOp(name: string, fn: (args: Atom[]) => Promise<Atom[]>): this;
|
|
145
|
+
/** Run raw MeTTa source, one result group per `!`-query. */
|
|
146
|
+
run(src: string): Atom[][];
|
|
147
|
+
}
|
|
148
|
+
/** Create an ergonomic, typed MeTTa runner. */
|
|
149
|
+
declare const mettaDB: () => MettaDB;
|
|
150
|
+
|
|
151
|
+
export { MettaDB, type Row, S, type SymbolBuilder, type Term, type Var, type VarValue, add, and, arrow, assertAlphaEqual, assertEqual, carAtom, caseOf, cdrAtom, collapse, consAtom, decl, deconsAtom, div, e, empty, eq, ge, getMetatype, getType, ground, gt, iff, intersection, le, letStar, lett, list, lt, m, mAll, matchSelf, mettaDB, mod, mul, nil, not, or, println, quote, rel, rule, sealed, sub, subtraction, superpose, unify, union, unique, v };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// src/term.ts
|
|
2
|
+
import {
|
|
3
|
+
Atom,
|
|
4
|
+
S as hS,
|
|
5
|
+
V,
|
|
6
|
+
E,
|
|
7
|
+
ValueAtom
|
|
8
|
+
} from "@metta-ts/hyperon";
|
|
9
|
+
var v = (name) => V(name);
|
|
10
|
+
var S = new Proxy(((name) => hS(name)), {
|
|
11
|
+
get(_target, prop) {
|
|
12
|
+
return typeof prop === "string" ? hS(prop) : void 0;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
function ground(x) {
|
|
16
|
+
if (x instanceof Atom) return x;
|
|
17
|
+
return ValueAtom(x);
|
|
18
|
+
}
|
|
19
|
+
var e = (...items) => E(...items.map(ground));
|
|
20
|
+
function rel(name) {
|
|
21
|
+
const head = hS(name);
|
|
22
|
+
return (...args) => E(head, ...args.map(ground));
|
|
23
|
+
}
|
|
24
|
+
var nil = () => E();
|
|
25
|
+
function list(items, opts) {
|
|
26
|
+
const cons = hS(opts?.cons ?? "::");
|
|
27
|
+
let acc = opts?.nil ?? E();
|
|
28
|
+
for (let i = items.length - 1; i >= 0; i--) acc = E(cons, ground(items[i]), acc);
|
|
29
|
+
return acc;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/forms.ts
|
|
33
|
+
import { E as E2, S as S2 } from "@metta-ts/hyperon";
|
|
34
|
+
var rule = (head, body) => E2(S2("="), ground(head), ground(body));
|
|
35
|
+
var decl = (subject, type) => E2(S2(":"), ground(subject), ground(type));
|
|
36
|
+
var arrow = (...types) => E2(S2("->"), ...types.map(ground));
|
|
37
|
+
var iff = (cond, then, els) => E2(S2("if"), ground(cond), ground(then), ground(els));
|
|
38
|
+
var caseOf = (scrutinee, cases) => E2(S2("case"), ground(scrutinee), E2(...cases.map(([pat, body]) => E2(ground(pat), ground(body)))));
|
|
39
|
+
var lett = (pattern, value, body) => E2(S2("let"), ground(pattern), ground(value), ground(body));
|
|
40
|
+
var letStar = (bindings, body) => E2(S2("let*"), E2(...bindings.map(([pat, val]) => E2(ground(pat), ground(val)))), ground(body));
|
|
41
|
+
var matchSelf = (pattern, template, space = S2("&self")) => E2(S2("match"), ground(space), ground(pattern), ground(template));
|
|
42
|
+
var superpose = (...items) => E2(S2("superpose"), E2(...items.map(ground)));
|
|
43
|
+
var collapse = (x) => E2(S2("collapse"), ground(x));
|
|
44
|
+
var empty = () => E2(S2("empty"));
|
|
45
|
+
var unify = (a, b, then, els) => E2(S2("unify"), ground(a), ground(b), ground(then), ground(els));
|
|
46
|
+
var op2 = (name) => (a, b) => E2(S2(name), ground(a), ground(b));
|
|
47
|
+
var add = op2("+");
|
|
48
|
+
var sub = op2("-");
|
|
49
|
+
var mul = op2("*");
|
|
50
|
+
var div = op2("/");
|
|
51
|
+
var mod = op2("%");
|
|
52
|
+
var eq = op2("==");
|
|
53
|
+
var gt = op2(">");
|
|
54
|
+
var lt = op2("<");
|
|
55
|
+
var ge = op2(">=");
|
|
56
|
+
var le = op2("<=");
|
|
57
|
+
var op1 = (name) => (x) => E2(S2(name), ground(x));
|
|
58
|
+
var and = op2("and");
|
|
59
|
+
var or = op2("or");
|
|
60
|
+
var not = op1("not");
|
|
61
|
+
var carAtom = op1("car-atom");
|
|
62
|
+
var cdrAtom = op1("cdr-atom");
|
|
63
|
+
var consAtom = (head, tail) => E2(S2("cons-atom"), ground(head), ground(tail));
|
|
64
|
+
var deconsAtom = op1("decons-atom");
|
|
65
|
+
var quote = op1("quote");
|
|
66
|
+
var getType = op1("get-type");
|
|
67
|
+
var getMetatype = op1("get-metatype");
|
|
68
|
+
var assertEqual = op2("assertEqual");
|
|
69
|
+
var assertAlphaEqual = op2("assertAlphaEqual");
|
|
70
|
+
var unique = op1("unique");
|
|
71
|
+
var union = op2("union");
|
|
72
|
+
var intersection = op2("intersection");
|
|
73
|
+
var subtraction = op2("subtraction");
|
|
74
|
+
var println = op1("println!");
|
|
75
|
+
var sealed = (vars, body) => E2(S2("sealed"), E2(...vars.map(ground)), ground(body));
|
|
76
|
+
|
|
77
|
+
// src/template.ts
|
|
78
|
+
import {
|
|
79
|
+
SExprParser,
|
|
80
|
+
SymbolAtom,
|
|
81
|
+
ExpressionAtom,
|
|
82
|
+
E as E3,
|
|
83
|
+
standardTokenizer
|
|
84
|
+
} from "@metta-ts/hyperon";
|
|
85
|
+
var SLOT = (i) => `__metta_ts_slot_${i}__`;
|
|
86
|
+
var SLOT_RE = /^__metta_ts_slot_(\d+)__$/;
|
|
87
|
+
var sharedTokenizer;
|
|
88
|
+
var tokenizer = () => sharedTokenizer ??= standardTokenizer();
|
|
89
|
+
function substituteSlots(atom, slots) {
|
|
90
|
+
if (atom instanceof SymbolAtom) {
|
|
91
|
+
const match = SLOT_RE.exec(atom.name());
|
|
92
|
+
return match ? slots[Number(match[1])] : atom;
|
|
93
|
+
}
|
|
94
|
+
if (atom instanceof ExpressionAtom)
|
|
95
|
+
return E3(...atom.children().map((c) => substituteSlots(c, slots)));
|
|
96
|
+
return atom;
|
|
97
|
+
}
|
|
98
|
+
function mAll(strings, ...values) {
|
|
99
|
+
let src = strings[0];
|
|
100
|
+
for (let i = 0; i < values.length; i++) src += SLOT(i) + strings[i + 1];
|
|
101
|
+
const slots = values.map(ground);
|
|
102
|
+
return new SExprParser(src).parseAll(tokenizer()).map((a) => substituteSlots(a, slots));
|
|
103
|
+
}
|
|
104
|
+
function m(strings, ...values) {
|
|
105
|
+
const atoms = mAll(strings, ...values);
|
|
106
|
+
if (atoms.length !== 1)
|
|
107
|
+
throw new Error(
|
|
108
|
+
`m\`...\`: expected exactly one atom, got ${atoms.length}. Use mAll\`...\` for several.`
|
|
109
|
+
);
|
|
110
|
+
return atoms[0];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/db.ts
|
|
114
|
+
import { MeTTa, atomToJs } from "@metta-ts/hyperon";
|
|
115
|
+
var MettaDB = class {
|
|
116
|
+
/** The underlying hyperon runner, for anything the eDSL does not wrap. */
|
|
117
|
+
metta = new MeTTa();
|
|
118
|
+
/** Add atoms (facts, rules, type declarations) to the program space. JS values are auto-grounded. */
|
|
119
|
+
add(...atoms) {
|
|
120
|
+
const space = this.metta.space();
|
|
121
|
+
for (const a of atoms) space.addAtom(ground(a));
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
/** Add a rewrite rule `(= head body)`. Call repeatedly with the same head for nondeterminism. */
|
|
125
|
+
rule(head, body) {
|
|
126
|
+
return this.add(rule(head, body));
|
|
127
|
+
}
|
|
128
|
+
/** Add a type declaration `(: subject type)`. */
|
|
129
|
+
declare(subject, type) {
|
|
130
|
+
return this.add(decl(subject, type));
|
|
131
|
+
}
|
|
132
|
+
/** Evaluate an atom by rewriting, returning all (nondeterministic) result atoms. */
|
|
133
|
+
eval(atom) {
|
|
134
|
+
return this.metta.evaluateAtom(ground(atom));
|
|
135
|
+
}
|
|
136
|
+
/** Evaluate and return the first result atom, or `undefined` for no result. */
|
|
137
|
+
evalFirst(atom) {
|
|
138
|
+
return this.eval(atom)[0];
|
|
139
|
+
}
|
|
140
|
+
/** Like {@link eval}, but each result unwrapped to a plain JS value (grounded -> value, symbol -> name,
|
|
141
|
+
* expression -> array). */
|
|
142
|
+
evalJs(atom) {
|
|
143
|
+
return this.eval(atom).map(atomToJs);
|
|
144
|
+
}
|
|
145
|
+
/** Evaluate `(assertEqual actual expected)` and report whether it passed. A passing assert reduces to
|
|
146
|
+
* the unit atom `()`; a failing one to an `(Error ...)`. Use it for tests written in the eDSL. */
|
|
147
|
+
test(actual, expected) {
|
|
148
|
+
const results = this.eval(assertEqual(actual, expected));
|
|
149
|
+
if (results.length !== 1) return false;
|
|
150
|
+
const js = atomToJs(results[0]);
|
|
151
|
+
return Array.isArray(js) && js.length === 0;
|
|
152
|
+
}
|
|
153
|
+
/** Like {@link eval}, awaiting any async grounded operations reached during evaluation. */
|
|
154
|
+
async evalAsync(atom) {
|
|
155
|
+
return this.metta.evaluateAtomAsync(ground(atom));
|
|
156
|
+
}
|
|
157
|
+
/** Like {@link evalJs}, awaiting async grounded operations. */
|
|
158
|
+
async evalJsAsync(atom) {
|
|
159
|
+
return (await this.evalAsync(atom)).map(atomToJs);
|
|
160
|
+
}
|
|
161
|
+
/** `match &self pattern` over stored atoms, returning one typed row of JS bindings per match. */
|
|
162
|
+
query(pattern, vars) {
|
|
163
|
+
const set = this.metta.space().query(ground(pattern));
|
|
164
|
+
return set.frames.map((frame) => {
|
|
165
|
+
const row = {};
|
|
166
|
+
for (const key in vars) {
|
|
167
|
+
const bound = frame.resolve(vars[key]);
|
|
168
|
+
row[key] = bound === void 0 ? void 0 : atomToJs(bound);
|
|
169
|
+
}
|
|
170
|
+
return row;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/** Register a synchronous TypeScript function as a grounded operation callable from MeTTa. */
|
|
174
|
+
op(name, fn) {
|
|
175
|
+
this.metta.registerOperation(name, fn);
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
/** Register an async TypeScript function (I/O) as a grounded operation; await it via {@link evalAsync}. */
|
|
179
|
+
asyncOp(name, fn) {
|
|
180
|
+
this.metta.registerAsyncOperation(name, fn);
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
/** Run raw MeTTa source, one result group per `!`-query. */
|
|
184
|
+
run(src) {
|
|
185
|
+
return this.metta.run(src);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
var mettaDB = () => new MettaDB();
|
|
189
|
+
|
|
190
|
+
// src/index.ts
|
|
191
|
+
import { Atom as Atom3, ValueAtom as ValueAtom2, atomToJs as atomToJs2 } from "@metta-ts/hyperon";
|
|
192
|
+
export {
|
|
193
|
+
Atom3 as Atom,
|
|
194
|
+
MettaDB,
|
|
195
|
+
S,
|
|
196
|
+
ValueAtom2 as ValueAtom,
|
|
197
|
+
add,
|
|
198
|
+
and,
|
|
199
|
+
arrow,
|
|
200
|
+
assertAlphaEqual,
|
|
201
|
+
assertEqual,
|
|
202
|
+
atomToJs2 as atomToJs,
|
|
203
|
+
carAtom,
|
|
204
|
+
caseOf,
|
|
205
|
+
cdrAtom,
|
|
206
|
+
collapse,
|
|
207
|
+
consAtom,
|
|
208
|
+
decl,
|
|
209
|
+
deconsAtom,
|
|
210
|
+
div,
|
|
211
|
+
e,
|
|
212
|
+
empty,
|
|
213
|
+
eq,
|
|
214
|
+
ge,
|
|
215
|
+
getMetatype,
|
|
216
|
+
getType,
|
|
217
|
+
ground,
|
|
218
|
+
gt,
|
|
219
|
+
iff,
|
|
220
|
+
intersection,
|
|
221
|
+
le,
|
|
222
|
+
letStar,
|
|
223
|
+
lett,
|
|
224
|
+
list,
|
|
225
|
+
lt,
|
|
226
|
+
m,
|
|
227
|
+
mAll,
|
|
228
|
+
matchSelf,
|
|
229
|
+
mettaDB,
|
|
230
|
+
mod,
|
|
231
|
+
mul,
|
|
232
|
+
nil,
|
|
233
|
+
not,
|
|
234
|
+
or,
|
|
235
|
+
println,
|
|
236
|
+
quote,
|
|
237
|
+
rel,
|
|
238
|
+
rule,
|
|
239
|
+
sealed,
|
|
240
|
+
sub,
|
|
241
|
+
subtraction,
|
|
242
|
+
superpose,
|
|
243
|
+
unify,
|
|
244
|
+
union,
|
|
245
|
+
unique,
|
|
246
|
+
v
|
|
247
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metta-ts/edsl",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"author": "MesTTo",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Ergonomic, typed TypeScript eDSL for MeTTa: typed term builders, rewrite rules, a tagged-template surface, and auto-grounding of TypeScript values.",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"metta",
|
|
9
|
+
"hyperon",
|
|
10
|
+
"edsl",
|
|
11
|
+
"dsl",
|
|
12
|
+
"term-rewriting",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"sideEffects": false,
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@metta-ts/hyperon": "1.0.0"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/MesTTo/Meta-TypeScript-Talk.git",
|
|
38
|
+
"directory": "packages/edsl"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/MesTTo/Meta-TypeScript-Talk#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/MesTTo/Meta-TypeScript-Talk/issues"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
49
|
+
}
|
|
50
|
+
}
|