Skip to content

Commit

Permalink
fix(pointfree-lang): add ensureEnv, update re-exports, update readme
Browse files Browse the repository at this point in the history
- add ensureEnv to avoid errors if `__words` key is missing
- minor formatting fix in grammar
  • Loading branch information
postspectacular committed Mar 31, 2018
1 parent a0bf781 commit 659cce9
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 34 deletions.
61 changes: 51 additions & 10 deletions packages/pointfree-lang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

This project is part of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo.

<!-- TOC depthFrom:2 depthTo:3 -->
<!-- TOC depthFrom:2 depthTo:4 -->

- [About](#about)
- [Status](#status)
Expand All @@ -14,6 +14,7 @@ This project is part of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrell
- [Comments](#comments)
- [Identifiers](#identifiers)
- [Word definitions](#word-definitions)
- [Hyperstatic words](#hyperstatic-words)
- [Boolean](#boolean)
- [Numbers](#numbers)
- [Strings](#strings)
Expand All @@ -30,17 +31,19 @@ This project is part of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrell

## About

Experimental, small DSL with compact
Experimental language layer with compact
[Forth](https://en.wikipedia.org/wiki/Forth_(programming_language))
style syntax for
[@thi.ng/pointfree](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree):
style syntax for the
[@thi.ng/pointfree](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree),
an ES6 embedded DSL for concatenative programming:

- [PegJS](https://pegjs.org/) based
[grammar](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree-lang/src/grammar.pegjs)
& parser
- untyped, interpreted, but with AOT compilation of user defined words
- support for custom / externally defined vocabularies (word sets)
- lexically scoped variables
- hyperstatic word definitions
- support for custom / externally defined vocabularies (word sets / JS functions)
- scoped variables (stored in environment object)
- nested quotations (code as data)
- array & object literals (optionally w/ computed properties)
- all other features of @thi.ng/pointfree (combinators, array/vector ops etc.)
Expand Down Expand Up @@ -89,6 +92,9 @@ const src = `

const drawLine = (ctx) => {
const stack = ctx[0];
// minimum stack depth guard
pf.ensureStack(stack, 2);
// pop top 2 values
const [x2, y2] = stack.pop();
const [x1, y1] = stack.pop();
console.log(`draw line: ${x1},${y1} -> ${x2},${y2}`);
Expand Down Expand Up @@ -160,7 +166,8 @@ drastically. In @thi.ng/pointfree (and therefore also in this DSL layer):
- the DSL has syntax sugar for variable value lookups & assignments
- the DSL allows nested quotations & object literals, optionally with
lazily resolved computed properties and/or values
- all symbols are separated by whitespace (like in Clojure, commas are considered whitespace too)
- all symbols are separated by whitespace (like in Clojure, commas are
considered whitespace too)

### Comments

Expand Down Expand Up @@ -188,8 +195,9 @@ ______ ____ |__| _____/ |__/ ____\______ ____ ____

### Identifiers

Word identifiers can contain any alhpanumeric character and these additional ones: `*?$%&/|~<>=._+-`.
Digits are not allowed as first char.
Word identifiers can contain any alhpanumeric character and these
additional ones: `*?$%&/|~<>=._+-`. Digits are not allowed as first
char.

All 100+ built-in words defined by
[@thi.ng/pointfree](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree)
Expand Down Expand Up @@ -234,6 +242,34 @@ As in Forth, new words can be defined using the `: name ... ;` form.

```
: square ( x -- x*x ) dup * ;
10 square .
```

Will result in `100`.

#### Hyperstatic words

Unlike [variables](#variables), words are defined in a
[hyper-static](http://wiki.c2.com/?HyperStaticGlobalEnvironment)
environment, meaning new versions of existing words can be defined,
however any other word (incl. the new version of same word) which uses
the earlier version will continue to do so. By implication, this too
means that attempting to use undefined words inside a word definition
will fail, even if they'd be defined later on.

```ts
pf.run(`
: foo "foo1" ;
: bar foo "bar" + ;
( redefine foo, incl. use of existing version )
: foo foo "foo2" + ;
( use words )
foo bar
`)[0];
// [ 'foo1foo2', 'foo1bar' ]
```

There're no formatting rules enforced (yet, but under consideration).
Expand Down Expand Up @@ -283,13 +319,18 @@ pf.runU(`@a @b +`, {a: 10, b: 20});
// 30
```

Storing a stack value in a variable is done via the `!` suffix:
Storing a stack value in a variable (in the the current environment) is
done via the `!` suffix:

```ts
pf.runE(`1 2 + a!`)
// {a: 3}
```

Furthermore, readonly variables can be defined via words. In this case
no prefix must be used and these kind of variables are
[hyperstatic](#hyperstatic-words).

TODO add info about scoping and resolution in words / quotations...

### Objects
Expand Down
36 changes: 18 additions & 18 deletions packages/pointfree-lang/src/grammar.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Root

Expr
= _ expr:(
Word
Word
/ Quot
/ LitQuote
/ Var
Expand All @@ -39,8 +39,8 @@ Expr
) _ { return ast(expr); }

Word
= ":" __ id:Sym body:Expr+ ";" {
return { type: NodeType.WORD, id: id.id, body};
= ":" __ id:Sym body:Expr+ ";" {
return { type: NodeType.WORD, id: id.id, body};
}

Quot
Expand All @@ -59,18 +59,18 @@ Map
}

MapPair
= k:MapKey v:MapVal { return [ k, v ]; }
= k:MapKey v:MapVal { return [ k, v ]; }

MapKey
= k:(String / Sym / Number / VarDeref) ":" { return k; }
= k:(String / Sym / Number / VarDeref) ":" { return k; }

MapVal
= _ val:(
Atom
/ Quot
/ LitQuote
/ VarDeref
/ Map
Atom
/ Quot
/ LitQuote
/ VarDeref
/ Map
// / Set
) _ { return val; }

Expand Down Expand Up @@ -105,20 +105,20 @@ SymRest
/ SymChars

SymChars
= [*?$%&/\|~<>=._+\-]
= [*?$%&/\|~<>=._+\-]

Var
= VarDeref
/ VarStore

VarDeref
= "@" id:Sym {
return {type: NodeType.VAR_DEREF, id: id.id}
= "@" id:Sym {
return {type: NodeType.VAR_DEREF, id: id.id}
}

VarStore
= id:Sym "!" {
return {type: NodeType.VAR_STORE, id: id.id}
= id:Sym "!" {
return {type: NodeType.VAR_STORE, id: id.id}
}

LitQuote
Expand All @@ -129,7 +129,7 @@ LitQuote
Comment
= "("+ body:$(!")" .)* ")" {
return body.indexOf("--") > 0 ?
{ type: NodeType.STACK_COMMENT,
{ type: NodeType.STACK_COMMENT,
body: body.split("--").map(x => x.trim().split(" "))
} :
{ type: NodeType.COMMENT, body: body.trim()};
Expand Down Expand Up @@ -157,10 +157,10 @@ Hex
}

Int
= Sign? Uint
= Sign? Uint

Uint
= Digit+
= Digit+

Decimal
= Int ("." Uint?)? ("e" Int)? {
Expand Down
27 changes: 21 additions & 6 deletions packages/pointfree-lang/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,30 +172,45 @@ const deferedPair = (res: any, k, v) => {
(ctx: pf.StackContext) => (res[k] = resolveVar(v.id, ctx), ctx);
};

export const ensureEnv = (env?: pf.StackEnv) => {
env = env || {};
if (!env.__words) {
env.__words = {};
}
return env;
};

export const run = (src: string, env?: pf.StackEnv, stack: pf.Stack = []) => {
let ctx = pf.ctx(stack, { __words: {}, ...env });
let ctx = pf.ctx(stack, ensureEnv(env));
for (let node of parse(src)) {
ctx = visit(node, ctx);
}
return ctx;
};

export const runU = (src: string, env?: pf.StackEnv, stack: pf.Stack = [], n = 1) =>
export const runU = (src: string, env?: pf.StackEnv, stack?: pf.Stack, n = 1) =>
pf.unwrap(run(src, env, stack), n);

export const runE = (src: string, env?: pf.StackEnv, stack: pf.Stack = []) =>
export const runE = (src: string, env?: pf.StackEnv, stack?: pf.Stack) =>
run(src, env, stack)[2];

export const runWord = (id: string, env?: pf.StackEnv, stack: pf.Stack = []) =>
env.__words[id](pf.ctx(stack, env));
env.__words[id](pf.ctx(stack, ensureEnv(env)));

export const runWordU = (id: string, env?: pf.StackEnv, stack: pf.Stack = [], n = 1) =>
pf.unwrap(env.__words[id](pf.ctx(stack, env)), n);
pf.unwrap(env.__words[id](pf.ctx(stack, ensureEnv(env))), n);

export const runWordE = (id: string, env?: pf.StackEnv, stack: pf.Stack = []) =>
env.__words[id](pf.ctx(stack, env))[2];
env.__words[id](pf.ctx(stack, ensureEnv(env)))[2];

export const ffi = (env: any, words: IObjectOf<pf.StackFn>) => {
env = ensureEnv(env);
env.__words = { ...env.__words, ...words };
return env;
};

export {
ensureStack,
ensureStackN,
unwrap,
} from "@thi.ng/pointfree";
3 changes: 3 additions & 0 deletions packages/pointfree-lang/test/readme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ const src = `

const drawLine = (ctx) => {
const stack = ctx[0];
// minimum stack depth guard
pf.ensureStack(stack, 2);
// pop top 2 values
const [x2, y2] = stack.pop();
const [x1, y1] = stack.pop();
console.log(`draw line: ${x1},${y1} -> ${x2},${y2}`);
Expand Down

0 comments on commit 659cce9

Please sign in to comment.