Skip to content

Commit

Permalink
disambiguate atoms, report residual goals, encoding options (#4 #5 #6)
Browse files Browse the repository at this point in the history
  • Loading branch information
guregu committed Sep 29, 2022
1 parent 38b72c7 commit 6a7fd2b
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 14 deletions.
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# trealla-js

Javascript bindings for [Trealla Prolog](https://github.com/trealla-prolog/trealla) via [wasmer-js](https://github.com/wasmerio/wasmer-js) and WASI.
Javascript bindings for [Trealla Prolog](https://github.com/trealla-prolog/trealla) via [wasmer-js](https://github.com/wasmerio/wasmer-js).

Trealla is a quick and lean ISO Prolog interpreter.

Trealla is built targeting [WASI](https://wasi.dev/) and should be useful for both browsers and serverless runtimes.

**Demo**: https://php.energy/trealla.html

Expand All @@ -18,7 +22,7 @@ import { loadFromWAPM, Prolog } from 'https://esm.sh/trealla';
// load the Trealla binary from WAPM.io, make sure to use the latest version!
// see: https://wapm.io/guregu/trealla
await loadFromWAPM("0.5.1");
await loadFromWAPM("0.6.0");
// alternatively, host it yourself and use the load function instead of loadFromWAPM:
// await load(await WebAssembly.compileStreaming(fetch("https://example.com/foo/bar/tpl.wasm"));
Expand Down Expand Up @@ -107,6 +111,12 @@ declare module 'trealla' {
interface QueryOptions {
// Prolog program text to evaluate before the query
program?: string | Uint8Array;
encode?: {
// Encoding for Prolog atoms. Default is "object".
atoms?: "string" | "object";
// Encoding for Prolog strings. Default is "string".
strings?: "string" | "list";
}
}

interface Answer {
Expand All @@ -118,21 +128,40 @@ declare module 'trealla' {

type Solution = Record<string, Term>;

type Term = Compound | Variable | List | string | number;
/*
Default encoding (in order of priority):
string(X) → string
is_list(X) → List
atom(X) → Atom
compound(X) → Compound
number(X) → number
var(X) → Variable
*/
type Term = Atom | Compound | Variable | List | string | number;

interface Atom {
functor: string;
}

interface Compound {
functor: string;
args: Term[];
}

interface Variable {
var: string;
var: string; // variable name
attr?: Term[]; // residual goals
}

type List = Term[];
}
```

## Implementation Details

Currently uses the WASM build from [guregu/trealla](https://github.com/guregu/trealla).
Output goes through the [`js_toplevel`](https://github.com/guregu/trealla/blob/main/library/js_toplevel.pl) module.

## See Also

- [trealla-prolog/go](https://github.com/trealla-prolog/go) is Trealla for Go.
Expand Down
4 changes: 2 additions & 2 deletions example/browser/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ <h3>Tinker</h3>
<button onclick="return resetProlog(),false;">Reset Interpreter</button>

<script type="module">
import { loadFromWAPM, Prolog } from 'https://esm.sh/trealla@0.6.1';
import { loadFromWAPM, Prolog } from 'https://esm.sh/trealla@0.7.0';

await loadFromWAPM("0.5.1");
await loadFromWAPM("0.6.0");

let pl = new Prolog();

Expand Down
2 changes: 1 addition & 1 deletion example/node.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ await pl.queryOnce("assertz(lang(prolog)), greeting:assertz(hello('Welt')).");

// run a query on the file we loaded and facts we asserted
await dumpQuery(
pl.query(`use_module(greeting), hello(Planet), lang(Lang), format("hello ~w from ~w!~n", [Planet, Lang]).`));
pl.query(`use_module(greeting), hello(Planet), lang(Lang), format("hello ~w from ~w!~n", [Planet, Lang]).`, {encode: {atoms: "string"}}));

/*
{
Expand Down
24 changes: 22 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ declare module 'trealla' {
interface QueryOptions {
// Prolog program text to evaluate before the query
program?: string | Uint8Array;
encode?: {
// Encoding for Prolog atoms. Default is "object".
atoms?: "string" | "object";
// Encoding for Prolog strings. Default is "string".
strings?: "string" | "list";
}
}

interface Answer {
Expand All @@ -36,15 +42,29 @@ declare module 'trealla' {

type Solution = Record<string, Term>;

type Term = Compound | Variable | List | string | number;
/*
Default encoding (in order of priority):
string(X) → string
is_list(X) → List
atom(X) → Atom
compound(X) → Compound
number(X) → number
var(X) → Variable
*/
type Term = Atom | Compound | Variable | List | string | number;

interface Atom {
functor: string;
}

interface Compound {
functor: string;
args: Term[];
}

interface Variable {
var: string;
var: string; // variable name
attr?: Term[]; // residual goals
}

type List = Term[];
Expand Down
34 changes: 30 additions & 4 deletions index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class Prolog {
* Call the return() method of the generator to kill it early. */
async* query(goal, options = {}) {
if (!this.instance) await this.init()
const { program } = options;
const { program, encode } = options;

const _id = ++this.n;
const token = {};
Expand Down Expand Up @@ -102,7 +102,7 @@ export class Prolog {
// "; false."
return;
}
yield parseOutput(stdout);
yield parseOutput(stdout, encode);
} while(alive = pl_redo(subquery) === 1)
} finally {
if (finalizing) {
Expand Down Expand Up @@ -181,7 +181,7 @@ export class Prolog {
}
}

function parseOutput(stdout) {
function parseOutput(stdout, opts) {
const dec = new TextDecoder();
let start = stdout.indexOf(2); // ASCII START OF TEXT
const end = stdout.indexOf(3); // ASCII END OF TEXT
Expand All @@ -191,11 +191,37 @@ function parseOutput(stdout) {
const nl = stdout.indexOf(10, end+1); // LINE FEED
const butt = nl >= 0 ? nl : stdout.length;
const json = dec.decode(stdout.slice(end + 1, butt));
const msg = JSON.parse(json);
const msg = JSON.parse(json, reviver(opts));
msg.output = dec.decode(stdout.slice(start + 1, end));
return msg;
}

function reviver(opts) {
if (!opts) return undefined;
const { atoms, strings } = opts;
return function(k, v) {
// atoms
if (typeof v === "object" && typeof v.functor === "string" && (!v.args || v.args.length === 0)) {
switch (atoms) {
case "string":
return v.functor;
case "object":
return v;
}
}
// strings
if (typeof v === "string" && k !== "result" && k !== "output") {
switch (strings) {
case "list":
return v.split("");
case "string":
return v;
}
}
return v;
}
}

function newWASI(library) {
const args = ["tpl", "--ns", "-q", "-g", "halt"];
if (library) {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "trealla",
"version": "0.6.2",
"version": "0.7.0",
"description": "Trealla Prolog bindings for JS",
"main": "index.mjs",
"type": "module",
Expand All @@ -14,6 +14,7 @@
"prolog",
"logic",
"logic-programming",
"interpreter",
"trealla-prolog"
],
"author": "guregu",
Expand Down

0 comments on commit 6a7fd2b

Please sign in to comment.