diff --git a/impls/ruby/step2_eval.rb b/impls/ruby/step2_eval.rb index 66376f2ce5..95a2fedd6a 100644 --- a/impls/ruby/step2_eval.rb +++ b/impls/ruby/step2_eval.rb @@ -13,28 +13,26 @@ def EVAL(ast, env) #puts "EVAL: #{_pr_str(ast, true)}" case ast - when Symbol + in Symbol raise "'" + ast.to_s + "' not found" if not env.key? ast return env[ast] - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - f = EVAL(ast[0], env) - args = ast.drop(1) - return f[*args.map{|a| EVAL(a, env)}] + in [a0, *] + f = EVAL(a0, env) + args = ast.drop(1) + return f[*args.map{|a| EVAL(a, env)}] + + else # Empty list or scalar + return ast + end end # print diff --git a/impls/ruby/step3_env.rb b/impls/ruby/step3_env.rb index 258e2e815d..69f80cdf80 100644 --- a/impls/ruby/step3_env.rb +++ b/impls/ruby/step3_env.rb @@ -16,38 +16,32 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end return EVAL(a2, let_env) - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) return f[*args.map{|a| EVAL(a, env)}] + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/step4_if_fn_do.rb b/impls/ruby/step4_if_fn_do.rb index d0e8983bd7..af8e7c5666 100644 --- a/impls/ruby/step4_if_fn_do.rb +++ b/impls/ruby/step4_if_fn_do.rb @@ -17,53 +17,46 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end return EVAL(a2, let_env) - when :do + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} - return EVAL(ast[-1], env) - when :if + return EVAL(ast.last, env) + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - return EVAL(a3, env) - else + if cond return EVAL(a2, env) + else + return EVAL(ast[3], env) end - when :"fn*" + in :"fn*", a1, a2 return lambda {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) return f[*args.map{|a| EVAL(a, env)}] + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/step5_tco.rb b/impls/ruby/step5_tco.rb index f149c4f55f..32114c151e 100644 --- a/impls/ruby/step5_tco.rb +++ b/impls/ruby/step5_tco.rb @@ -19,51 +19,41 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end env = let_env ast = a2 # Continue loop (TCO) - when :do + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} ast = ast.last # Continue loop (TCO) - when :if + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - ast = a3 # Continue loop (TCO) - else + if cond ast = a2 # Continue loop (TCO) + else + ast = ast[3] # Continue loop (TCO) end - when :"fn*" + in :"fn*", a1, a2 return Function.new(a2, env, a1) {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) if f.class == Function @@ -73,6 +63,9 @@ def EVAL(ast, env) else return f[*args.map{|a| EVAL(a, env)}] end + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/step6_file.rb b/impls/ruby/step6_file.rb index c639926836..4bd5af33cf 100644 --- a/impls/ruby/step6_file.rb +++ b/impls/ruby/step6_file.rb @@ -19,51 +19,41 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end env = let_env ast = a2 # Continue loop (TCO) - when :do + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} ast = ast.last # Continue loop (TCO) - when :if + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - ast = a3 # Continue loop (TCO) - else + if cond ast = a2 # Continue loop (TCO) + else + ast = ast[3] # Continue loop (TCO) end - when :"fn*" + in :"fn*", a1, a2 return Function.new(a2, env, a1) {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) if f.class == Function @@ -73,6 +63,9 @@ def EVAL(ast, env) else return f[*args.map{|a| EVAL(a, env)}] end + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/step7_quote.rb b/impls/ruby/step7_quote.rb index a9a4e33ca5..95246b8902 100644 --- a/impls/ruby/step7_quote.rb +++ b/impls/ruby/step7_quote.rb @@ -14,8 +14,8 @@ def READ(str) def qq_loop(ast) acc = List.new [] ast.reverse_each do |elt| - if elt.is_a?(List) && elt.size == 2 && elt[0] == :"splice-unquote" - acc = List.new [:concat, elt[1], acc] + if elt in List and elt in :"splice-unquote", quoted + acc = List.new [:concat, quoted, acc] else acc = List.new [:cons, quasiquote(elt), acc] end @@ -26,16 +26,14 @@ def qq_loop(ast) def quasiquote(ast) return case ast when List - if ast.size == 2 && ast[0] == :unquote - ast[1] + if ast in :unquote, quoted + quoted else qq_loop(ast) end when Vector List.new [:vec, qq_loop(ast)] - when Hash - List.new [:quote, ast] - when Symbol + when Hash, Symbol List.new [:quote, ast] else ast @@ -50,55 +48,45 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end env = let_env ast = a2 # Continue loop (TCO) - when :quote + in :quote, a1 return a1 - when :quasiquote + in :quasiquote, a1 ast = quasiquote(a1); # Continue loop (TCO) - when :do + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} ast = ast.last # Continue loop (TCO) - when :if + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - ast = a3 # Continue loop (TCO) - else + if cond ast = a2 # Continue loop (TCO) + else + ast = ast[3] # Continue loop (TCO) end - when :"fn*" + in :"fn*", a1, a2 return Function.new(a2, env, a1) {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) if f.class == Function @@ -108,6 +96,9 @@ def EVAL(ast, env) else return f[*args.map{|a| EVAL(a, env)}] end + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/step8_macros.rb b/impls/ruby/step8_macros.rb index 01150de4c2..a25eec2872 100644 --- a/impls/ruby/step8_macros.rb +++ b/impls/ruby/step8_macros.rb @@ -14,8 +14,8 @@ def READ(str) def qq_loop(ast) acc = List.new [] ast.reverse_each do |elt| - if elt.is_a?(List) && elt.size == 2 && elt[0] == :"splice-unquote" - acc = List.new [:concat, elt[1], acc] + if elt in List and elt in :"splice-unquote", quoted + acc = List.new [:concat, quoted, acc] else acc = List.new [:cons, quasiquote(elt), acc] end @@ -26,16 +26,14 @@ def qq_loop(ast) def quasiquote(ast) return case ast when List - if ast.size == 2 && ast[0] == :unquote - ast[1] + if ast in :unquote, quoted + quoted else qq_loop(ast) end when Vector List.new [:vec, qq_loop(ast)] - when Hash - List.new [:quote, ast] - when Symbol + when Hash, Symbol List.new [:quote, ast] else ast @@ -50,59 +48,49 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end env = let_env ast = a2 # Continue loop (TCO) - when :quote + in :quote, a1 return a1 - when :quasiquote + in :quasiquote, a1 ast = quasiquote(a1); # Continue loop (TCO) - when :defmacro! + in :defmacro!, a1, a2 func = EVAL(a2, env).clone func.is_macro = true return env.set(a1, func) - when :do + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} ast = ast.last # Continue loop (TCO) - when :if + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - ast = a3 # Continue loop (TCO) - else + if cond ast = a2 # Continue loop (TCO) + else + ast = ast[3] # Continue loop (TCO) end - when :"fn*" + in :"fn*", a1, a2 return Function.new(a2, env, a1) {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) if f.class == Function @@ -116,6 +104,9 @@ def EVAL(ast, env) else return f[*args.map{|a| EVAL(a, env)}] end + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/step9_try.rb b/impls/ruby/step9_try.rb index 84357f46cf..901d2db1d1 100644 --- a/impls/ruby/step9_try.rb +++ b/impls/ruby/step9_try.rb @@ -14,8 +14,8 @@ def READ(str) def qq_loop(ast) acc = List.new [] ast.reverse_each do |elt| - if elt.is_a?(List) && elt.size == 2 && elt[0] == :"splice-unquote" - acc = List.new [:concat, elt[1], acc] + if elt in List and elt in :"splice-unquote", quoted + acc = List.new [:concat, quoted, acc] else acc = List.new [:cons, quasiquote(elt), acc] end @@ -26,16 +26,14 @@ def qq_loop(ast) def quasiquote(ast) return case ast when List - if ast.size == 2 && ast[0] == :unquote - ast[1] + if ast in :unquote, quoted + quoted else qq_loop(ast) end when Vector List.new [:vec, qq_loop(ast)] - when Hash - List.new [:quote, ast] - when Symbol + when Hash, Symbol List.new [:quote, ast] else ast @@ -50,44 +48,35 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end env = let_env ast = a2 # Continue loop (TCO) - when :quote + in :quote, a1 return a1 - when :quasiquote + in :quasiquote, a1 ast = quasiquote(a1); # Continue loop (TCO) - when :defmacro! + in :defmacro!, a1, a2 func = EVAL(a2, env).clone func.is_macro = true return env.set(a1, func) - when :"try*" + in [:"try*", a1, [:"catch*", key, handler]] begin return EVAL(a1, env) rescue Exception => exc @@ -96,28 +85,26 @@ def EVAL(ast, env) else exc = exc.message end - if a2 && a2[0] == :"catch*" - return EVAL(a2[2], Env.new(env, [a2[1]], [exc])) - else - raise exc - end + ast = handler + env = Env.new(env, [key], [exc]) # Continue loop (TCO) end - when :do + in [:"try*", a1] + ast = a1 # Continue loop (TCO) + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} ast = ast.last # Continue loop (TCO) - when :if + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - ast = a3 # Continue loop (TCO) - else + if cond ast = a2 # Continue loop (TCO) + else + ast = ast[3] # Continue loop (TCO) end - when :"fn*" + in :"fn*", a1, a2 return Function.new(a2, env, a1) {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) if f.class == Function @@ -131,6 +118,9 @@ def EVAL(ast, env) else return f[*args.map{|a| EVAL(a, env)}] end + + else # Empty list or scalar + return ast end end diff --git a/impls/ruby/stepA_mal.rb b/impls/ruby/stepA_mal.rb index 787fb578f0..6113ee3a65 100644 --- a/impls/ruby/stepA_mal.rb +++ b/impls/ruby/stepA_mal.rb @@ -14,8 +14,8 @@ def READ(str) def qq_loop(ast) acc = List.new [] ast.reverse_each do |elt| - if elt.is_a?(List) && elt.size == 2 && elt[0] == :"splice-unquote" - acc = List.new [:concat, elt[1], acc] + if elt in List and elt in :"splice-unquote", quoted + acc = List.new [:concat, quoted, acc] else acc = List.new [:cons, quasiquote(elt), acc] end @@ -26,16 +26,14 @@ def qq_loop(ast) def quasiquote(ast) return case ast when List - if ast.size == 2 && ast[0] == :unquote - ast[1] + if ast in :unquote, quoted + quoted else qq_loop(ast) end when Vector List.new [:vec, qq_loop(ast)] - when Hash - List.new [:quote, ast] - when Symbol + when Hash, Symbol List.new [:quote, ast] else ast @@ -50,50 +48,41 @@ def EVAL(ast, env) end case ast - when Symbol + in Symbol return env.get(ast) - when List - when Vector + in Vector return Vector.new ast.map{|a| EVAL(a, env)} - when Hash + in Hash new_hm = {} ast.each{|k,v| new_hm[k] = EVAL(v, env)} return new_hm - else - return ast - end # apply list - if ast.empty? - return ast - end - a0,a1,a2,a3 = ast - case a0 - when :def! + in :def!, a1, a2 return env.set(a1, EVAL(a2, env)) - when :"let*" + in :"let*", a1, a2 let_env = Env.new(env) a1.each_slice(2) do |a,e| let_env.set(a, EVAL(e, let_env)) end env = let_env ast = a2 # Continue loop (TCO) - when :quote + in :quote, a1 return a1 - when :quasiquote + in :quasiquote, a1 ast = quasiquote(a1); # Continue loop (TCO) - when :defmacro! + in :defmacro!, a1, a2 func = EVAL(a2, env).clone func.is_macro = true return env.set(a1, func) - when :"rb*" + in :"rb*", a1 res = eval(a1) return case res when Array; List.new res else; res end - when :"try*" + in [:"try*", a1, [:"catch*", key, handler]] begin return EVAL(a1, env) rescue Exception => exc @@ -102,28 +91,26 @@ def EVAL(ast, env) else exc = exc.message end - if a2 && a2[0] == :"catch*" - return EVAL(a2[2], Env.new(env, [a2[1]], [exc])) - else - raise exc - end + ast = handler + env = Env.new(env, [key], [exc]) # Continue loop (TCO) end - when :do + in [:"try*", a1] + ast = a1 # Continue loop (TCO) + in [:do, *] ast[1..-2].map{|a| EVAL(a, env)} ast = ast.last # Continue loop (TCO) - when :if + in [:if, a1, a2, *] cond = EVAL(a1, env) - if not cond - return nil if a3 == nil - ast = a3 # Continue loop (TCO) - else + if cond ast = a2 # Continue loop (TCO) + else + ast = ast[3] # Continue loop (TCO) end - when :"fn*" + in :"fn*", a1, a2 return Function.new(a2, env, a1) {|*args| EVAL(a2, Env.new(env, a1, List.new(args))) } - else + in [a0, *] f = EVAL(a0, env) args = ast.drop(1) if f.class == Function @@ -137,6 +124,9 @@ def EVAL(ast, env) else return f[*args.map{|a| EVAL(a, env)}] end + + else # Empty list or scalar + return ast end end