-
Notifications
You must be signed in to change notification settings - Fork 0
/
lisp.mz
148 lines (104 loc) · 4.48 KB
/
lisp.mz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# ./lisp.mjs
<!-- toc -->
The furver lisp is an essential part of the furver experience. Firstly it
enables the bulk and parallel handling of requests when using the user friendly
client. Secondly it allows for more advanced usage by using the furver lisp
directly.
This docs shows what the lisp features are. It also shows that you can use the
lisp module directly by importing it.
## Import
To use the Lisp interpreter, you first need to import the `exec` function from
the module:
```javascript cat - > .tmp.lisp_import.mjs
import exec from './lisp.mjs'
```
Once you have imported the `exec` function, you can call it with an environment
object and an expression to evaluate:
## Basic usage
```javascript cat .tmp.lisp_import.mjs - > .tmp.lisp_exec.mjs; node .tmp.lisp_exec.mjs
const env = { 'add': (a, b) => a + b }
const expression = ['add', 1, 2]
exec(env, expression).then(console.log)
```
The `exec` function takes two arguments:
1. The environment object in which the expression is evaluated. This is an
object that maps operator names to functions that implement those operators.
In the example above, the environment object contains a `add` operator that
adds two numbers.
2. The expression to evaluate. This is an array that represents the expression
in the Lisp syntax. The first element of the array is the operator name, and
the rest of the elements are the arguments to the operator. In the example
above, the expression is `['add', 1, 2]`, which means "add 1 and 2".
## Async
The lisp is will wait for a promise returned by a call before calling the next
function.
```javascript cat .tmp.lisp_import.mjs - > .tmp.lisp_exec.mjs; node .tmp.lisp_exec.mjs
const env = { 'add': (a, b) => a + b, pIdentity: (x) => Promise.resolve(x) }
exec(env, ['add', ['pIdentity', 1], 2]).then(console.log)
```
> Define a `promiseAll` helper on the env if you are working with arrays of
> promises.
## Let Expressions
The Lisp interpreter supports let expressions, which allow you to define local
variables in an expression. A let expression has the form:
```javascript
[let [[name1 value1] [name2 value2] ...] body]
```
The `let` operator takes two arguments:
1. An array of bindings, where each binding is a pair of a variable name and
its value.
2. The body expression, which is evaluated in a new environment that includes
the variable bindings.
Here's an example of using a let expression:
```javascript cat .tmp.lisp_import.mjs - > .tmp.lisp_let.mjs; node .tmp.lisp_let.mjs
const env = { 'add': (a, b) => a + b, always: (x) => () => x }
const expression = ['let',
[
['x', ['always', 2]],
['y', ['always', 3]],
],
['add', ['x'], ['y']]]
exec(env, expression).then(console.log)
```
This expression defines two lexically scoped variables `x` and `y` with values
2 and 3, respectively, and then adds them together.
## Fn Expressions
The Lisp interpreter also supports fn expressions, which allow you to define
anonymous functions. An fn expression has the form:
```javascript
[fn body]
```
The `fn` operator takes only the body argument. It does not support arguments
as the environment already allows passing aliased values to the body. If you
wish to define your own values you should use the let statement.
Here's an example of using an fn expression:
```javascript cat .tmp.lisp_import.mjs - > .tmp.lisp_fn.mjs; node .tmp.lisp_fn.mjs
const env = { '+': (a, b) => a + b, x: () => 2}
const expression = ['fn', ['+', 1, ['x']]]
exec(env, expression)
.then(async fn => console.log(await fn(), fn))
```
This expression defines an anonymous function that adds 1 to a single argument
`x`.
You can also overwrite the env value for `x` by passing an object to the
function object.
```javascript cat .tmp.lisp_import.mjs - > .tmp.lisp_fn.mjs; node .tmp.lisp_fn.mjs
const env = { '+': (a, b) => a + b }
const expression = ['fn', ['+', 1, ['ref', 'x']]]
exec(env, expression)
.then(fn => fn({ x: 2 }))
.then(console.log)
```
## Ref Keyword
You might see a `ref` in the previous example. The ref allows you to get the
reference/value of a specific alias on the environment. This removes the need
for wrapping a value in a function that returns itself.
```javascript
[ref, name]
```
```javascript cat .tmp.lisp_import.mjs - > .tmp.lisp_ref.mjs; node .tmp.lisp_ref.mjs
exec({ 'hello': 'world' }, ['ref', 'hello']).then(console.log)
```
In conclusion, the lisp.mjs module provides an asynchronous implementation of
a Lisp interpreter that supports basic expressions, let expressions, ref keyword and fn
expressions.