-
Notifications
You must be signed in to change notification settings - Fork 0
/
LispRuby.rb
152 lines (121 loc) · 4.25 KB
/
LispRuby.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
147
148
149
150
151
152
#take input from user
#add spaces in ) and( with gsub
#split them into tokens of individual units
#make syntax tree out of it using norvig's read_from_tokens recursive method
#write a function to convert each element to int, float and symbol using regexp
#check the parser
########## Parsing completes here ##########
#create environment to process the eval
#write eval function to calculate mathematical operation
#upadate the car, cdr, cons
#update with null? max min eq?
#update with lambda
class LispRuby
def initialize
@environment = {
:+ => lambda{|*list| list.inject{|sum,x| sum + x }},
:== => lambda{|x, y| x == y},
:!= => lambda{|x, y| x != y},
:< => lambda{|x, y| x < y},
:<= => lambda{|x, y| x <= y},
:> => lambda{|x, y| x > y},
:>= => lambda{|x, y| x >= y},
:* => lambda{|*list| list.inject(1){|prod, x| prod * x}},
:/ => lambda{|x, y| x / y},
:- => lambda{|x, y| x - y},
:list => lambda{|*list| Array(list)},
:eq? => lambda{|x, y| x == y},
:min => lambda{|list| list.min},
:max => lambda{|list| list.max},
:sqrt => lambda{|x| Math.sqrt(x)},
:pow => lambda{|x, y| x**y},
:car => lambda{|list|},
:cdr => lambda{|list|},
:cons => lambda{|e, list|},
}
end
################# USER INPUT ##################
def user_input
while true
print "LispRuby >> "
lisp_command = gets.chomp
break if lisp_command.downcase == "exit"
result = run_the lisp_command
puts (result.inspect) unless result.nil?
end
end
################# PARSING STARTS ################
def run_the lisp
eval make_syntax seperate lisp
end
def seperate input_from_user
raise SyntaxError, "Empty input" if input_from_user.empty?
raise SyntaxError, "Unexpected ')' or '(" if ((input_from_user.count '(') != (input_from_user.count ')'))
input_from_user.gsub('(', ' ( ').gsub(')', ' ) ').split(" ")
end
def make_syntax tokens
token = tokens.shift
if '(' == token
list = []
while tokens.first != ')'
list << make_syntax(tokens)
end
tokens.shift
list
elsif ')' == token
raise 'Wrong Syntax'
else
convert_int_float_sym token
end
end
def convert_int_float_sym token
if token[/\.\d+/]
token.to_f
elsif token[/\d+/]
token.to_i
else
token.to_sym
end
end
################# PARSING COMPLETED ##################
################# EVALUATION STARTS ##################
def eval (exp, env = @environment)
if exp.is_a? Numeric
exp
elsif exp.is_a? Symbol
env[exp]
elsif exp.first == :quote
exp[1..-1]
elsif exp.first == :if
_, condition, yes, no = exp
exp = eval(condition, env) ? yes : no
eval(exp, env)
elsif exp.first == :define
_, var, e = exp
env[var] = eval(e, env)
elsif exp.first == :lambda
_, params, e = exp
lambda { |*args| eval(e, env.merge(Hash[params.zip(args)])) }
else
car_cdr_cons(exp) if exp.first == :car || :cdr || :cons
proc = eval(exp[0], env)
args = exp[1..-1].map{ |arg| eval(arg, env) }
proc.(*args)
end
end
def car_cdr_cons(expression)
start = expression.first
case start
when :car
puts expression.flatten[2].inspect
when :cdr
puts expression.flatten[3..-1].inspect
when :cons
puts (expression.flatten[3..-1] << expression.flatten[1]).inspect
end
end
end
################# EVALUATION COMPLETED ##############
################# INVOKING PROGRAM ##################
invoke = LispRuby.new
invoke.user_input