Fixing ruby send with arguments

When send has complex args, mostly more sends, we hoist those out and pass created temporary variables
This commit is contained in:
Torsten Rüger 2019-08-15 21:30:36 +03:00
parent 31ae0a9670
commit 84b9811e55
8 changed files with 124 additions and 12 deletions

View File

@ -17,6 +17,7 @@ module Ruby
@arguments ||= [] @arguments ||= []
end end
# we "normalize" or flatten any complex argument expressions into a list
def to_vool def to_vool
statements = Vool::Statements.new([]) statements = Vool::Statements.new([])
arguments = [] arguments = []
@ -31,12 +32,23 @@ module Ruby
end end
end end
# this is called for each arg and if the arg is not constant or Variable
# we create a tmp variable and assign to that, hoising all the calls.
# the effect is of walking the call tree now,
# rather than using a stack to do that at runtime
def normalize_arg(arg , arguments , statements) def normalize_arg(arg , arguments , statements)
if arg.is_a?(Constant) and !arg.is_a?(CallStatement) vool_arg = arg.to_vool
arguments << arg.to_vool if arg.is_a?(Constant)
arguments << vool_arg
return return
end end
assign = Vool::LocalAssignment.new( "tmp_#{arg.object_id}".to_sym, arg.to_vool) if( vool_arg.is_a?(Vool::Statements))
while(vool_arg.length > 1)
statements << vool_arg.shift
end
vool_arg = vool_arg.shift
end
assign = Vool::LocalAssignment.new( "tmp_#{arg.object_id}".to_sym, vool_arg)
statements << assign statements << assign
arguments << Vool::LocalVariable.new(assign.name) arguments << Vool::LocalVariable.new(assign.name)
end end

View File

@ -20,13 +20,16 @@ module Ruby
def length def length
@statements.length @statements.length
end end
def shift
@statements.shift
end
def [](i) def [](i)
@statements[i] @statements[i]
end end
def <<(o) def <<(o)
@statements << o @statements << o
self self
end end
def to_vool def to_vool
if( single? ) if( single? )
first.to_vool first.to_vool

View File

@ -5,6 +5,8 @@ module Vool
def initialize(name , value ) def initialize(name , value )
raise "Name nil #{self}" unless name raise "Name nil #{self}" unless name
raise "Value nil #{self}" unless value raise "Value nil #{self}" unless value
raise "Value cant be Assignment #{value}" if value.is_a?(Assignment)
raise "Value cant be Statements #{value}" if value.is_a?(Statements)
@name , @value = name , value @name , @value = name , value
end end
@ -19,6 +21,7 @@ module Vool
def chain_assign(assign , compiler) def chain_assign(assign , compiler)
return assign unless @value.is_a?(CallStatement) return assign unless @value.is_a?(CallStatement)
raise "Move me to ruby layer"
@value.to_mom(compiler) << assign @value.to_mom(compiler) << assign
end end
end end

View File

@ -30,6 +30,9 @@ module Vool
def prepend(o) def prepend(o)
@statements = [o] + @statements @statements = [o] + @statements
end end
def shift
@statements.shift
end
# to_mom all the statements. Append subsequent ones to the first, and return the # to_mom all the statements. Append subsequent ones to the first, and return the
# first. # first.

View File

@ -9,10 +9,6 @@ module Ruby
RubyCompiler.compile(input) RubyCompiler.compile(input)
end end
def ruby_to_vool(input)
FIXMERubyXCompiler.new(input).ruby_to_vool
end
def assert_raises_muted &block def assert_raises_muted &block
orig_stdout = $stdout orig_stdout = $stdout
$stdout = StringIO.new $stdout = StringIO.new

View File

