-
Notifications
You must be signed in to change notification settings - Fork 0
/
interpret.js
60 lines (52 loc) · 1.42 KB
/
interpret.js
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
const {
append, apply, curryN, filter,
head, isArrayLike, join, length,
map, merge, nth, pipe, replace,
split, tail, trim, values,
zipObj
} = require('ramda')
const tokenize = pipe(trim, replace(/\(/g, '( '), replace(/\)/g, ' )'), split(/\s+/))
const ast = curryN(2, (list, tokens) => {
if (length(tokens) == 0) {
return list
}
if (head(tokens) == '(') {
const x = ast([], tail(tokens))
return ast(append(nth(0, x), list), nth(1, x))
}
if (head(tokens) == ')') {
return [list, tail(tokens)]
}
return ast(append(head(tokens), list), tail(tokens))
})
const parse = pipe(tokenize, ast([]))
const evaluate = curryN(2, (env, x, defName) => {
if (!isArrayLike(x)) {
return env[x]
}
if (x[0] === 'def') {
const [, name, exp] = x
env[name] = evaluate(env, exp, name)
return
}
if (x[0] === '#') {
const [, params, body] = x
const lambda = (...args) => evaluate(merge(env, zipObj(params, args)), body)
lambda.expr = exprToString(x)
lambda.exprName = defName
return lambda
}
const [name, ...rest] = x
return apply(evaluate(env, name), map(evaluate(env), rest))
})
const wrap = curryN(3, (start, end, value) => start + value + end)
const exprToString = pipe(map((exp) => isArrayLike(exp) ? exprToString(exp) : exp), join(' '), wrap('(', ')'))
const la = pipe(parse, map(evaluate({})))
module.exports = {
tokenize,
ast,
parse,
evaluate,
exprToString,
la
}