forked from geekskool/lisp-interpreter-js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ljs.js
116 lines (108 loc) · 3.68 KB
/
ljs.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
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
function tokenize (programString) {
return programString.replace(/\(/g,' ( ').replace(/\)/g,' ) ').trim().split(/\s+/)
}
function tokenHandler (token) {
var float
if (!isNaN (float = parseFloat(token))) return float
return token
}
function parse (tokens) {
token = tokens.shift()
if (token === '(') {
var expr = []
while (tokens[0] !== ')' && typeof tokens[0] !== 'undefined') {
expr.push(parse(tokens))
}
tokens.shift()
return expr
}
if (token == ')'){
throw new Error('Syntax Error')
}
return tokenHandler(token)
}
function Env (params, args, outer) {
this.dict = {}
this.outer = outer
this.find = function (name) {
if (name in this.dict) { return this.dict[name] }
return outer.find(name)
}
this.set = function (name, val) { this.dict[name] = val }
if (params && args) {
if (params instanceof Array) {
for (var i = 0; i < params.length; i++) this.set(params[i], parseInt(args[i]))
}
this.set (params, parseInt(args))
}
}
var global_env = {
'+': function (a, b) { return a+b },
'-': function (a, b) { if (b == null) return -a; return a - b },
'*': function (a, b) { return a * b },
'/': function (a, b) { return a / b },
'>': function (a, b) { return a > b },
'>=': function (a, b) { return a >= b },
'<': function (a, b) { return a < b },
'<=': function (a, b) { return a <= b },
'pi': function () { return 3.14 },
'pow': function (a, b) { return Math.pow(a,b) },
'length': function (a) { return a.length },
'abs': function (a) { return Math.abs(a) },
'append': function (a, b) { return String(a)+String(b) },
'eq?': function (a, b) { return a == b },
'equal?': function (a, b) { return a === b },
'car': function (a) { return a[0] },
'cdr': function (a) { return a[1] },
'cons': function (a, b) { a.push(b); return a },
'sqrt': function (a) { return Math.sqrt(a) },
'max': function (a) { return Math.max(a) },
'min': function (a) { return Math.min(a) },
'round': function (a) { return Math.round(a) },
'not': function (a) { return !a },
'number?': function (a) { return !isNaN(a) }
}
var env = new Env()
env.dict = global_env
function eval (x, env) {
if (typeof x === 'number') { return x }
if (typeof x === 'string') {
try
{ return env.find(x) }
catch (e) { return String(x) }
}
if (!x instanceof Array) { return x }
if (x[0] === 'if') { return eval(x[1], env) ? eval(x[2], env) : eval(x[3], env) }
if (x[0] === 'define') { return env.set(x[1], eval(x[2], env)) }
if (x[0] === 'lambda') { return function (args) { return eval(x[2], new Env(x[1], args, env)) }}
if (x[0] === 'set!') { env.find (x[1]).set(x[1], eval(x[2], env)) }
if (x[0] === 'quote') { return x[1] }
if (x[0] === 'begin') {
x.shift ()
var out = x.pop()
x.map (function (token) {
eval(token, env)
});
return eval(out, env)
}
var expr1 = typeof eval(x[1], env)
if (typeof x[2] === 'undefined') {
if (expr1 === 'function') return eval(x[0], env)(eval(x[1](), env))
return eval(x[0], env)(eval(x[1], env))
}
var expr2 = typeof eval(x[2], env)
if (expr1 === 'function' && expr2 === 'function') return eval(x[0], env)(eval(x[1], env)(), eval(x[2], env)())
if (expr1 === 'function') return eval(x[0], env)(eval(x[1], env)(), eval(x[2], env))
if (expr2 === 'function') return eval(x[0], env)(eval(x[1], env), eval(x[2], env)())
return eval(x[0], env)(eval(x[1], env), eval(x[2], env))
}
const readline = require('readline')
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
rl.on ('line', (input) => {
input = input.trim()
var solution = eval((parse(tokenize (input))), env)
if (typeof solution !== 'undefined') console.log(solution)
})