A good start on the macro idea
I call it macro because it lets you insert basically arbitrary risc code into the ruby level. The way it works: Reserve namespace X map any X.some_call to a Mom instruction by the name SomeCall which must take the same args in constructor as given And obviously produce whatever risc it wants Hoping to rewrite builtin around this idea (with the existing Mom builtn instructions)
This commit is contained in:
parent
c0a3c9b65c
commit
b9bdc55059
@ -33,6 +33,7 @@ module Ruby
|
|||||||
ast = Parser::CurrentRuby.parse( input )
|
ast = Parser::CurrentRuby.parse( input )
|
||||||
rescue => e
|
rescue => e
|
||||||
puts "Error parsing #{input}"
|
puts "Error parsing #{input}"
|
||||||
|
raise e
|
||||||
end
|
end
|
||||||
begin
|
begin
|
||||||
self.new.process(ast)
|
self.new.process(ast)
|
||||||
|
@ -4,6 +4,12 @@ module Ruby
|
|||||||
# The SendStatement really only provides to_s, so see CallStatement
|
# The SendStatement really only provides to_s, so see CallStatement
|
||||||
#
|
#
|
||||||
class SendStatement < 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)
|
def to_s(depth = 0)
|
||||||
at_depth( depth , "#{receiver}.#{name}(#{arguments.join(', ')})")
|
at_depth( depth , "#{receiver}.#{name}(#{arguments.join(', ')})")
|
||||||
end
|
end
|
||||||
|
@ -15,12 +15,11 @@ module Vool
|
|||||||
false_label = Mom::Label.new( self , "false_label_#{object_id.to_s(16)}")
|
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)}")
|
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)
|
if @condition.is_a?(CallStatement)
|
||||||
head = @condition.to_mom(compiler)
|
head = @condition.to_mom(compiler)
|
||||||
head << check
|
head << check_slot(compiler , false_label)
|
||||||
else
|
else
|
||||||
head = check
|
head = check_slot(compiler , false_label)
|
||||||
end
|
end
|
||||||
head << true_label
|
head << true_label
|
||||||
head << if_true.to_mom(compiler) if @if_true
|
head << if_true.to_mom(compiler) if @if_true
|
||||||
@ -31,6 +30,11 @@ module Vool
|
|||||||
head
|
head
|
||||||
end
|
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)
|
def each(&block)
|
||||||
block.call(condition)
|
block.call(condition)
|
||||||
@if_true.each(&block) if @if_true
|
@if_true.each(&block) if @if_true
|
||||||
|
27
lib/vool/macro_expression.rb
Normal file
27
lib/vool/macro_expression.rb
Normal file
@ -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
|
@ -17,13 +17,11 @@ module Vool
|
|||||||
# - store the given return value, this is a SlotMove
|
# - store the given return value, this is a SlotMove
|
||||||
# - activate return sequence (reinstantiate old message and jump to return address)
|
# - activate return sequence (reinstantiate old message and jump to return address)
|
||||||
def to_mom( compiler )
|
def to_mom( compiler )
|
||||||
load = Mom::SlotLoad.new( self , [:message , :return_value] ,
|
|
||||||
@return_value.to_slot(compiler) )
|
|
||||||
if @return_value.is_a?(CallStatement)
|
if @return_value.is_a?(CallStatement)
|
||||||
ret = @return_value.to_mom(compiler)
|
ret = @return_value.to_mom(compiler)
|
||||||
ret << load
|
ret << slot_load(compiler)
|
||||||
else
|
else
|
||||||
ret = load
|
ret = slot_load(compiler)
|
||||||
end
|
end
|
||||||
ret << Mom::ReturnJump.new(self , compiler.return_label )
|
ret << Mom::ReturnJump.new(self , compiler.return_label )
|
||||||
end
|
end
|
||||||
@ -31,5 +29,10 @@ module Vool
|
|||||||
def to_s(depth = 0)
|
def to_s(depth = 0)
|
||||||
at_depth(depth , "return #{@return_value.to_s}")
|
at_depth(depth , "return #{@return_value.to_s}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def slot_load(compiler)
|
||||||
|
Mom::SlotLoad.new( self , [:message , :return_value] ,
|
||||||
|
@return_value.to_slot(compiler) )
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -70,6 +70,7 @@ require_relative "if_statement"
|
|||||||
require_relative "ivar_assignment"
|
require_relative "ivar_assignment"
|
||||||
require_relative "lambda_expression"
|
require_relative "lambda_expression"
|
||||||
require_relative "local_assignment"
|
require_relative "local_assignment"
|
||||||
|
require_relative "macro_expression"
|
||||||
require_relative "method_expression"
|
require_relative "method_expression"
|
||||||
require_relative "class_method_expression"
|
require_relative "class_method_expression"
|
||||||
require_relative "return_statement"
|
require_relative "return_statement"
|
||||||
|
@ -44,7 +44,7 @@ module Mains
|
|||||||
run_all
|
run_all
|
||||||
assert_equal ::Integer , get_return.class , " "
|
assert_equal ::Integer , get_return.class , " "
|
||||||
assert_equal 4 , get_return , " "
|
assert_equal 4 , get_return , " "
|
||||||
assert_equal "hi" , @interpreter.stdout
|
assert_equal "11111" , @interpreter.stdout
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -2,12 +2,20 @@ require_relative "../helper"
|
|||||||
|
|
||||||
module Ruby
|
module Ruby
|
||||||
module RubyTests
|
module RubyTests
|
||||||
|
include ScopeHelper
|
||||||
def setup
|
def setup
|
||||||
Parfait.boot!(Parfait.default_test_options)
|
Parfait.boot!(Parfait.default_test_options)
|
||||||
end
|
end
|
||||||
def compile(input)
|
def compile(input)
|
||||||
RubyCompiler.compile(input)
|
RubyCompiler.compile(input)
|
||||||
end
|
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
|
def assert_raises_muted &block
|
||||||
orig_stdout = $stdout
|
orig_stdout = $stdout
|
||||||
|
65
test/ruby/test_macro_expression.rb
Normal file
65
test/ruby/test_macro_expression.rb
Normal file
@ -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
|
35
test/vool/test_macro_expression.rb
Normal file
35
test/vool/test_macro_expression.rb
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user