-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlittle_lisp.rb
111 lines (100 loc) · 2.69 KB
/
little_lisp.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
class Context
def initialize(scope, parent = nil)
@scope = scope
@parent = parent
end
def get identifier
if @scope[identifier]
@scope[identifier]
elsif @parent
@parent.get identifier
end
end
end
class LittleLisp
def interpret input
_interpret(parenthesize(tokenize(input)))
end
def _interpret(input, context = nil)
if context === nil
_interpret(input, Context.new({}))
elsif input.is_a?(Array)
_interpretList(input, context);
elsif input[:type] === "identifier"
context.get(input[:value])
else
input[:value]
end
end
def _interpretList(input, context)
if input[0] && input[0].is_a?(Hash) && SPECIAL[input[0][:value]]
# build a Proc for the lambda or invoke an operator on input parameters
SPECIAL[input[0][:value]].call(self, input, context)
else
list = input.map{|x| _interpret(x, context) }
if list[0].is_a?(Proc)
list[0].call(list[1..-1])
else
list
end
end
end
private
def tokenize input
input.gsub(/\(/, ' ( ').gsub(/\)/, ' ) ').strip.split(/\s+/)
end
def parenthesize(input, list = [])
token = input.shift
if token == nil
list.pop
elsif token === "("
list.push(parenthesize(input, []))
parenthesize(input, list)
elsif token === ")"
list
else
parenthesize(input, list.push(categorize(token)) )
end
end
def categorize input
{ type: 'literal', value: Float(input) }
rescue ArgumentError
if input[0] === '"' && input.slice(-1) === '"'
{ type: 'literal', value: input[1...-1] }
else
{ type:'identifier', value: input }
end
end
SPECIAL = {
'lambda' => proc do |this, input, context|
proc do |*args|
lambdaArguments = args.flatten
lambdaScope = {}
input[1].to_enum.with_index.each do |x, i|
lambdaScope[x[:value]] = lambdaArguments[i]
end
this._interpret(input[2], Context.new(lambdaScope, context))
end
end,
'>' => proc do |this, input, context|
this._interpret(input[1], context) > this._interpret(input[2], context)
end,
'<' => proc do |this, input, context|
this._interpret(input[1], context) < this._interpret(input[2], context)
end,
'if' => proc do |this, input, context|
if this._interpret(input[1], context)
this._interpret(input[2], context)
else
this._interpret(input[3], context)
end
end
}
['+', '*'].each do |operator|
SPECIAL.merge!({operator => proc do |this, input, context|
input[1..-1].inject([]) do |sum, x|
sum << this._interpret(x, context)
end.inject(operator)
end})
end
end