@mettascript/prolog 2.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 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,91 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 MesTTo
3
+ SPDX-License-Identifier: MIT
4
+ -->
5
+
6
+ # @mettascript/prolog
7
+
8
+ `@mettascript/prolog` lets a MeTTa program call a host Prolog runtime through the
9
+ PeTTa-compatible `Predicate`, `callPredicate`, `assertzPredicate`,
10
+ `retractPredicate`, `prolog-call`, and `import_prolog_function` surface.
11
+
12
+ The root package is runtime-agnostic. Pick the adapter subpath for the host you
13
+ want to use.
14
+
15
+ ## Node SWI-Prolog
16
+
17
+ `@mettascript/prolog/swi-node` talks to the `swipl` executable over a small JSON
18
+ server. The CLI uses this when you pass `--prolog`.
19
+
20
+ ```ts
21
+ import { MeTTa } from "@mettascript/hyperon";
22
+ import { registerPrologInterop } from "@mettascript/prolog";
23
+ import { swiPrologBridge } from "@mettascript/prolog/swi-node";
24
+
25
+ const bridge = swiPrologBridge();
26
+ const metta = new MeTTa();
27
+ registerPrologInterop(metta, bridge);
28
+
29
+ const out = await metta.runAsync(`
30
+ !(assertzPredicate (Predicate (edge alice bob)))
31
+ !(prolog-call (edge alice $x))
32
+ `);
33
+
34
+ await bridge.dispose();
35
+ ```
36
+
37
+ ## Browser SWI-Prolog WASM
38
+
39
+ `@mettascript/prolog/swi-wasm` runs the same MeTTa surface over `swipl-wasm`.
40
+ Files are loaded through the host text loader, written into SWI's virtual
41
+ filesystem, and consulted with SWI's normal `consult/1`.
42
+
43
+ ```ts
44
+ import { createBrowserRunner, createBrowserTextLoader } from "@mettascript/browser/host";
45
+ import { createSwiWasmInterop } from "@mettascript/prolog/swi-wasm";
46
+
47
+ const files = new Map([["facts.pl", "edge(alice, bob).\nedge(alice, mars).\n"]]);
48
+ const loadText = createBrowserTextLoader({ files, baseUrl: import.meta.url });
49
+ const prolog = await createSwiWasmInterop({ loadText });
50
+ const runner = createBrowserRunner({ files, interops: [prolog] });
51
+
52
+ const results = await runner.run(`
53
+ !(import! &self "facts.pl")
54
+ !(prolog-call (edge alice $x))
55
+ `);
56
+
57
+ await runner.dispose();
58
+ ```
59
+
60
+ For browser bundlers, install `swipl-wasm` next to `@mettascript/prolog` and serve
61
+ its WASM assets according to the `swipl-wasm` package documentation, or use its
62
+ single-file bundle setup. For interactive pages, run the browser runner and
63
+ SWI-WASM inside a Web Worker.
64
+
65
+ ## PeTTa Compatibility
66
+
67
+ The interop surface follows PeTTa's Prolog bridge shape where it is independent
68
+ of PeTTa's evaluator:
69
+
70
+ ```metta
71
+ !(assertzPredicate (Predicate (edge alice bob)))
72
+ !(callPredicate (Predicate (edge alice $x))) ; True
73
+ !(import_prolog_function edge)
74
+ !(edge alice) ; bob
75
+ ```
76
+
77
+ MeTTaScript keeps Hyperon semantics. It does not switch into a PeTTa execution
78
+ mode, and it does not compile all MeTTa rules to Prolog. A later
79
+ PeTTa-compatibility layer can expose PeTTa's `process_metta_string` path
80
+ explicitly, but normal `.pl` imports and predicate calls already work through
81
+ the shared adapter contract.
82
+
83
+ ## Testing
84
+
85
+ The default tests use mock bridges and do not need SWI installed.
86
+
87
+ - `PROLOG_LIVE=1 pnpm vitest run packages/prolog/src/swi.test.ts` checks the
88
+ Node adapter against a local `swipl` executable.
89
+ - `SWI_WASM_LIVE=1 pnpm vitest run packages/prolog/src/swi-wasm.test.ts` checks
90
+ `.pl` import, `prolog-call`, and `import_prolog_function` against real
91
+ `swipl-wasm`.
@@ -0,0 +1,223 @@
1
+ // src/prolog.ts
2
+ import * as core from "@mettascript/core";
3
+ import {
4
+ Atom,
5
+ E,
6
+ ExpressionAtom,
7
+ GroundedAtom,
8
+ S,
9
+ SymbolAtom,
10
+ ValueAtom,
11
+ VariableAtom,
12
+ asyncOperationReturnToReduceResult
13
+ } from "@mettascript/hyperon";
14
+ var TRUE = S("True");
15
+ var FALSE = S("False");
16
+ var SELF = S("&self");
17
+ var SAFE_MIN = BigInt(Number.MIN_SAFE_INTEGER);
18
+ var SAFE_MAX = BigInt(Number.MAX_SAFE_INTEGER);
19
+ var PROLOG_METTA_SRC = `
20
+ (: prolog-call (-> Atom Atom))
21
+ (= (prolog-match $goal $template)
22
+ (let $answer (prolog-call $goal)
23
+ (let $goal $answer $template)))
24
+ (: Predicate (-> Atom %Undefined%))
25
+ (= (Predicate $goal) $goal)
26
+ (: callPredicate (-> Atom Bool))
27
+ (= (callPredicate $goal) (prolog-match $goal True))
28
+ (: assertaPredicate (-> Atom Bool))
29
+ (= (assertaPredicate $goal) (prolog-asserta $goal))
30
+ (: assertzPredicate (-> Atom Bool))
31
+ (= (assertzPredicate $goal) (prolog-assertz $goal))
32
+ (: retractPredicate (-> Atom Bool))
33
+ (= (retractPredicate $goal) (prolog-retract $goal))
34
+ (: prolog-function (-> Atom Expression Atom))
35
+ (: import_prolog_function (-> Atom Bool))
36
+ (: prolog-consult (-> Atom Bool))
37
+ (: import_prolog_functions_from_file (-> Atom Expression Bool))
38
+ `;
39
+ function atomName(atom, ctx) {
40
+ if (atom instanceof SymbolAtom) return atom.name();
41
+ if (atom instanceof GroundedAtom) {
42
+ const content = atom.object().content;
43
+ if (typeof content === "string") return content;
44
+ }
45
+ throw new Error(`${ctx}: expected a Symbol or String`);
46
+ }
47
+ function pathName(atom, ctx) {
48
+ if (atom instanceof ExpressionAtom) {
49
+ const children = atom.children();
50
+ if (children.length === 2 && children[0] instanceof SymbolAtom && children[0].name() === "library")
51
+ return atomName(children[1], ctx);
52
+ }
53
+ return atomName(atom, ctx);
54
+ }
55
+ function expressionItems(atom, ctx) {
56
+ if (atom instanceof ExpressionAtom) return atom.children();
57
+ throw new Error(`${ctx}: expected an expression`);
58
+ }
59
+ function bigintAtom(value) {
60
+ const n = BigInt(value);
61
+ if (n >= SAFE_MIN && n <= SAFE_MAX) return ValueAtom(Number(n));
62
+ return Atom.fromCAtom(core.gint(n));
63
+ }
64
+ function atomToPrologTerm(atom) {
65
+ if (atom instanceof VariableAtom) return { type: "var", name: atom.name() };
66
+ if (atom instanceof SymbolAtom) return { type: "atom", name: atom.name() };
67
+ if (atom instanceof GroundedAtom) {
68
+ const content = atom.object().content;
69
+ if (typeof content === "bigint") return { type: "int", value: String(content) };
70
+ if (typeof content === "number") {
71
+ return Number.isInteger(content) ? { type: "int", value: String(content) } : { type: "float", value: content };
72
+ }
73
+ if (typeof content === "string") return { type: "string", value: content };
74
+ if (typeof content === "boolean") return { type: "atom", name: content ? "true" : "false" };
75
+ if (content === void 0 || content === null) return { type: "atom", name: "[]" };
76
+ return { type: "atom", name: atom.toString() };
77
+ }
78
+ if (atom instanceof ExpressionAtom) {
79
+ const children = atom.children();
80
+ if (children.length === 0) return { type: "atom", name: "[]" };
81
+ const [head, ...args] = children;
82
+ if (head instanceof SymbolAtom && head.name() === "Predicate" && args.length === 1) {
83
+ return atomToPrologTerm(args[0]);
84
+ }
85
+ if (!(head instanceof SymbolAtom))
86
+ throw new Error(`prolog: compound head must be a Symbol, got ${head?.toString() ?? "()"}`);
87
+ return {
88
+ type: "compound",
89
+ functor: head.name(),
90
+ args: args.map((arg) => atomToPrologTerm(arg))
91
+ };
92
+ }
93
+ return { type: "atom", name: atom.toString() };
94
+ }
95
+ function prologTermToAtom(term) {
96
+ switch (term.type) {
97
+ case "atom":
98
+ return term.name === "[]" ? E() : S(term.name);
99
+ case "int":
100
+ return bigintAtom(term.value);
101
+ case "float":
102
+ return ValueAtom(term.value);
103
+ case "string":
104
+ return ValueAtom(term.value);
105
+ case "var":
106
+ return VariableAtom.parseName(term.name);
107
+ case "compound":
108
+ return E(S(term.functor), ...term.args.map(prologTermToAtom));
109
+ }
110
+ }
111
+ function functionRuleEffects(name, arities) {
112
+ const usable = [...new Set(arities)].filter((arity) => arity >= 1).sort((a, b) => a - b);
113
+ if (usable.length === 0)
114
+ throw new Error(`import_prolog_function: no predicate arity found for ${name}`);
115
+ const effects = [];
116
+ for (const arity of usable) {
117
+ const vars = Array.from(
118
+ { length: arity - 1 },
119
+ (_, i) => VariableAtom.parseName(`prolog_arg_${i}`)
120
+ );
121
+ effects.push({
122
+ kind: "addAtom",
123
+ space: SELF,
124
+ atom: E(S("="), E(S(name), ...vars), E(S("prolog-function"), S(name), E(...vars)))
125
+ });
126
+ }
127
+ return effects;
128
+ }
129
+ async function importFunctionEffects(bridge, name) {
130
+ return functionRuleEffects(name, await bridge.predicateArities(name));
131
+ }
132
+ function namesFromAtom(atom, ctx) {
133
+ if (atom instanceof ExpressionAtom) return atom.children().map((child) => atomName(child, ctx));
134
+ return [atomName(atom, ctx)];
135
+ }
136
+ async function callFunction(bridge, name, argsAtom) {
137
+ const args = expressionItems(argsAtom, "prolog-function");
138
+ const output = VariableAtom.parseName("prolog_result");
139
+ const goal = E(S(name), ...args, output);
140
+ const answers = await bridge.query(goal);
141
+ return answers.map((answer) => {
142
+ const children = expressionItems(answer, "prolog-function result");
143
+ const value = children[children.length - 1];
144
+ if (value === void 0) throw new Error(`prolog-function: ${name} returned an empty answer`);
145
+ return value;
146
+ });
147
+ }
148
+ function prologOps(bridge, opts = {}) {
149
+ const resolvePath = opts.resolvePath ?? ((path) => path);
150
+ return /* @__PURE__ */ new Map([
151
+ ["prolog-call", async (args) => await bridge.query(args[0] ?? E())],
152
+ [
153
+ "prolog-function",
154
+ async (args) => await callFunction(bridge, atomName(args[0], "prolog-function"), args[1] ?? E())
155
+ ],
156
+ [
157
+ "prolog-asserta",
158
+ async (args) => {
159
+ await bridge.asserta(args[0] ?? E());
160
+ return [TRUE];
161
+ }
162
+ ],
163
+ [
164
+ "prolog-assertz",
165
+ async (args) => {
166
+ await bridge.assertz(args[0] ?? E());
167
+ return [TRUE];
168
+ }
169
+ ],
170
+ ["prolog-retract", async (args) => [await bridge.retract(args[0] ?? E()) ? TRUE : FALSE]],
171
+ [
172
+ "prolog-consult",
173
+ async (args) => {
174
+ await bridge.consult(resolvePath(pathName(args[0], "prolog-consult")));
175
+ return [TRUE];
176
+ }
177
+ ],
178
+ [
179
+ "import_prolog_function",
180
+ async (args) => ({
181
+ results: [TRUE],
182
+ effects: await importFunctionEffects(bridge, atomName(args[0], "import_prolog_function"))
183
+ })
184
+ ],
185
+ [
186
+ "import_prolog_functions_from_file",
187
+ async (args) => {
188
+ await bridge.consult(resolvePath(pathName(args[0], "import_prolog_functions_from_file")));
189
+ const effects = [];
190
+ for (const name of namesFromAtom(args[1], "import_prolog_functions_from_file")) {
191
+ effects.push(...await importFunctionEffects(bridge, name));
192
+ }
193
+ return { results: [TRUE], effects };
194
+ }
195
+ ]
196
+ ]);
197
+ }
198
+ function registerPrologInterop(m, bridge, opts = {}) {
199
+ for (const [name, fn] of prologOps(bridge, opts)) m.registerAsyncOperation(name, fn);
200
+ m.run(PROLOG_METTA_SRC);
201
+ }
202
+ function prologCoreAsyncOps(bridge, opts = {}) {
203
+ const out = /* @__PURE__ */ new Map();
204
+ for (const [name, fn] of prologOps(bridge, opts))
205
+ out.set(name, async (args) => {
206
+ try {
207
+ const raw = await fn(args.map((a) => Atom.fromCAtom(a)));
208
+ return asyncOperationReturnToReduceResult(raw);
209
+ } catch (e) {
210
+ return { tag: "runtimeError", msg: e instanceof Error ? e.message : String(e) };
211
+ }
212
+ });
213
+ return out;
214
+ }
215
+
216
+ export {
217
+ PROLOG_METTA_SRC,
218
+ atomToPrologTerm,
219
+ prologTermToAtom,
220
+ prologOps,
221
+ registerPrologInterop,
222
+ prologCoreAsyncOps
223
+ };
@@ -0,0 +1,19 @@
1
+ import { P as PrologBridge } from './prolog-cLhN4SQh.js';
2
+ export { a as PROLOG_METTA_SRC, b as PrologEffect, c as PrologInteropOptions, d as PrologOperationResult, e as PrologOperationReturn, f as PrologTermJson, g as atomToPrologTerm, p as prologCoreAsyncOps, h as prologOps, i as prologTermToAtom, r as registerPrologInterop } from './prolog-cLhN4SQh.js';
3
+ import { Atom } from '@mettascript/hyperon';
4
+ import '@mettascript/core';
5
+
6
+ declare class MockPrologBridge implements PrologBridge {
7
+ readonly consulted: string[];
8
+ private readonly facts;
9
+ constructor(facts?: readonly Atom[]);
10
+ query(goal: Atom): Promise<Atom[]>;
11
+ asserta(term: Atom): Promise<void>;
12
+ assertz(term: Atom): Promise<void>;
13
+ retract(term: Atom): Promise<boolean>;
14
+ consult(path: string): Promise<void>;
15
+ predicateArities(name: string): Promise<number[]>;
16
+ dispose(): void;
17
+ }
18
+
19
+ export { MockPrologBridge, PrologBridge };
package/dist/index.js ADDED
@@ -0,0 +1,74 @@
1
+ import {
2
+ PROLOG_METTA_SRC,
3
+ atomToPrologTerm,
4
+ prologCoreAsyncOps,
5
+ prologOps,
6
+ prologTermToAtom,
7
+ registerPrologInterop
8
+ } from "./chunk-DG3MQ2BR.js";
9
+
10
+ // src/mock.ts
11
+ import * as core from "@mettascript/core";
12
+ import { Atom, ExpressionAtom, SymbolAtom } from "@mettascript/hyperon";
13
+ function predicateNameAndArity(atom) {
14
+ if (!(atom instanceof ExpressionAtom)) return void 0;
15
+ const children = atom.children();
16
+ const head = children[0];
17
+ if (!(head instanceof SymbolAtom)) return void 0;
18
+ return { name: head.name(), arity: children.length - 1 };
19
+ }
20
+ var MockPrologBridge = class {
21
+ consulted = [];
22
+ facts = [];
23
+ constructor(facts = []) {
24
+ this.facts.push(...facts);
25
+ }
26
+ query(goal) {
27
+ const out = [];
28
+ for (const fact of this.facts) {
29
+ for (const bindings of core.matchAtoms(goal.catom, fact.catom)) {
30
+ out.push(Atom.fromCAtom(core.instantiate(bindings, goal.catom)));
31
+ }
32
+ }
33
+ return Promise.resolve(out);
34
+ }
35
+ asserta(term) {
36
+ this.facts.unshift(term);
37
+ return Promise.resolve();
38
+ }
39
+ assertz(term) {
40
+ this.facts.push(term);
41
+ return Promise.resolve();
42
+ }
43
+ retract(term) {
44
+ const index = this.facts.findIndex(
45
+ (fact) => core.matchAtoms(term.catom, fact.catom).length > 0
46
+ );
47
+ if (index === -1) return Promise.resolve(false);
48
+ this.facts.splice(index, 1);
49
+ return Promise.resolve(true);
50
+ }
51
+ consult(path) {
52
+ this.consulted.push(path);
53
+ return Promise.resolve();
54
+ }
55
+ predicateArities(name) {
56
+ const arities = /* @__PURE__ */ new Set();
57
+ for (const fact of this.facts) {
58
+ const pred = predicateNameAndArity(fact);
59
+ if (pred?.name === name) arities.add(pred.arity);
60
+ }
61
+ return Promise.resolve([...arities].sort((a, b) => a - b));
62
+ }
63
+ dispose() {
64
+ }
65
+ };
66
+ export {
67
+ MockPrologBridge,
68
+ PROLOG_METTA_SRC,
69
+ atomToPrologTerm,
70
+ prologCoreAsyncOps,
71
+ prologOps,
72
+ prologTermToAtom,
73
+ registerPrologInterop
74
+ };
@@ -0,0 +1,46 @@
1
+ import { AsyncGroundFn } from '@mettascript/core';
2
+ import { Atom, AsyncOperationEffect, AsyncOperationResult, AsyncOperationReturn, MeTTa } from '@mettascript/hyperon';
3
+
4
+ type PrologTermJson = {
5
+ readonly type: "atom";
6
+ readonly name: string;
7
+ } | {
8
+ readonly type: "int";
9
+ readonly value: string;
10
+ } | {
11
+ readonly type: "float";
12
+ readonly value: number;
13
+ } | {
14
+ readonly type: "string";
15
+ readonly value: string;
16
+ } | {
17
+ readonly type: "var";
18
+ readonly name: string;
19
+ } | {
20
+ readonly type: "compound";
21
+ readonly functor: string;
22
+ readonly args: readonly PrologTermJson[];
23
+ };
24
+ interface PrologBridge {
25
+ query(goal: Atom): Promise<Atom[]>;
26
+ asserta(term: Atom): Promise<void>;
27
+ assertz(term: Atom): Promise<void>;
28
+ retract(term: Atom): Promise<boolean>;
29
+ consult(path: string): Promise<void>;
30
+ predicateArities(name: string): Promise<number[]>;
31
+ dispose(): Promise<void> | void;
32
+ }
33
+ interface PrologInteropOptions {
34
+ readonly resolvePath?: (path: string) => string;
35
+ }
36
+ type PrologEffect = AsyncOperationEffect;
37
+ type PrologOperationResult = AsyncOperationResult;
38
+ type PrologOperationReturn = AsyncOperationReturn;
39
+ declare const PROLOG_METTA_SRC = "\n(: prolog-call (-> Atom Atom))\n(= (prolog-match $goal $template)\n (let $answer (prolog-call $goal)\n (let $goal $answer $template)))\n(: Predicate (-> Atom %Undefined%))\n(= (Predicate $goal) $goal)\n(: callPredicate (-> Atom Bool))\n(= (callPredicate $goal) (prolog-match $goal True))\n(: assertaPredicate (-> Atom Bool))\n(= (assertaPredicate $goal) (prolog-asserta $goal))\n(: assertzPredicate (-> Atom Bool))\n(= (assertzPredicate $goal) (prolog-assertz $goal))\n(: retractPredicate (-> Atom Bool))\n(= (retractPredicate $goal) (prolog-retract $goal))\n(: prolog-function (-> Atom Expression Atom))\n(: import_prolog_function (-> Atom Bool))\n(: prolog-consult (-> Atom Bool))\n(: import_prolog_functions_from_file (-> Atom Expression Bool))\n";
40
+ declare function atomToPrologTerm(atom: Atom): PrologTermJson;
41
+ declare function prologTermToAtom(term: PrologTermJson): Atom;
42
+ declare function prologOps(bridge: PrologBridge, opts?: PrologInteropOptions): Map<string, (args: Atom[]) => Promise<PrologOperationReturn>>;
43
+ declare function registerPrologInterop(m: MeTTa, bridge: PrologBridge, opts?: PrologInteropOptions): void;
44
+ declare function prologCoreAsyncOps(bridge: PrologBridge, opts?: PrologInteropOptions): Map<string, AsyncGroundFn>;
45
+
46
+ export { type PrologBridge as P, PROLOG_METTA_SRC as a, type PrologEffect as b, type PrologInteropOptions as c, type PrologOperationResult as d, type PrologOperationReturn as e, type PrologTermJson as f, atomToPrologTerm as g, prologOps as h, prologTermToAtom as i, prologCoreAsyncOps as p, registerPrologInterop as r };
@@ -0,0 +1,31 @@
1
+ import { Atom } from '@mettascript/hyperon';
2
+ import { P as PrologBridge } from './prolog-cLhN4SQh.js';
3
+ import '@mettascript/core';
4
+
5
+ interface SwiPrologBridgeOptions {
6
+ readonly executable?: string;
7
+ }
8
+ declare class SwiPrologBridge implements PrologBridge {
9
+ private readonly dir;
10
+ private readonly proc;
11
+ private readonly pending;
12
+ private nextId;
13
+ private stdout;
14
+ private stderr;
15
+ private disposed;
16
+ constructor(opts?: SwiPrologBridgeOptions);
17
+ query(goal: Atom): Promise<Atom[]>;
18
+ asserta(term: Atom): Promise<void>;
19
+ assertz(term: Atom): Promise<void>;
20
+ retract(term: Atom): Promise<boolean>;
21
+ consult(path: string): Promise<void>;
22
+ predicateArities(name: string): Promise<number[]>;
23
+ dispose(): void;
24
+ private request;
25
+ private handleStdout;
26
+ private rejectAll;
27
+ private stderrText;
28
+ }
29
+ declare function swiPrologBridge(opts?: SwiPrologBridgeOptions): SwiPrologBridge;
30
+
31
+ export { SwiPrologBridge, type SwiPrologBridgeOptions, swiPrologBridge };
@@ -0,0 +1,265 @@
1
+ import {
2
+ atomToPrologTerm,
3
+ prologTermToAtom
4
+ } from "./chunk-DG3MQ2BR.js";
5
+
6
+ // src/swi.ts
7
+ import { spawn, spawnSync } from "child_process";
8
+ import { mkdtempSync, rmSync, writeFileSync } from "fs";
9
+ import { tmpdir } from "os";
10
+ import { join } from "path";
11
+ var SERVER_SRC = String.raw`
12
+ :- use_module(library(http/json)).
13
+ :- initialization(main, main).
14
+
15
+ main :-
16
+ repeat,
17
+ read_line_to_string(user_input, Line),
18
+ ( Line == end_of_file
19
+ -> !
20
+ ; handle_line(Line),
21
+ fail
22
+ ).
23
+
24
+ handle_line(Line) :-
25
+ catch(
26
+ ( open_string(Line, Stream),
27
+ json_read_dict(Stream, Req),
28
+ close(Stream),
29
+ dispatch(Req, Resp0),
30
+ Resp = Resp0.put(id, Req.id)
31
+ ),
32
+ E,
33
+ ( message_to_string(E, Msg),
34
+ ( catch(open_string(Line, S2), _, fail),
35
+ catch(json_read_dict(S2, BadReq), _, fail),
36
+ catch(close(S2), _, true),
37
+ get_dict(id, BadReq, Id)
38
+ -> true
39
+ ; Id = -1
40
+ ),
41
+ Resp = json{id:Id, ok:false, error:Msg}
42
+ )
43
+ ),
44
+ json_write_dict(current_output, Resp, [width(0)]),
45
+ nl,
46
+ flush_output(current_output).
47
+
48
+ dispatch(Req, json{ok:true, answers:Answers}) :-
49
+ Req.cmd == "query",
50
+ !,
51
+ json_to_term(Req.goal, Goal),
52
+ findall(Answer, (call(Goal), term_to_json(Goal, Answer)), Answers).
53
+ dispatch(Req, json{ok:true}) :-
54
+ Req.cmd == "asserta",
55
+ !,
56
+ json_to_term(Req.term, Term),
57
+ asserta(Term).
58
+ dispatch(Req, json{ok:true}) :-
59
+ Req.cmd == "assertz",
60
+ !,
61
+ json_to_term(Req.term, Term),
62
+ assertz(Term).
63
+ dispatch(Req, json{ok:true, value:Value}) :-
64
+ Req.cmd == "retract",
65
+ !,
66
+ json_to_term(Req.term, Term),
67
+ ( once(retract(Term)) -> Value = true ; Value = false ).
68
+ dispatch(Req, json{ok:true}) :-
69
+ Req.cmd == "consult",
70
+ !,
71
+ consult(Req.path).
72
+ dispatch(Req, json{ok:true, arities:Arities}) :-
73
+ Req.cmd == "arities",
74
+ !,
75
+ atom_string(Name, Req.name),
76
+ findall(A, current_predicate(Name/A), Raw),
77
+ sort(Raw, Arities).
78
+ dispatch(Req, _) :-
79
+ throw(error(domain_error(prolog_bridge_command, Req.cmd), _)).
80
+
81
+ json_to_term(Json, Term) :-
82
+ json_to_term(Json, Term, [], _).
83
+
84
+ json_to_term(Json, Term, Vars0, Vars) :-
85
+ Type = Json.type,
86
+ ( Type == "var"
87
+ -> var_for_name(Json.name, Term, Vars0, Vars)
88
+ ; Type == "atom"
89
+ -> atom_string(Term, Json.name),
90
+ Vars = Vars0
91
+ ; Type == "int"
92
+ -> number_string(Term, Json.value),
93
+ Vars = Vars0
94
+ ; Type == "float"
95
+ -> Term = Json.value,
96
+ Vars = Vars0
97
+ ; Type == "string"
98
+ -> Term = Json.value,
99
+ Vars = Vars0
100
+ ; Type == "compound"
101
+ -> atom_string(Functor, Json.functor),
102
+ json_to_terms(Json.args, Args, Vars0, Vars),
103
+ Term =.. [Functor|Args]
104
+ ; throw(error(domain_error(prolog_term_json, Type), _))
105
+ ).
106
+
107
+ json_to_terms([], [], Vars, Vars).
108
+ json_to_terms([Json|Rest], [Term|Terms], Vars0, Vars) :-
109
+ json_to_term(Json, Term, Vars0, Vars1),
110
+ json_to_terms(Rest, Terms, Vars1, Vars).
111
+
112
+ var_for_name(Name, Var, Vars, Vars) :-
113
+ memberchk(Name=Existing, Vars),
114
+ !,
115
+ Var = Existing.
116
+ var_for_name(Name, Var, Vars, [Name=Var|Vars]).
117
+
118
+ term_to_json(Term, Json) :-
119
+ ( var(Term)
120
+ -> Json = json{type:"var", name:"_"}
121
+ ; integer(Term)
122
+ -> number_string(Term, Value),
123
+ Json = json{type:"int", value:Value}
124
+ ; float(Term)
125
+ -> Json = json{type:"float", value:Term}
126
+ ; string(Term)
127
+ -> Json = json{type:"string", value:Term}
128
+ ; atom(Term)
129
+ -> atom_string(Term, Name),
130
+ Json = json{type:"atom", name:Name}
131
+ ; Term =.. [Functor|Args],
132
+ atom_string(Functor, FunctorString),
133
+ terms_to_json(Args, JsonArgs),
134
+ Json = json{type:"compound", functor:FunctorString, args:JsonArgs}
135
+ ).
136
+
137
+ terms_to_json([], []).
138
+ terms_to_json([Term|Rest], [Json|JsonRest]) :-
139
+ term_to_json(Term, Json),
140
+ terms_to_json(Rest, JsonRest).
141
+ `;
142
+ var SwiPrologBridge = class {
143
+ dir;
144
+ proc;
145
+ pending = /* @__PURE__ */ new Map();
146
+ nextId = 1;
147
+ stdout = "";
148
+ stderr = "";
149
+ disposed = false;
150
+ constructor(opts = {}) {
151
+ const executable = opts.executable ?? "swipl";
152
+ const version = spawnSync(executable, ["--version"], { encoding: "utf8" });
153
+ if (version.error !== void 0) {
154
+ throw new Error(`--prolog needs SWI-Prolog on PATH (${version.error.message})`);
155
+ }
156
+ this.dir = mkdtempSync(join(tmpdir(), "metta-ts-prolog-"));
157
+ const server = join(this.dir, "server.pl");
158
+ writeFileSync(server, SERVER_SRC);
159
+ this.proc = spawn(executable, ["-q", "-f", "none", server]);
160
+ this.proc.stdout.setEncoding("utf8");
161
+ this.proc.stderr.setEncoding("utf8");
162
+ this.proc.stdout.on("data", (chunk) => this.handleStdout(chunk));
163
+ this.proc.stderr.on("data", (chunk) => {
164
+ this.stderr += chunk;
165
+ });
166
+ this.proc.on("error", (error) => this.rejectAll(error));
167
+ this.proc.on("exit", (code, signal) => {
168
+ if (!this.disposed) {
169
+ this.rejectAll(
170
+ new Error(
171
+ `SWI-Prolog bridge exited (${signal ?? code ?? "unknown"})${this.stderrText()}`
172
+ )
173
+ );
174
+ }
175
+ });
176
+ }
177
+ async query(goal) {
178
+ const response = await this.request({ cmd: "query", goal: atomToPrologTerm(goal) });
179
+ if (!("answers" in response)) throw new Error("SWI-Prolog bridge query returned no answers");
180
+ return response.answers.map(prologTermToAtom);
181
+ }
182
+ async asserta(term) {
183
+ await this.request({ cmd: "asserta", term: atomToPrologTerm(term) });
184
+ }
185
+ async assertz(term) {
186
+ await this.request({ cmd: "assertz", term: atomToPrologTerm(term) });
187
+ }
188
+ async retract(term) {
189
+ const response = await this.request({ cmd: "retract", term: atomToPrologTerm(term) });
190
+ if (!("value" in response)) throw new Error("SWI-Prolog bridge retract returned no value");
191
+ return response.value;
192
+ }
193
+ async consult(path) {
194
+ await this.request({ cmd: "consult", path });
195
+ }
196
+ async predicateArities(name) {
197
+ const response = await this.request({ cmd: "arities", name });
198
+ if (!("arities" in response)) throw new Error("SWI-Prolog bridge arities returned no value");
199
+ return [...response.arities];
200
+ }
201
+ dispose() {
202
+ this.disposed = true;
203
+ for (const pending of this.pending.values())
204
+ pending.reject(new Error("SWI-Prolog bridge closed"));
205
+ this.pending.clear();
206
+ this.proc.kill();
207
+ rmSync(this.dir, { recursive: true, force: true });
208
+ }
209
+ request(payload) {
210
+ const id = this.nextId++;
211
+ return new Promise((resolve, reject) => {
212
+ this.pending.set(id, {
213
+ resolve: (response) => {
214
+ if (!response.ok) reject(new Error(response.error));
215
+ else resolve(response);
216
+ },
217
+ reject
218
+ });
219
+ this.proc.stdin.write(JSON.stringify({ ...payload, id }) + "\n", (error) => {
220
+ if (error !== null && error !== void 0) {
221
+ this.pending.delete(id);
222
+ reject(error);
223
+ }
224
+ });
225
+ });
226
+ }
227
+ handleStdout(chunk) {
228
+ this.stdout += chunk;
229
+ for (; ; ) {
230
+ const index = this.stdout.indexOf("\n");
231
+ if (index === -1) return;
232
+ const line = this.stdout.slice(0, index).trim();
233
+ this.stdout = this.stdout.slice(index + 1);
234
+ if (line === "") continue;
235
+ let parsed;
236
+ try {
237
+ parsed = JSON.parse(line);
238
+ } catch (e) {
239
+ this.rejectAll(new Error(`SWI-Prolog bridge emitted invalid JSON: ${String(e)}`));
240
+ continue;
241
+ }
242
+ const id = parsed.id;
243
+ if (id === void 0) continue;
244
+ const pending = this.pending.get(id);
245
+ if (pending === void 0) continue;
246
+ this.pending.delete(id);
247
+ pending.resolve(parsed);
248
+ }
249
+ }
250
+ rejectAll(error) {
251
+ for (const pending of this.pending.values()) pending.reject(error);
252
+ this.pending.clear();
253
+ }
254
+ stderrText() {
255
+ const text = this.stderr.trim();
256
+ return text === "" ? "" : `: ${text}`;
257
+ }
258
+ };
259
+ function swiPrologBridge(opts = {}) {
260
+ return new SwiPrologBridge(opts);
261
+ }
262
+ export {
263
+ SwiPrologBridge,
264
+ swiPrologBridge
265
+ };
@@ -0,0 +1,61 @@
1
+ import { HostTextLoader, HostInterop } from '@mettascript/core/host';
2
+ import { Atom } from '@mettascript/hyperon';
3
+ import { P as PrologBridge } from './prolog-cLhN4SQh.js';
4
+ import '@mettascript/core';
5
+
6
+ interface SwiWasmFs {
7
+ writeFile(path: string, data: string, opts?: {
8
+ readonly encoding?: "utf8";
9
+ }): void;
10
+ mkdirTree?(path: string): void;
11
+ analyzePath?(path: string): {
12
+ readonly exists: boolean;
13
+ };
14
+ mkdir?(path: string): void;
15
+ }
16
+ interface SwiWasmQuery {
17
+ next(): unknown;
18
+ once(): unknown;
19
+ }
20
+ interface SwiWasmProlog {
21
+ query(goal: string, input?: Record<string, unknown>): SwiWasmQuery;
22
+ }
23
+ interface SwiWasmRuntime {
24
+ readonly FS: SwiWasmFs;
25
+ readonly prolog: SwiWasmProlog;
26
+ }
27
+ interface SwiWasmLoadOptions {
28
+ readonly arguments?: readonly string[];
29
+ readonly locateFile?: (path: string, prefix?: string) => string;
30
+ }
31
+ type SwiWasmLoader = (options?: SwiWasmLoadOptions) => Promise<SwiWasmRuntime>;
32
+ interface SwiWasmBridgeOptions {
33
+ readonly loadText?: HostTextLoader;
34
+ readonly baseUrl?: string | URL;
35
+ readonly files?: ReadonlyMap<string, string>;
36
+ }
37
+ interface SwiWasmInteropOptions extends SwiWasmBridgeOptions {
38
+ readonly prolog?: SwiWasmRuntime;
39
+ readonly loadSwipl?: SwiWasmLoader;
40
+ readonly locateFile?: (path: string, prefix?: string) => string;
41
+ readonly arguments?: readonly string[];
42
+ readonly preload?: readonly string[];
43
+ }
44
+ declare class SwiWasmBridge implements PrologBridge {
45
+ private readonly runtime;
46
+ private readonly loadText;
47
+ private readonly consulted;
48
+ constructor(runtime: SwiWasmRuntime, options?: SwiWasmBridgeOptions);
49
+ query(goal: Atom): Promise<Atom[]>;
50
+ asserta(term: Atom): Promise<void>;
51
+ assertz(term: Atom): Promise<void>;
52
+ retract(term: Atom): Promise<boolean>;
53
+ consult(path: string): Promise<void>;
54
+ predicateArities(name: string): Promise<number[]>;
55
+ dispose(): void;
56
+ private runOnce;
57
+ }
58
+ declare function swiWasmBridge(runtime: SwiWasmRuntime, options?: SwiWasmBridgeOptions): SwiWasmBridge;
59
+ declare function createSwiWasmInterop(options?: SwiWasmInteropOptions): Promise<HostInterop>;
60
+
61
+ export { SwiWasmBridge, type SwiWasmBridgeOptions, type SwiWasmFs, type SwiWasmInteropOptions, type SwiWasmLoadOptions, type SwiWasmLoader, type SwiWasmProlog, type SwiWasmQuery, type SwiWasmRuntime, createSwiWasmInterop, swiWasmBridge };
@@ -0,0 +1,295 @@
1
+ import {
2
+ PROLOG_METTA_SRC,
3
+ atomToPrologTerm,
4
+ prologCoreAsyncOps
5
+ } from "./chunk-DG3MQ2BR.js";
6
+
7
+ // src/swi-wasm.ts
8
+ import * as core from "@mettascript/core";
9
+ import { emptyExpr } from "@mettascript/core";
10
+ import "@mettascript/core/host";
11
+ import { Atom, E, ExpressionAtom, S, ValueAtom, VariableAtom } from "@mettascript/hyperon";
12
+ import defaultLoadSwipl from "swipl-wasm";
13
+ var SAFE_MIN = BigInt(Number.MIN_SAFE_INTEGER);
14
+ var SAFE_MAX = BigInt(Number.MAX_SAFE_INTEGER);
15
+ var SourceVariables = class {
16
+ sourceToOriginal = /* @__PURE__ */ new Map();
17
+ originalToSource = /* @__PURE__ */ new Map();
18
+ sourceName(original) {
19
+ let source = this.originalToSource.get(original);
20
+ if (source === void 0) {
21
+ source = `V${this.originalToSource.size}`;
22
+ this.originalToSource.set(original, source);
23
+ this.sourceToOriginal.set(source, original);
24
+ }
25
+ return source;
26
+ }
27
+ };
28
+ function fileCandidates(path) {
29
+ if (path.endsWith(".pl")) return [path, path.slice(0, -".pl".length)];
30
+ return [path, `${path}.pl`];
31
+ }
32
+ function globalBaseUrl() {
33
+ const location = globalThis.location;
34
+ return typeof location?.href === "string" ? location.href : void 0;
35
+ }
36
+ function createDefaultTextLoader(options) {
37
+ const files = options.files ?? /* @__PURE__ */ new Map();
38
+ return async (path, from) => {
39
+ for (const candidate of fileCandidates(path)) {
40
+ const text = files.get(candidate);
41
+ if (text !== void 0) return text;
42
+ }
43
+ const base = from ?? options.baseUrl ?? globalBaseUrl();
44
+ if (base === void 0) throw new Error(`swi-wasm import: ${path}: no base URL`);
45
+ const url = new URL(path, base);
46
+ const response = await fetch(url);
47
+ if (!response.ok) throw new Error(`swi-wasm import: ${path}: ${response.status} ${url.href}`);
48
+ return response.text();
49
+ };
50
+ }
51
+ var toPosix = (path) => path.replaceAll("\\", "/");
52
+ function virtualPath(path) {
53
+ const normalized = toPosix(path).replace(/^\.\/+/, "");
54
+ return normalized.startsWith("/") ? normalized : `/metta-ts-prolog/${normalized}`;
55
+ }
56
+ function dirname(path) {
57
+ const i = path.lastIndexOf("/");
58
+ return i <= 0 ? "/" : path.slice(0, i);
59
+ }
60
+ function ensureDir(fs, dir) {
61
+ if (dir === "" || dir === "/") return;
62
+ if (fs.mkdirTree !== void 0) {
63
+ fs.mkdirTree(dir);
64
+ return;
65
+ }
66
+ let current = "";
67
+ for (const part of dir.split("/")) {
68
+ if (part === "") continue;
69
+ current += `/${part}`;
70
+ if (fs.analyzePath?.(current).exists === true) continue;
71
+ fs.mkdir?.(current);
72
+ }
73
+ }
74
+ function quoteAtom(value) {
75
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "''")}'`;
76
+ }
77
+ function quoteString(value) {
78
+ return JSON.stringify(value);
79
+ }
80
+ function termToSource(term, vars) {
81
+ switch (term.type) {
82
+ case "atom":
83
+ return term.name === "[]" ? "[]" : quoteAtom(term.name);
84
+ case "int":
85
+ return term.value;
86
+ case "float":
87
+ return String(term.value);
88
+ case "string":
89
+ return quoteString(term.value);
90
+ case "var":
91
+ return vars.sourceName(term.name);
92
+ case "compound":
93
+ if (term.args.length === 0) return quoteAtom(term.functor);
94
+ return `${quoteAtom(term.functor)}(${term.args.map((arg) => termToSource(arg, vars)).join(",")})`;
95
+ }
96
+ }
97
+ function goalSource(term, vars = new SourceVariables()) {
98
+ return `${termToSource(term, vars)}.`;
99
+ }
100
+ function isObject(value) {
101
+ return typeof value === "object" && value !== null;
102
+ }
103
+ function stringProperty(value, key) {
104
+ const out = value[key];
105
+ return typeof out === "string" ? out : void 0;
106
+ }
107
+ function booleanProperty(value, key) {
108
+ const out = value[key];
109
+ return typeof out === "boolean" ? out : void 0;
110
+ }
111
+ function messageFrom(error) {
112
+ return error instanceof Error && error.message !== "" ? error.message : String(error);
113
+ }
114
+ function throwIfError(value) {
115
+ if (!isObject(value)) return;
116
+ if (booleanProperty(value, "error") === true) {
117
+ throw new Error(stringProperty(value, "message") ?? "SWI-Prolog WASM error");
118
+ }
119
+ }
120
+ function queryStep(value) {
121
+ if (!isObject(value)) return {};
122
+ const out = {};
123
+ const done = booleanProperty(value, "done");
124
+ if (done !== void 0) out.done = done;
125
+ if (value.value !== void 0) out.value = value.value;
126
+ const error = booleanProperty(value, "error");
127
+ if (error !== void 0) out.error = error;
128
+ const message = stringProperty(value, "message");
129
+ if (message !== void 0) out.message = message;
130
+ return out;
131
+ }
132
+ function isSuccess(value) {
133
+ if (!isObject(value)) return false;
134
+ if (booleanProperty(value, "success") === false) return false;
135
+ return booleanProperty(value, "success") === true || value["$tag"] === "bindings";
136
+ }
137
+ function bigintAtom(value) {
138
+ if (value >= SAFE_MIN && value <= SAFE_MAX) return ValueAtom(Number(value));
139
+ return Atom.fromCAtom(core.gint(value));
140
+ }
141
+ function numberAtom(value) {
142
+ return Number.isInteger(value) && value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER ? ValueAtom(value) : ValueAtom(value);
143
+ }
144
+ function prologValueToAtom(value) {
145
+ if (typeof value === "string") return S(value);
146
+ if (typeof value === "number") return numberAtom(value);
147
+ if (typeof value === "bigint") return bigintAtom(value);
148
+ if (typeof value === "boolean") return S(value ? "true" : "false");
149
+ if (value === null || value === void 0) return VariableAtom.parseName("_");
150
+ if (Array.isArray(value)) return E(...value.map(prologValueToAtom));
151
+ if (!isObject(value)) return S(String(value));
152
+ if (value["$t"] === "s") {
153
+ const text = stringProperty(value, "v");
154
+ return text === void 0 ? ValueAtom("") : ValueAtom(text);
155
+ }
156
+ if (value["$t"] === "v") return VariableAtom.parseName("_");
157
+ if (value["$t"] === "t") {
158
+ const functor = stringProperty(value, "functor");
159
+ if (functor === void 0) return S(String(value));
160
+ const rawArgs = value[functor];
161
+ const args = Array.isArray(rawArgs) && Array.isArray(rawArgs[0]) ? rawArgs[0].map(prologValueToAtom) : [];
162
+ return E(S(functor), ...args);
163
+ }
164
+ return S(String(value));
165
+ }
166
+ function instantiate(atom, bindings) {
167
+ if (atom instanceof VariableAtom) return bindings.get(atom.name()) ?? atom;
168
+ if (atom instanceof ExpressionAtom)
169
+ return E(...atom.children().map((child) => instantiate(child, bindings)));
170
+ return atom;
171
+ }
172
+ function bindingsFromAnswer(answer, sourceToOriginal) {
173
+ const out = /* @__PURE__ */ new Map();
174
+ if (!isObject(answer)) return out;
175
+ for (const [source, original] of sourceToOriginal) {
176
+ if (Object.hasOwn(answer, source)) out.set(original, prologValueToAtom(answer[source]));
177
+ }
178
+ return out;
179
+ }
180
+ function importAtomName(atom) {
181
+ if (atom?.kind === "sym") return atom.name;
182
+ if (atom?.kind === "gnd" && atom.value.g === "str") return atom.value.s;
183
+ if (atom?.kind === "expr" && atom.items.length === 2 && atom.items[0]?.kind === "sym" && atom.items[0].name === "library")
184
+ return importAtomName(atom.items[1]);
185
+ return void 0;
186
+ }
187
+ function collectAnswers(query) {
188
+ const out = [];
189
+ for (; ; ) {
190
+ const step = queryStep(query.next());
191
+ if (step.error === true) throw new Error(step.message ?? "SWI-Prolog WASM query error");
192
+ if (step.value !== void 0) {
193
+ throwIfError(step.value);
194
+ out.push(step.value);
195
+ }
196
+ if (step.done === true) break;
197
+ }
198
+ return out;
199
+ }
200
+ var SwiWasmBridge = class {
201
+ constructor(runtime, options = {}) {
202
+ this.runtime = runtime;
203
+ this.loadText = options.loadText ?? createDefaultTextLoader(options);
204
+ }
205
+ runtime;
206
+ loadText;
207
+ consulted = /* @__PURE__ */ new Set();
208
+ async query(goal) {
209
+ const vars = new SourceVariables();
210
+ const source = goalSource(atomToPrologTerm(goal), vars);
211
+ const answers = collectAnswers(this.runtime.prolog.query(source));
212
+ return answers.map(
213
+ (answer) => instantiate(goal, bindingsFromAnswer(answer, vars.sourceToOriginal))
214
+ );
215
+ }
216
+ async asserta(term) {
217
+ this.runOnce(
218
+ goalSource({ type: "compound", functor: "asserta", args: [atomToPrologTerm(term)] })
219
+ );
220
+ }
221
+ async assertz(term) {
222
+ this.runOnce(
223
+ goalSource({ type: "compound", functor: "assertz", args: [atomToPrologTerm(term)] })
224
+ );
225
+ }
226
+ async retract(term) {
227
+ return this.runOnce(
228
+ goalSource({ type: "compound", functor: "retract", args: [atomToPrologTerm(term)] })
229
+ );
230
+ }
231
+ async consult(path) {
232
+ if (this.consulted.has(path)) return;
233
+ try {
234
+ const source = await this.loadText(path);
235
+ const target = virtualPath(path);
236
+ ensureDir(this.runtime.FS, dirname(target));
237
+ this.runtime.FS.writeFile(target, source, { encoding: "utf8" });
238
+ this.runOnce(`consult(${quoteAtom(target)}).`);
239
+ this.consulted.add(path);
240
+ } catch (error) {
241
+ throw new Error(`swi-wasm consult: ${path}: ${messageFrom(error)}`);
242
+ }
243
+ }
244
+ async predicateArities(name) {
245
+ const query = this.runtime.prolog.query(`current_predicate(${quoteAtom(name)}/A).`);
246
+ const arities = /* @__PURE__ */ new Set();
247
+ for (const answer of collectAnswers(query)) {
248
+ if (!isObject(answer)) continue;
249
+ const arity = answer.A;
250
+ if (typeof arity === "number" && Number.isInteger(arity)) arities.add(arity);
251
+ }
252
+ return [...arities].sort((a, b) => a - b);
253
+ }
254
+ dispose() {
255
+ this.consulted.clear();
256
+ }
257
+ runOnce(goal) {
258
+ const result = this.runtime.prolog.query(goal).once();
259
+ throwIfError(result);
260
+ return isSuccess(result);
261
+ }
262
+ };
263
+ function swiWasmBridge(runtime, options = {}) {
264
+ return new SwiWasmBridge(runtime, options);
265
+ }
266
+ async function createSwiWasmInterop(options = {}) {
267
+ const loadSwipl = options.loadSwipl ?? defaultLoadSwipl;
268
+ const runtime = options.prolog ?? await loadSwipl({
269
+ arguments: [...options.arguments ?? ["-q"]],
270
+ ...options.locateFile !== void 0 ? { locateFile: options.locateFile } : {}
271
+ });
272
+ const bridge = swiWasmBridge(runtime, options);
273
+ for (const path of options.preload ?? []) await bridge.consult(path);
274
+ return {
275
+ name: "swi-wasm",
276
+ prelude: PROLOG_METTA_SRC,
277
+ asyncOps: prologCoreAsyncOps(bridge),
278
+ hostImport: async (_space, target) => {
279
+ const name = importAtomName(target);
280
+ if (!name?.endsWith(".pl")) return { tag: "noReduce" };
281
+ try {
282
+ await bridge.consult(name);
283
+ return { tag: "ok", results: [emptyExpr] };
284
+ } catch (error) {
285
+ return { tag: "runtimeError", msg: `import!: ${name}: ${messageFrom(error)}` };
286
+ }
287
+ },
288
+ dispose: () => bridge.dispose()
289
+ };
290
+ }
291
+ export {
292
+ SwiWasmBridge,
293
+ createSwiWasmInterop,
294
+ swiWasmBridge
295
+ };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@mettascript/prolog",
3
+ "version": "2.0.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./swi-node": {
14
+ "types": "./dist/swi-node.d.ts",
15
+ "default": "./dist/swi-node.js"
16
+ },
17
+ "./swi-wasm": {
18
+ "types": "./dist/swi-wasm.d.ts",
19
+ "default": "./dist/swi-wasm.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "dependencies": {
27
+ "@mettascript/core": "2.0.0",
28
+ "@mettascript/hyperon": "2.0.0"
29
+ },
30
+ "author": "MesTTo",
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "sideEffects": false,
35
+ "module": "./dist/index.js",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "description": "Prolog interop for MeTTaScript: PeTTa-compatible Predicate/callPredicate/prolog-call/import_prolog_function over SWI-Prolog or SWI-WASM.",
40
+ "keywords": [
41
+ "metta",
42
+ "hyperon",
43
+ "prolog",
44
+ "swi-prolog",
45
+ "interop",
46
+ "typescript"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/MesTTo/MeTTaScript.git",
51
+ "directory": "packages/prolog"
52
+ },
53
+ "homepage": "https://github.com/MesTTo/MeTTaScript#readme",
54
+ "bugs": {
55
+ "url": "https://github.com/MesTTo/MeTTaScript/issues"
56
+ },
57
+ "devDependencies": {
58
+ "swipl-wasm": "8.0.3"
59
+ },
60
+ "peerDependencies": {
61
+ "swipl-wasm": "8.0.3"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "swipl-wasm": {
65
+ "optional": true
66
+ }
67
+ },
68
+ "scripts": {
69
+ "build": "tsup src/index.ts src/swi-node.ts src/swi-wasm.ts --format esm --dts --clean"
70
+ }
71
+ }