diff --git a/lib/ruby/ruby_compiler.rb b/lib/ruby/ruby_compiler.rb index 27746258..d614b822 100644 --- a/lib/ruby/ruby_compiler.rb +++ b/lib/ruby/ruby_compiler.rb @@ -33,6 +33,7 @@ module Ruby ast = Parser::CurrentRuby.parse( input ) rescue => e puts "Error parsing #{input}" + raise e end begin self.new.process(ast) diff --git a/lib/ruby/send_statement.rb b/lib/ruby/send_statement.rb index 5611bd5a..ce781d92 100644 --- a/lib/ruby/send_statement.rb +++ b/lib/ruby/send_statement.rb @@ -4,6 +4,12 @@ module Ruby # The SendStatement really only provides to_s, so see CallStatement # class SendStatement < CallStatement + + def to_vool + return super unless @receiver.is_a?(ModuleName) and @receiver.name == :X + args = @arguments.collect { |arg| arg.to_vool } + Vool::MacroExpression.new(name , args) + end def to_s(depth = 0) at_depth( depth , "#{receiver}.#{name}(#{arguments.join(', ')})") end diff --git a/lib/vool/if_statement.rb b/lib/vool/if_statement.rb index c7ebd829..5c83a833 100644 --- a/lib/vool/if_statement.rb +++ b/lib/vool/if_statement.rb @@ -15,12 +15,11 @@ module Vool false_label = Mom::Label.new( self , "false_label_#{object_id.to_s(16)}") merge_label = Mom::Label.new( self , "merge_label_#{object_id.to_s(16)}") - check = Mom::TruthCheck.new(condition.to_slot(compiler) , false_label) if @condition.is_a?(CallStatement) head = @condition.to_mom(compiler) - head << check + head << check_slot(compiler , false_label) else - head = check + head = check_slot(compiler , false_label) end head << true_label head << if_true.to_mom(compiler) if @if_true @@ -31,6 +30,11 @@ module Vool head end + # create the slot lazily, so to_mom gets called first + def check_slot(compiler , false_label) + Mom::TruthCheck.new(@condition.to_slot(compiler) , false_label) + end + def each(&block) block.call(condition) @if_true.each(&block) if @if_true diff --git a/lib/vool/macro_expression.rb b/lib/vool/macro_expression.rb new file mode 100644 index 00000000..03a69221 --- /dev/null +++ b/lib/vool/macro_expression.rb @@ -0,0 +1,27 @@ +module Vool + + class MacroExpression < CallStatement + + def initialize(name , arguments ) + super(name , SelfExpression.new , arguments) + end + + def to_mom(compiler) + parts = name.to_s.split("_") + class_name = "Mom::#{parts.collect{|s| s.capitalize}.join}" + eval(class_name).new( self , *arguments) + end + + # When used as right hand side, this tells what data to move to get the result into + # a varaible. It is (off course) the return value of the message + def to_slot(_) + Mom::SlotDefinition.new(:message ,[ :return_value]) + end + + def to_s(depth = 0) + sen = "X.#{name}(#{@arguments.collect{|a| a.to_s}.join(', ')})" + at_depth(depth , sen) + end + + end +end diff --git a/lib/vool/return_statement.rb b/lib/vool/return_statement.rb index a19643ba..2ea288de 100644 --- a/lib/vool/return_statement.rb +++ b/lib/vool/return_statement.rb @@ -17,13 +17,11 @@ module Vool # - store the given return value, this is a SlotMove # - activate return sequence (reinstantiate old message and jump to return address) def to_mom( compiler ) - load = Mom::SlotLoad.new( self , [:message , :return_value] , - @return_value.to_slot(compiler) ) if @return_value.is_a?(CallStatement) ret = @return_value.to_mom(compiler) - ret << load + ret << slot_load(compiler) else - ret = load + ret = slot_load(compiler) end ret << Mom::ReturnJump.new(self , compiler.return_label ) end @@ -31,5 +29,10 @@ module Vool def to_s(depth = 0) at_depth(depth , "return #{@return_value.to_s}") end + + def slot_load(compiler) + Mom::SlotLoad.new( self , [:message , :return_value] , + @return_value.to_slot(compiler) ) + end end end diff --git a/lib/vool/statement.rb b/lib/vool/statement.rb index 44d6b184..bf2fa101 100644 --- a/lib/vool/statement.rb +++ b/lib/vool/statement.rb @@ -70,6 +70,7 @@ require_relative "if_statement" require_relative "ivar_assignment" require_relative "lambda_expression" require_relative "local_assignment" +require_relative "macro_expression" require_relative "method_expression" require_relative "class_method_expression" require_relative "return_statement" diff --git a/test/mains/test_new.rb b/test/mains/test_new.rb index 3ead3eaa..1b5afbad 100644 --- a/test/mains/test_new.rb +++ b/test/mains/test_new.rb @@ -44,7 +44,7 @@ module Mains run_all assert_equal ::Integer , get_return.class , " " assert_equal 4 , get_return , " " - assert_equal "hi" , @interpreter.stdout + assert_equal "11111" , @interpreter.stdout end end diff --git a/test/ruby/helper.rb b/test/ruby/helper.rb index e5c7d3cd..eb3425ed 100644 --- a/test/ruby/helper.rb +++ b/test/ruby/helper.rb @@ -2,12 +2,20 @@ require_relative "../helper" module Ruby module RubyTests + include ScopeHelper def setup Parfait.boot!(Parfait.default_test_options) end def compile(input) RubyCompiler.compile(input) end + def compile_main(input) + RubyCompiler.compile(as_main(input)) + end + def compile_main_vool(input) + xcompiler = RubyX::RubyXCompiler.new(RubyX.default_test_options) + xcompiler.ruby_to_vool(as_main(input)) + end def assert_raises_muted &block orig_stdout = $stdout diff --git a/test/ruby/test_macro_expression.rb b/test/ruby/test_macro_expression.rb new file mode 100644 index 00000000..bdd55cc8 --- /dev/null +++ b/test/ruby/test_macro_expression.rb @@ -0,0 +1,65 @@ +require_relative "helper" + +module Ruby + class TestPlusEqualsRuby < Minitest::Test + include RubyTests + def setup + @lst = compile_main( "X.plus_equals(1)") + end + def method_body + @lst.body.first.body + end + def test_class + assert_equal Ruby::ClassStatement , @lst.class + assert_equal Ruby::Statements , @lst.body.class + end + def test_method + assert_equal Ruby::MethodStatement , @lst.body.first.class + assert_equal Ruby::SendStatement , method_body.class + end + def test_send + assert_equal :X , method_body.receiver.name + assert_equal :plus_equals , method_body.name + end + end + class TestPlusEquals < Minitest::Test + include RubyTests + def setup + @lst = compile( "X.plus_equals(1)").to_vool + end + def test_class + assert_equal Vool::MacroExpression , @lst.class + end + def test_arg1 + assert_equal Vool::IntegerConstant , @lst.arguments.first.class + end + def test_name + assert_equal :plus_equals , @lst.name + end + end + class TestPlusEqualsX < Minitest::Test + include RubyTests + def setup + @lst = compile_main( "X.plus_equals(arg,1)").to_vool + end + def method_body + @lst.body.first.body + end + def test_class + assert_equal Vool::ClassExpression , @lst.class + assert_equal Vool::MethodExpression , @lst.body.first.class + end + def test_macro_class + assert_equal Vool::ReturnStatement , method_body.class + assert_equal Vool::MacroExpression , method_body.return_value.class + end + def test_args + assert_equal Vool::LocalVariable , method_body.return_value.arguments.first.class + assert_equal Vool::IntegerConstant , method_body.return_value.arguments.last.class + end + def test_name + assert_equal :plus_equals , method_body.return_value.name + end + end + +end diff --git a/test/vool/test_macro_expression.rb b/test/vool/test_macro_expression.rb new file mode 100644 index 00000000..c82b62a2 --- /dev/null +++ b/test/vool/test_macro_expression.rb @@ -0,0 +1,35 @@ +require_relative "helper" +module Mom + class PlusEquals < Instruction + attr_reader :a , :b + def initialize(source , arg , b) + super(source) + @a = arg + @b = b + end + end +end + +module Vool + class TestMacroMom < MiniTest::Test + include VoolCompile + + def setup + @compiler = compile_first_method( "X.plus_equals(arg,1)") + @ins = @compiler.mom_instructions.next + end + + def test_class_compiles + assert_equal Mom::PlusEquals , @ins.class , @ins + end + def test_arg1 + assert_equal Vool::LocalVariable , @ins.a.class + assert_equal :arg , @ins.a.name + end + def test_arg2 + assert_equal Vool::IntegerConstant , @ins.b.class + assert_equal 1 , @ins.b.value + end + end + +end