From 84b9811e557c9e2d6772a92ba162e185a02ad190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20R=C3=BCger?= Date: Thu, 15 Aug 2019 21:30:36 +0300 Subject: [PATCH] Fixing ruby send with arguments When send has complex args, mostly more sends, we hoist those out and pass created temporary variables --- lib/ruby/call_statement.rb | 18 ++++++-- lib/ruby/statements.rb | 5 ++- lib/vool/assignment.rb | 3 ++ lib/vool/statements.rb | 3 ++ test/ruby/helper.rb | 4 -- test/ruby/test_send_statement.rb | 29 ++++++++++++- test/ruby/test_send_statement1.rb | 4 +- test/ruby/test_send_statement2.rb | 70 +++++++++++++++++++++++++++++++ 8 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 test/ruby/test_send_statement2.rb diff --git a/lib/ruby/call_statement.rb b/lib/ruby/call_statement.rb index 3675a342..17496c14 100644 --- a/lib/ruby/call_statement.rb +++ b/lib/ruby/call_statement.rb @@ -17,6 +17,7 @@ module Ruby @arguments ||= [] end + # we "normalize" or flatten any complex argument expressions into a list def to_vool statements = Vool::Statements.new([]) arguments = [] @@ -31,12 +32,23 @@ module Ruby 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) - if arg.is_a?(Constant) and !arg.is_a?(CallStatement) - arguments << arg.to_vool + vool_arg = arg.to_vool + if arg.is_a?(Constant) + arguments << vool_arg return 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 arguments << Vool::LocalVariable.new(assign.name) end diff --git a/lib/ruby/statements.rb b/lib/ruby/statements.rb index df716b90..958256e7 100644 --- a/lib/ruby/statements.rb +++ b/lib/ruby/statements.rb @@ -20,13 +20,16 @@ module Ruby def length @statements.length end + def shift + @statements.shift + end def [](i) @statements[i] end def <<(o) @statements << o self - end + end def to_vool if( single? ) first.to_vool diff --git a/lib/vool/assignment.rb b/lib/vool/assignment.rb index e5838e0f..fb4d896e 100644 --- a/lib/vool/assignment.rb +++ b/lib/vool/assignment.rb @@ -5,6 +5,8 @@ module Vool def initialize(name , value ) raise "Name nil #{self}" unless name 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 end @@ -19,6 +21,7 @@ module Vool def chain_assign(assign , compiler) return assign unless @value.is_a?(CallStatement) + raise "Move me to ruby layer" @value.to_mom(compiler) << assign end end diff --git a/lib/vool/statements.rb b/lib/vool/statements.rb index 358bda4d..f9702cee 100644 --- a/lib/vool/statements.rb +++ b/lib/vool/statements.rb @@ -30,6 +30,9 @@ module Vool def prepend(o) @statements = [o] + @statements end + def shift + @statements.shift + end # to_mom all the statements. Append subsequent ones to the first, and return the # first. diff --git a/test/ruby/helper.rb b/test/ruby/helper.rb index 097096fe..0e6b2b12 100644 --- a/test/ruby/helper.rb +++ b/test/ruby/helper.rb @@ -9,10 +9,6 @@ module Ruby RubyCompiler.compile(input) end - def ruby_to_vool(input) - FIXMERubyXCompiler.new(input).ruby_to_vool - end - def assert_raises_muted &block orig_stdout = $stdout $stdout = StringIO.new diff --git a/test/ruby/test_send_statement.rb b/test/ruby/test_send_statement.rb index ae490f4e..6c8475d2 100644 --- a/test/ruby/test_send_statement.rb +++ b/test/ruby/test_send_statement.rb @@ -1,7 +1,7 @@ require_relative "helper" module Ruby - class TestSendFoo < MiniTest::Test + class TestSendNoArg < MiniTest::Test include RubyTests def setup @lst = compile( "foo") @@ -22,7 +22,7 @@ module Ruby assert_equal "self.foo()" , @lst.to_s end end - class TestSendBar < MiniTest::Test + class TestSendSimpleArg < MiniTest::Test include RubyTests def setup @lst = compile( "bar(1)") @@ -63,5 +63,30 @@ module Ruby lst = compile( "super(1)") assert_nil lst.name 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 diff --git a/test/ruby/test_send_statement1.rb b/test/ruby/test_send_statement1.rb index 17d0439f..9e72b714 100644 --- a/test/ruby/test_send_statement1.rb +++ b/test/ruby/test_send_statement1.rb @@ -1,7 +1,7 @@ require_relative "helper" module Ruby - class TestSendFooVool < MiniTest::Test + class TestSendNoArgVool < MiniTest::Test include RubyTests def setup @lst = compile( "foo").to_vool @@ -19,7 +19,7 @@ module Ruby assert_equal [] , @lst.arguments end end - class TestSendBarVool < MiniTest::Test + class TestSendSimpleArgVool < MiniTest::Test include RubyTests def setup @lst = compile( "bar(1)").to_vool diff --git a/test/ruby/test_send_statement2.rb b/test/ruby/test_send_statement2.rb new file mode 100644 index 00000000..5e553b1f --- /dev/null +++ b/test/ruby/test_send_statement2.rb @@ -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