Skip to content

Commit ed9e40b

Browse files
committed
Get case/when mostly working
1 parent d603358 commit ed9e40b

File tree

1 file changed

+66
-47
lines changed

1 file changed

+66
-47
lines changed

compiler.rb

Lines changed: 66 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -336,67 +336,84 @@ def compile_hash(scope, *args)
336336
compile_callm(scope, :Hash, :[], pairs)
337337
end
338338

339-
def compile_case(scope, *args)
340-
# error(":case not implemented yet", scope, [:case]+args)
341-
# FIXME:
342-
# Implement like this: compile_eval_arg
343-
# save the register, and loop over the "when"'s.
344-
# Compile each of the "when"'s as "if"'s where the value
345-
# is loaded from the stack and compared with the value
346-
# (or values) in the when clause
347-
348-
349-
# experimental (need to look into saving to register etc..):
350-
# but makes it compile all the way through for now...
351-
352-
@e.comment("compiling case expression")
353-
compare_exp = args.first
354-
355-
@e.comment("compare_exp: #{compare_exp}")
356-
357-
test = lambda do |test_exprs|
358-
if test_exprs.is_a?(Array) && test_exprs[0] == :comma
339+
# FIXME: Compiler @bug: This method was a self-recursive
340+
# lambda in `#compile_case`
341+
def compile_case_test(compare_exp, test_exprs)
342+
test_value = test_exprs
343+
xrest = []
344+
if test_exprs.is_a?(Array)
345+
#STDERR.puts test_exprs.inspect
346+
if test_exprs[0] == :comma
359347
test_value = test_exprs[1]
360-
rest = test_exprs[2]
361-
else
362-
test_value = test_exprs
363-
rest = []
348+
xrest = Array(test_exprs[2])
364349
end
365-
cmp = [:callm, test_value, :===, [compare_exp]]
350+
#STDERR.puts xrest.inspect
351+
end
352+
cmp = [:callm, test_value, :===, [compare_exp]]
366353

367-
rest.empty? ? cmp : [:or, cmp, test.call(rest)]
354+
if xrest.empty?
355+
cmp
356+
else
357+
[:or, cmp, compile_case_test(compare_exp, xrest)]
368358
end
359+
end
369360

370-
r = lambda do |whens|
371-
exp = whens.first
361+
# FIXME: This is unsafe. It only works for the compiler
362+
# for now because none of the case expressions in the
363+
# compiler itself have side effects.
364+
def compile_whens(compare_exp, whens)
365+
exp = whens.first
372366

373-
if exp[0] == :when
374-
test_values = exp[1]
367+
if exp[0] == :when
368+
test_values = exp[1]
375369

376-
body = exp[2] # body to be executed, if compare_exp === test_value
370+
body = exp[2] # body to be executed, if compare_exp === test_value
377371

378-
@e.comment("test_value: #{test_values.inspect}")
379-
@e.comment("body: #{body.inspect}")
372+
@e.comment("test_value: #{test_values.inspect}")
373+
@e.comment("body: #{body.inspect}")
380374

381-
rest = whens.slice(1..-1)
382-
if rest.empty?
383-
rest = [:do]
384-
else
385-
rest = r.call(rest)
386-
end
387-
[:do, [:if, test.call(test_values), [:do]+body, rest]]
375+
xrest = whens.slice(1..-1)
376+
if xrest.empty?
377+
xrest = [:do]
388378
else
389-
[:do]+exp
379+
xrest = compile_whens(compare_exp, xrest)
390380
end
381+
[:do, [:if, compile_case_test(compare_exp, test_values), [:do]+body, xrest]]
382+
else
383+
[:do]+exp
391384
end
385+
end
386+
387+
def compile_case(scope, *args)
388+
# FIXME: Compiler @bug:
389+
# The `xrest`'s below were `rest` but that causes `rest` in the
390+
# expression `arg.rest` to be misinterpreted during rewrite to
391+
# method call relative to the contents of the `rest` variable,
392+
# which needless to say is a total disaster.
393+
#
394+
# Further, there is likely another problem here, in that it looks like
395+
# a single, shared, environment is created for the two lambdas, but that
396+
# may be unavoidable given Ruby semantics.
392397

393-
rest = args.rest
394-
exprs = rest[0]
395-
if rest[1]
396-
exprs << rest[1]
398+
# FIXME:
399+
# Implement like this: compile_eval_arg
400+
# save the register, and loop over the "when"'s.
401+
# Compile each of the "when"'s as "if"'s where the value
402+
# is loaded from the stack and compared with the value
403+
# (or values) in the when clause
404+
405+
@e.comment("compiling case expression")
406+
compare_exp = args.first
407+
408+
@e.comment("compare_exp: #{compare_exp}")
409+
410+
xrest = args.rest
411+
exprs = xrest[0]
412+
if xrest[1]
413+
exprs << xrest[1]
397414
end
398415

399-
exprs = r.call(exprs)
416+
exprs = compile_whens(compare_exp, exprs)
400417
compile_eval_arg(scope, exprs)
401418

402419
return Value.new([:subexpr])
@@ -660,7 +677,9 @@ def compile_exp(scope, exp)
660677
pos = exp.position
661678
end
662679

663-
@e.lineno(pos) if pos
680+
if pos && exp[0] != :defm
681+
@e.lineno(pos) if pos
682+
end
664683
#trace(pos,exp)
665684

666685
# check if exp is within predefined keywords list

0 commit comments

Comments
 (0)