@ -1,7 +1,7 @@
require_relative "helper" require_relative "helper"
module Ruby module Ruby
class TestSendFoo < MiniTest::Test class TestSendNoArg < MiniTest::Test
include RubyTests include RubyTests
def setup def setup
@lst = compile( "foo") @lst = compile( "foo")
@ -22,7 +22,7 @@ module Ruby
assert_equal "self.foo()" , @lst.to_s assert_equal "self.foo()" , @lst.to_s
end end
end end
class TestSendBar < MiniTest::Test class TestSendSimpleArg < MiniTest::Test
include RubyTests include RubyTests
def setup def setup
@lst = compile( "bar(1)") @lst = compile( "bar(1)")
@ -63,5 +63,30 @@ module Ruby
lst = compile( "super(1)") lst = compile( "super(1)")
assert_nil lst.name assert_nil lst.name
end end
class TestSendSendArgs < MiniTest::Test
include RubyTests
def setup
@lst = compile( "call(arg1, arg2(more))")
end
def test_one_arg
assert_equal SendStatement , @lst.class
end
def test_one_arg_name
assert_equal :call , @lst.name
end
def test_one_arg_args
assert_equal SendStatement , @lst.arguments.first.class
end
def test_one_arg_args_args
assert_equal 0 , @lst.arguments.first.arguments.length
end
def test_two_arg_args
assert_equal SendStatement , @lst.arguments[1].class
end
def test_two_arg_args_args
assert_equal SendStatement , @lst.arguments[1].arguments.first.class
assert_equal :more , @lst.arguments[1].arguments.first.name
end
end
end end
end end

View File

@ -1,7 +1,7 @@
require_relative "helper" require_relative "helper"
module Ruby module Ruby
class TestSendFooVool < MiniTest::Test class TestSendNoArgVool < MiniTest::Test
include RubyTests include RubyTests
def setup def setup
@lst = compile( "foo").to_vool @lst = compile( "foo").to_vool
@ -19,7 +19,7 @@ module Ruby
assert_equal [] , @lst.arguments assert_equal [] , @lst.arguments
end end
end end
class TestSendBarVool < MiniTest::Test class TestSendSimpleArgVool < MiniTest::Test
include RubyTests include RubyTests
def setup def setup
@lst = compile( "bar(1)").to_vool @lst = compile( "bar(1)").to_vool

View File

@ -0,0 +1,70 @@
require_relative "helper"
module Ruby
module LastBar
include RubyTests
def test_last_class
assert_equal Vool::SendStatement , @lst.last.class
end
def test_last_name
assert_equal :last , @lst.last.name
end
def test_last_arg
assert_equal Vool::LocalVariable , @lst.last.arguments.first.class
end
def test_lst_class
assert_equal Vool::Statements , @lst.class
end
def test_lst_no_statements
@lst.statements.each{|st| assert( ! st.is_a?(Vool::Statements) , st.class)}
end
end
class TestSendSendArgVool < MiniTest::Test
include LastBar
def setup
@lst = compile( "last(foo(1))").to_vool
end
def test_classes
assert_equal Vool::Statements , @lst.class
assert_equal Vool::LocalAssignment , @lst.first.class
assert_equal Vool::SendStatement , @lst.last.class
end
def test_foo1
assert_equal Vool::SendStatement , @lst.first.value.class
assert_equal :foo , @lst.first.value.name
assert_equal Vool::IntegerConstant , @lst.first.value.arguments.first.class
end
end
class Test3SendVool < MiniTest::Test
include LastBar
def setup
@lst = compile( "last(foo(more(1)))").to_vool
end
def test_classes
assert_equal Vool::Statements , @lst.class
assert_equal Vool::LocalAssignment , @lst.first.class
assert_equal Vool::SendStatement , @lst.last.class
end
def test_foo
assert_equal Vool::SendStatement , @lst.first.value.class
assert_equal :more , @lst.first.value.name
assert_equal Vool::IntegerConstant , @lst.first.value.arguments.first.class
end
end
class Test5SendVool < MiniTest::Test
include LastBar
def setup
@lst = compile( "last(foo(more(even_more(1),and_more(with_more))))").to_vool
end
def test_classes
assert_equal Vool::Statements , @lst.class
assert_equal Vool::LocalAssignment , @lst.first.class
assert_equal Vool::SendStatement , @lst.last.class
end
def test_foo
assert_equal Vool::SendStatement , @lst.first.value.class
assert_equal :even_more , @lst.first.value.name
assert_equal Vool::IntegerConstant , @lst.first.value.arguments.first.class
end
end
end