Skip to content

Commit 325f676

Browse files
authored
Merge pull request #23 from thefrontside/cl/exported-run
add `run()` to platformscript interpreter
2 parents 4209639 + f9420ae commit 325f676

6 files changed

Lines changed: 78 additions & 51 deletions

File tree

evaluate.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
import { parseYAML } from "./deps.ts";
1111
import { yaml2ps } from "./convert.ts";
1212
import { concat, lookup, map, Maybe } from "./psmap.ts";
13+
import * as data from "./data.ts";
1314

1415
type Segment = {
1516
type: "const";
@@ -51,7 +52,7 @@ function* segments(t: PSTemplate): Generator<Segment> {
5152
}
5253

5354
export function createYSEnv(parent = global): PSEnv {
54-
return {
55+
let env: PSEnv = {
5556
*eval(value, context = { type: "map", value: new Map() }) {
5657
let scope = concat(parent, context);
5758
let env = createYSEnv(scope);
@@ -155,10 +156,15 @@ export function createYSEnv(parent = global): PSEnv {
155156
return value;
156157
}
157158
},
158-
*call(fn, funcall) {
159-
return yield* fn.value(funcall);
159+
call(fn, arg, options) {
160+
return fn.value({
161+
arg,
162+
env,
163+
rest: options ?? data.map({}),
164+
});
160165
},
161166
};
167+
return env;
162168
}
163169

164170
export const letdo = {

platformscript.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,50 @@
1-
import type { PSFn, PSFnCall, PSMap, PSModule, PSValue } from "./types.ts";
2-
import type { Task } from "./deps.ts";
3-
import { createYSEnv, global, parse } from "./evaluate.ts";
4-
import { concat, createPSMap } from "./psmap.ts";
1+
import type { PSEnv, PSFn, PSMap, PSModule, PSValue } from "./types.ts";
2+
import type { Operation, Task } from "./deps.ts";
3+
import { createYSEnv, global } from "./evaluate.ts";
4+
import { concat } from "./psmap.ts";
55
import { load } from "./load.ts";
6-
import { run } from "./deps.ts";
6+
import { map } from "./data.ts";
7+
import { run as $run } from "./deps.ts";
78

89
export interface PlatformScript {
9-
call(fn: PSFn, funcall: PSFnCall): Task<PSValue>;
10+
run<T>(block: (env: PSEnv) => Operation<T>): Task<T>;
11+
call(fn: PSFn, arg: PSValue, options?: PSMap): Task<PSValue>;
1012
eval(value: PSValue, bindings?: PSMap): Task<PSValue>;
1113
load(url: string | URL, base?: string): Task<PSModule>;
12-
parse(source: string, filename?: string): PSValue;
1314
}
1415

15-
export function createPlatformScript(extensions?: PSMap): PlatformScript {
16-
let ext = extensions ?? createPSMap();
17-
let env = createYSEnv(concat(global, ext));
16+
export function createPlatformScript(
17+
globals?: (ps: PlatformScript) => PSMap,
18+
): PlatformScript {
19+
let env = lazy(() => {
20+
let ext = globals ? globals(platformscript) : map({});
21+
return createYSEnv(concat(global, ext));
22+
});
1823

19-
return {
20-
call(fn, funcall) {
21-
return run(() => env.call(fn, funcall));
24+
function run<T>(block: (env: PSEnv) => Operation<T>): Task<T> {
25+
return $run(() => block(env()));
26+
}
27+
28+
let platformscript: PlatformScript = {
29+
run,
30+
call(fn, arg, options) {
31+
return run((env) => env.call(fn, arg, options));
2232
},
2333
eval(value, bindings) {
24-
return run(() => env.eval(value, bindings));
34+
return run((env) => env.eval(value, bindings));
2535
},
2636
load(location, base) {
27-
return run(() => load({ location, base, env }));
28-
},
29-
parse(source, filename) {
30-
return parse(source, filename);
37+
return run((env) => load({ location, base, env }));
3138
},
3239
};
40+
return platformscript;
41+
}
42+
43+
function lazy<T>(create: () => T): () => T {
44+
let thunk = () => {
45+
let value = create();
46+
thunk = () => value;
47+
return value;
48+
};
49+
return () => thunk();
3350
}

test/external.test.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,56 @@
11
import { describe, expect, it } from "./suite.ts";
2-
import { createPlatformScript, external, map, number } from "../mod.ts";
2+
import { createPlatformScript, external, map, number, parse } from "../mod.ts";
33

44
import * as _ from "https://raw.githubusercontent.com/lodash/lodash/4.17.21-es/lodash.js";
55

66
describe("external", () => {
77
it("can represent any arbitrary javascript value", async () => {
88
let obj = { unqiue: "object" };
9-
let ps = createPlatformScript(map({
10-
"extern": external(obj),
11-
}));
12-
expect((await ps.eval(ps.parse("$extern"))).value).toBe(obj);
9+
let ps = createPlatformScript(() =>
10+
map({
11+
"extern": external(obj),
12+
})
13+
);
14+
expect((await ps.eval(parse("$extern"))).value).toBe(obj);
1315
});
1416

1517
it("can define new PS values from the external value", async () => {
1618
let obj = { I: { contain: { the: { number: number(5) } } } };
17-
let ps = createPlatformScript(map({
18-
"truly": external(obj, (path, o) => _.get(o, path)),
19-
}));
19+
let ps = createPlatformScript(() =>
20+
map({
21+
"truly": external(obj, (path, o) => _.get(o, path)),
22+
})
23+
);
2024

21-
let program = ps.parse("$truly.I.contain.the.number");
25+
let program = parse("$truly.I.contain.the.number");
2226
expect((await ps.eval(program)).value).toEqual(5);
2327
});
2428
it("errors if a dereference is undefined", async () => {
25-
let ps = createPlatformScript(map({
26-
"oops": external({}, () => void 0),
27-
}));
29+
let ps = createPlatformScript(() =>
30+
map({
31+
"oops": external({}, () => void 0),
32+
})
33+
);
2834
try {
29-
await ps.eval(ps.parse("$oops.i.did.it.again"));
35+
await ps.eval(parse("$oops.i.did.it.again"));
3036
throw new Error("expected block to throw, but it did not");
3137
} catch (error) {
3238
expect(error.name).toEqual("ReferenceError");
3339
}
3440
});
3541
it("errors if a derefenence does not return a PSValue", async () => {
36-
let ps = createPlatformScript(map({
37-
"oops": external(
38-
{ wrong: "type" },
39-
//@ts-expect-error situation could happen if you are using JavaScript...
40-
() => ({ type: "WAT!!", value: "hi" }),
41-
),
42-
}));
42+
let ps = createPlatformScript(() =>
43+
map({
44+
"oops": external(
45+
{ wrong: "type" },
46+
//@ts-expect-error situation could happen if you are using JavaScript...
47+
() => ({ type: "WAT!!", value: "hi" }),
48+
),
49+
})
50+
);
4351

4452
try {
45-
await ps.eval(ps.parse("$oops.wrong"));
53+
await ps.eval(parse("$oops.wrong"));
4654
throw new Error("expected block to throw, but it did not");
4755
} catch (error) {
4856
expect(error.message).toMatch(

test/fn.test.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@ import * as ps from "../mod.ts";
44
describe("fn", () => {
55
it("can be called directly", async () => {
66
let interp = ps.createPlatformScript();
7-
let program = interp.parse("$(thing): Hello %($thing)!");
7+
let program = ps.parse("$(thing): Hello %($thing)!");
88
let fn = await interp.eval(program);
99
assert(fn.type === "fn");
1010
expect(
11-
(await interp.call(fn, {
12-
arg: ps.string("World"),
13-
rest: ps.map({}),
14-
env: ps.createYSEnv(),
15-
})).value,
11+
(await interp.call(fn, ps.string("World"))).value,
1612
).toEqual("Hello World!");
1713
});
1814
});

test/suite.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ export * from "https://deno.land/[email protected]/testing/bdd.ts";
22
export { expect } from "https://deno.land/x/[email protected]/mod.ts";
33
export * from "https://deno.land/[email protected]/testing/asserts.ts";
44

5-
import { createPlatformScript, js2ps, ps2js, PSMap } from "../mod.ts";
5+
import { createPlatformScript, js2ps, parse, ps2js, PSMap } from "../mod.ts";
66

77
export function evaluate(source: string, scope?: PSMap) {
88
let ps = createPlatformScript();
9-
let value = ps.parse(source, "script");
9+
let value = parse(source, "script");
1010
return ps.eval(value, scope);
1111
}
1212

types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Operation, YAMLNode } from "./deps.ts";
33

44
export interface PSEnv {
55
eval(value: PSValue, scope?: PSMap): Operation<PSValue>;
6-
call(fn: PSFn, funcall: PSFnCall): Operation<PSValue>;
6+
call(fn: PSFn, arg: PSValue, options?: PSMap): Operation<PSValue>;
77
}
88

99
export interface PSModule {

0 commit comments

Comments
 (0)