@@ -11,6 +11,7 @@ def initialize(params, args, outer_environment = nil)
1111 @env = { params => args }
1212 else
1313 raise "expected #{ params . length } arguments, got #{ args . length } " unless params . length == args . length
14+
1415 @env = params . zip ( args ) . to_h
1516 end
1617 end
@@ -68,80 +69,78 @@ def call(*args)
6869 :length => :length . to_proc ,
6970 :not => proc { |a | !a } ,
7071 :null? => proc { |a | a . nil? || a . empty? } ,
71- :pair? => proc { |x | x . is_a? ( Array ) && !x . empty? } ,
72+ :pair? => proc { |tokens | tokens . is_a? ( Array ) && !tokens . empty? } ,
7273 :sqrt => proc { |a | Math . sqrt ( a ) }
7374 } . entries . transpose
7475 GLOBAL_ENV = Environment . new ( GLOBAL_DICT [ 0 ] , GLOBAL_DICT [ 1 ] )
7576
7677 # Helper methods for expand
77- def is_pair ( x )
78- x . is_a? ( Array ) && !x . empty?
78+ def pair? ( tokens )
79+ tokens . is_a? ( Array ) && !tokens . empty?
7980 end
8081
81- def to_string ( x )
82- case x
82+ def to_string ( tokens )
83+ case tokens
8384 when true then "#t"
8485 when false then "#f"
85- when Symbol then x . to_s
86- when String then "\" #{ x . gsub ( '"' , '\"' ) } \" "
87- when Array then "(#{ x . map { |e | to_string ( e ) } . join ( " " ) } )"
88- else x . to_s
86+ when Symbol then tokens . to_s
87+ when String then "\" #{ tokens . gsub ( '"' , '\"' ) } \" "
88+ when Array then "(#{ tokens . map { |e | to_string ( e ) } . join ( " " ) } )"
89+ else tokens . to_s
8990 end
9091 end
9192
92- def require_syntax ( x , predicate , msg = "wrong length" )
93- raise "#{ to_string ( x ) } : #{ msg } " unless predicate
93+ def require_syntax ( tokens , predicate , msg = "wrong length" )
94+ raise "#{ to_string ( tokens ) } : #{ msg } " unless predicate
9495 end
9596
9697 # Main expand function
97- def expand ( x , toplevel = false )
98+ def expand ( tokens , toplevel : false )
9899 # Check for empty list
99- require_syntax ( x , !( x . is_a? ( Array ) && x . empty? ) )
100+ require_syntax ( tokens , !( tokens . is_a? ( Array ) && tokens . empty? ) )
100101
101102 # Constant/non-list => unchanged
102- return x unless x . is_a? ( Array )
103+ return tokens unless tokens . is_a? ( Array )
103104
104- case x . first
105+ case tokens . first
105106 when :include
106107 # (include string1 string2 ...)
107- require_syntax ( x , x . length > 1 )
108- expand_include ( x )
108+ require_syntax ( tokens , tokens . length > 1 )
109+ expand_include ( tokens )
109110 when :quote
110111 # (quote exp)
111- require_syntax ( x , x . length == 2 )
112- x
112+ require_syntax ( tokens , tokens . length == 2 )
113+ tokens
113114 when :if
114115 # (if test conseq) => (if test conseq nil)
115- if x . length == 3
116- x += [ nil ]
117- end
118- require_syntax ( x , x . length == 4 )
119- x . map { |xi | expand ( xi ) }
116+ tokens += [ nil ] if tokens . length == 3
117+ require_syntax ( tokens , tokens . length == 4 )
118+ tokens . map { |token | expand ( token ) }
120119 when :set
121120 # (set! var exp)
122- require_syntax ( x , x . length == 3 )
123- var = x [ 1 ]
124- require_syntax ( x , var . is_a? ( Symbol ) , "can set! only a symbol" )
125- [ :set! , var , expand ( x [ 2 ] ) ]
121+ require_syntax ( tokens , tokens . length == 3 )
122+ var = tokens [ 1 ]
123+ require_syntax ( tokens , var . is_a? ( Symbol ) , "can set! only a symbol" )
124+ [ :set! , var , expand ( tokens [ 2 ] ) ]
126125 when :define , :"define-macro"
127126 # Check correct length
128- require_syntax ( x , x . length >= 3 )
127+ require_syntax ( tokens , tokens . length >= 3 )
129128
130- token , v , body = x [ 0 ] , x [ 1 ] , x [ 2 ..]
129+ token , v , body = tokens [ 0 ] , tokens [ 1 ] , tokens [ 2 ..]
131130
132131 if v . is_a? ( Array ) && !v . empty?
133132 # (define (f args) body) => (define f (lambda (args) body))
134133 f , *args = v
135134 expand ( [ token , f , [ :lambda , args ] + body ] )
136135 else
137- require_syntax ( x , x . length == 3 , "wrong length in definition" )
138- require_syntax ( x , v . is_a? ( Symbol ) , "can define only a symbol" )
139- exp = expand ( x [ 2 ] )
136+ require_syntax ( tokens , tokens . length == 3 , "wrong length in definition" )
137+ require_syntax ( tokens , v . is_a? ( Symbol ) , "can define only a symbol" )
138+ exp = expand ( tokens [ 2 ] )
140139
141140 if token == :"define-macro"
142- require_syntax ( x , toplevel , "define-macro only allowed at top level" )
141+ require_syntax ( tokens , toplevel , "define-macro only allowed at top level" )
143142 proc = evaluate ( exp )
144- require_syntax ( x , proc . respond_to? ( :call ) , "macro must be a procedure" )
143+ require_syntax ( tokens , proc . respond_to? ( :call ) , "macro must be a procedure" )
145144 MACRO_TABLE [ v ] = proc
146145 return nil
147146 end
@@ -150,84 +149,80 @@ def expand(x, toplevel = false)
150149 end
151150 when :begin
152151 # (begin exp*)
153- return nil if x . length == 1
154- x . map { |xi | expand ( xi , toplevel ) }
152+ return nil if tokens . length == 1
153+
154+ tokens . map { |token | expand ( token , toplevel : toplevel ) }
155155 when :lambda
156156 # (lambda (vars) exp1 exp2...)
157- require_syntax ( x , x . length >= 3 )
157+ require_syntax ( tokens , tokens . length >= 3 )
158158
159- vars , *body = x [ 1 ..]
159+ vars , *body = tokens [ 1 ..]
160160
161161 # Check that vars is a symbol or list of symbols
162162 is_valid_vars = vars . is_a? ( Symbol ) ||
163163 ( vars . is_a? ( Array ) && vars . all? { |v | v . is_a? ( Symbol ) } )
164- require_syntax ( x , is_valid_vars , "illegal lambda argument list" )
164+ require_syntax ( tokens , is_valid_vars , "illegal lambda argument list" )
165165
166166 # Wrap multiple expressions in begin
167167 exp = ( body . length == 1 ) ? body [ 0 ] : [ :begin ] + body
168168 [ :lambda , vars , expand ( exp ) ]
169169 when :quasiquote
170170 # `x => expand_quasiquote(x)
171- require_syntax ( x , x . length == 2 )
172- expand_quasiquote ( x [ 1 ] )
171+ require_syntax ( tokens , tokens . length == 2 )
172+ expand_quasiquote ( tokens [ 1 ] )
173173 when :cond
174174 # (cond (test exp) ...)
175- expanded_clauses = x [ 1 ..] . map do |clause |
176- require_syntax ( x , clause . is_a? ( Array ) && clause . length == 2 ,
175+ expanded_clauses = tokens [ 1 ..] . map do |clause |
176+ require_syntax ( tokens , clause . is_a? ( Array ) && clause . length == 2 ,
177177 "Invalid cond clause format" )
178178 [ expand ( clause [ 0 ] ) , expand ( clause [ 1 ] ) ]
179179 end
180180 [ :cond ] + expanded_clauses
181181 else
182182 # Check for macro expansion
183- if x . first . is_a? ( Symbol ) && MACRO_TABLE . key? ( x . first )
183+ if tokens . first . is_a? ( Symbol ) && MACRO_TABLE . key? ( tokens . first )
184184 # (m arg...) => macroexpand if m is a macro
185- expand ( MACRO_TABLE [ x . first ] . call ( *x [ 1 ..] ) , toplevel )
185+ expand ( MACRO_TABLE [ tokens . first ] . call ( *tokens [ 1 ..] ) , toplevel : toplevel )
186186 else
187187 # (f arg...) => expand each
188- x . map { |xi | expand ( xi ) }
188+ tokens . map { |token | expand ( token ) }
189189 end
190190 end
191191 end
192192
193193 # Expand quasiquote expression
194- def expand_quasiquote ( x )
194+ def expand_quasiquote ( tokens )
195195 # 'x => 'x
196- unless is_pair ( x )
197- return [ :quote , x ]
198- end
196+ return [ :quote , tokens ] unless pair? ( tokens )
199197
200198 # Check for invalid splicing
201- require_syntax ( x , x [ 0 ] != :"unquote-splicing" , "can't splice here" )
199+ require_syntax ( tokens , tokens [ 0 ] != :"unquote-splicing" , "can't splice here" )
202200
203- if x [ 0 ] == :unquote
201+ if tokens [ 0 ] == :unquote
204202 # ,x => x
205- require_syntax ( x , x . length == 2 )
206- x [ 1 ]
207- elsif is_pair ( x [ 0 ] ) && x [ 0 ] [ 0 ] == :"unquote-splicing"
203+ require_syntax ( tokens , tokens . length == 2 )
204+ tokens [ 1 ]
205+ elsif pair? ( tokens [ 0 ] ) && tokens [ 0 ] [ 0 ] == :"unquote-splicing"
208206 # (,@x y) => (append x y)
209- require_syntax ( x [ 0 ] , x [ 0 ] . length == 2 )
210- [ :append , x [ 0 ] [ 1 ] , expand_quasiquote ( x [ 1 ..] ) ]
207+ require_syntax ( tokens [ 0 ] , tokens [ 0 ] . length == 2 )
208+ [ :append , tokens [ 0 ] [ 1 ] , expand_quasiquote ( tokens [ 1 ..] ) ]
211209 else
212210 # `(x . y) => (cons `x `y)
213- [ :cons , expand_quasiquote ( x [ 0 ] ) , expand_quasiquote ( x [ 1 ..] ) ]
211+ [ :cons , expand_quasiquote ( tokens [ 0 ] ) , expand_quasiquote ( tokens [ 1 ..] ) ]
214212 end
215213 end
216214
217215 # Expand include directive
218- def expand_include ( x )
216+ def expand_include ( tokens )
219217 result = [ :begin ]
220218
221- x [ 1 ..] . each do |file_name |
219+ tokens [ 1 ..] . each do |file_name |
222220 File . open ( file_name , "r" ) do |include_file |
223221 content = include_file . read
224222 include_result = Parser . parse_string ( content )
223+ raise "Could not include content of #{ file_name } " unless include_result
225224
226- if include_result
227- result << expand ( include_result , true )
228- else
229- raise SchemeException , "Could not include content of #{ file_name } "
230- end
225+ result << expand ( include_result , true )
231226 end
232227 end
233228
@@ -270,6 +265,7 @@ def evaluate(tokens, environment = GLOBAL_ENV)
270265 case tokens . first
271266 when :if
272267 raise "`if` expected 3 arguments, got #{ tokens . length - 1 } " unless tokens . length == 4
268+
273269 condition = evaluate ( tokens [ 1 ] , environment )
274270 tokens = condition ? tokens [ 2 ] : tokens [ 3 ]
275271
@@ -283,11 +279,15 @@ def evaluate(tokens, environment = GLOBAL_ENV)
283279
284280 when :quote
285281 raise "`quote` expected 1 argument, got #{ tokens . length - 1 } " unless tokens . length == 2
282+
286283 return tokens [ 1 ]
287284
288285 when :lambda
289286 raise "`lambda` expected 2 argument, got #{ tokens . length - 1 } " unless tokens . length == 3
290- raise "invalid argument list (expected symbol or list of symbols)" if !tokens [ 1 ] . is_a? ( Symbol ) && !( tokens [ 1 ] . is_a? ( Array ) && tokens [ 1 ] . all? { |t | t . is_a? ( Symbol ) } )
287+ if !tokens [ 1 ] . is_a? ( Symbol ) && !( tokens [ 1 ] . is_a? ( Array ) && tokens [ 1 ] . all? { |t | t . is_a? ( Symbol ) } )
288+ raise "invalid argument list (expected symbol or list of symbols)"
289+ end
290+
291291 return Procedure . new ( tokens [ 1 ] , tokens [ 2 ] , environment )
292292
293293 when :begin
@@ -303,7 +303,7 @@ def evaluate(tokens, environment = GLOBAL_ENV)
303303 return
304304
305305 when :cond
306- branch = tokens [ 1 ..] . find do |( test , expression ) |
306+ branch = tokens [ 1 ..] . find do |( test , _ ) |
307307 test == :else || evaluate ( test , environment )
308308 end
309309 return if branch . nil?
@@ -329,10 +329,10 @@ def evaluate(tokens, environment = GLOBAL_ENV)
329329 end
330330
331331 def evaluate_string ( source )
332- Scheme . evaluate ( Scheme . expand ( Parser . parse_string ( source ) , true ) )
332+ Scheme . evaluate ( Scheme . expand ( Parser . parse_string ( source ) , toplevel : true ) )
333333 end
334334
335- module_function :evaluate_string , :evaluate , :expand , :is_pair , :require_syntax ,
335+ module_function :evaluate_string , :evaluate , :expand , :pair? , :require_syntax ,
336336 :expand_quasiquote , :expand_include , :let_macro , :to_string
337337
338338 # Get current directory
@@ -341,8 +341,9 @@ def evaluate_string(source)
341341 begin
342342 stdlib = File . join ( dirname , "../ports/stdlib.scm" )
343343 evaluate_string ( File . read ( stdlib ) )
344- rescue => error
345- puts "Error loading standard library: #{ error . message } "
344+ rescue => e
345+ puts e . backtrace
346+ puts "Error loading standard library: #{ e . message } "
346347 exit ( 1 )
347348 end
348349end
0 commit comments