-
Notifications
You must be signed in to change notification settings - Fork 11
/
purelisp.rb
executable file
·146 lines (137 loc) · 3.71 KB
/
purelisp.rb
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
#!/usr/bin/env ruby
$vals = {}
def parse_sexpr(s, orig)
case s.strip
when /^\(/
s = $'
r = []
while s !~ /^\)/
raise "invalid sexpr: #{orig}" if s.empty?
x, s = parse_sexpr(s, orig)
r << x
s.lstrip!
end
[r, $']
when /^-?\d+/
[$&.to_i, $']
when /^t\b/
[:t, $']
when /^nil\b/
[[], $']
when /^([^() ]+)/
[$1, $']
end
end
def atom?(sexpr)
!sexpr.is_a?(Array) || sexpr == []
end
def eval_sexpr(sexpr, vals)
if atom?(sexpr)
return vals[sexpr] || $vals[sexpr] || sexpr
end
op, *args = sexpr
case op
when 'if'
raise "invalid if: #{stringify_sexpr(sexpr)}" if args.size != 3
cond = eval_sexpr(args[0], vals)
result = cond != [] ? eval_sexpr(args[1], vals) : eval_sexpr(args[2], vals)
return result
when 'defun'
if (args.size != 3 || !args[0].is_a?(String) || !args[1].is_a?(Array) ||
!args[1].all?{|a|a.is_a?(String)})
raise "invalid defun: #{stringify_sexpr(sexpr)}"
end
$vals[args[0]] = {:args => args[1], :expr => args[2]}
return args[0]
when 'lambda'
if (args.size != 2 ||
!args[0].is_a?(Array) || !args[0].all?{|a|a.is_a?(String)})
raise "invalid lambda: #{stringify_sexpr(sexpr)}"
end
return {:args => args[0], :expr => args[1]}
when 'define'
if args.size != 2 || !args[0].is_a?(String)
raise "invalid define: #{stringify_sexpr(sexpr)}"
end
$vals[args[0]] = eval_sexpr(args[1], vals)
return []
when 'quote'
raise "invalid quote: #{stringify_sexpr(sexpr)}" if args.size != 1
return args[0]
end
op = eval_sexpr(op, vals)
args = args.map{|a|eval_sexpr(a, vals)}
case op
when Hash
if op[:args].size != args.size
raise "invalid number of args: #{stringify_sexpr(sexpr)}"
end
vals = {}
op[:args].zip(args){|k, v|vals[k] = v}
eval_sexpr(op[:expr], vals)
when '+'
raise "invalid add: #{stringify_sexpr(sexpr)}" if args.size != 2
args[0] + args[1]
when '-'
raise "invalid sub: #{stringify_sexpr(sexpr)}" if args.size != 2
args[0] - args[1]
when '*'
raise "invalid mul: #{stringify_sexpr(sexpr)}" if args.size != 2
args[0] * args[1]
when '/'
raise "invalid div: #{stringify_sexpr(sexpr)}" if args.size != 2
args[0] / args[1]
when 'mod'
raise "invalid mod: #{stringify_sexpr(sexpr)}" if args.size != 2
args[0] % args[1]
when 'eq'
raise "invalid eq: #{stringify_sexpr(sexpr)}" if args.size != 2
args[0] == args[1] ? :t : []
when 'car'
if args.size != 1 || !args[0].is_a?(Array)
raise "invalid car: #{stringify_sexpr(sexpr)}"
end
args[0][0] || []
when 'cdr'
if args.size != 1 || !args[0].is_a?(Array)
raise "invalid cdr: #{stringify_sexpr(sexpr)}"
end
args[0][1..-1] || []
when 'cons'
if args.size != 2 || !args[1].is_a?(Array)
raise "invalid cons: #{stringify_sexpr(sexpr)}"
end
[args[0]] + args[1]
when 'atom'
raise "invalid atom: #{stringify_sexpr(sexpr)}" if args.size != 1
atom?(args[0]) ? :t : []
when 'neg?'
raise "invalid neg?: #{stringify_sexpr(sexpr)}" if args.size != 1
args[0] < 0 ? :t : []
when 'print'
raise "invalid print: #{stringify_sexpr(sexpr)}" if args.size != 1
puts "PRINT: #{stringify_sexpr(args[0])}"
args[0]
else
raise "undefined function: #{op}"
end
end
def stringify_sexpr(sexpr)
if sexpr == []
'nil'
elsif sexpr == :t
't'
elsif sexpr.is_a?(Array)
'(' + sexpr.map{|s|stringify_sexpr(s)} * ' ' + ')'
else
sexpr.to_s
end
end
$<.each do |line|
line.sub!(/;.*/, '')
next if line =~ /^$/
line.gsub!(/\s+/, ' ')
sexpr, rest = parse_sexpr(line, line)
raise "invalid sexpr: #{line}" if !rest.empty?
puts stringify_sexpr(eval_sexpr(sexpr, {}))
end