start with block_compiler

as a copy of method_compiler
re-merge later, when we know what's needed
This commit is contained in:
Torsten Ruger 2018-07-09 16:48:23 +03:00
parent 7231f301ba
commit dd544214b3
10 changed files with 159 additions and 13 deletions

View File

@ -5,6 +5,16 @@ module Mom
class Constant
end
class BlockConstant < Constant
attr_reader :block
def initialize(bl)
@block = bl
end
def to_parfait(compiler)
@block
end
end
class IntegerConstant < Constant
attr_reader :value
def initialize(value)

View File

@ -43,7 +43,7 @@ module Parfait
def add_block(bl)
block = self.blocks
bl.next = block if(block)
bl.set_next(block) if(block)
@blocks = bl
end

View File

@ -27,6 +27,7 @@ require_relative "risc/parfait_adapter"
require_relative "risc/parfait_boot"
require_relative "risc/linker"
require_relative "risc/method_compiler"
require_relative "risc/block_compiler"
require_relative "risc/assembler"
class Fixnum

View File

@ -0,0 +1,21 @@
module Risc
# A BlockCompiler is much like a Mehtodcompiler, exept for blocks
#
class BlockCompiler
attr_reader :block , :risc_instructions , :constants
def initialize( block , method)
@method = method
@regs = []
@block = block
name = "#{method.self_type.name}.init"
@risc_instructions = Risc.label(name, name)
@risc_instructions << Risc.label( name, "unreachable")
@current = @risc_instructions
@constants = []
end
end
end

View File

@ -19,18 +19,19 @@ module Risc
@risc_instructions << Risc.label( name, "unreachable")
@current = @risc_instructions
@constants = []
@block_compilers = []
end
attr_reader :method , :risc_instructions , :constants
# helper method for builtin mainly
# the class_name is a symbol, which is resolved to the instance_type of that class
#
# return compiler_self_type with the resolved type
# return compiler_for_type with the resolved type
#
def self.compiler_for_class( class_name , method_name , args , frame )
raise "create_method #{class_name}.#{class_name.class}" unless class_name.is_a? Symbol
clazz = Parfait.object_space.get_class_by_name! class_name
compiler_self_type( clazz.instance_type , method_name , args , frame)
compiler_for_type( clazz.instance_type , method_name , args , frame)
end
# create a method for the given type ( Parfait type object)
@ -38,14 +39,18 @@ module Risc
# args a hash that will be converted to a type
# the created method is set as the current and the given type too
# return the compiler
def self.compiler_self_type( type , method_name , args , frame)
def self.compiler_for_type( type , method_name , args , frame)
raise "create_method #{type.inspect} is not a Type" unless type.is_a? Parfait::Type
raise "Args must be Type #{args}" unless args.is_a?(Parfait::Type)
raise "create_method #{method_name}.#{method_name.class}" unless method_name.is_a? Symbol
method = type.create_method( method_name , args , frame)
self.new(method)
end
def add_block_compiler(compiler)
@block_compilers << compiler
end
# convert the given mom instruction to_risc and then add it (see add_code)
# continue down the instruction chain unti depleted
# (adding moves the insertion point so the whole mom chain is added as a risc chain)

View File

@ -14,12 +14,19 @@ module Vool
# This means we do the compiler here (rather than to_mom, which is in
# fact never called)
def slot_definition(compiler)
@parfait_block = to_parfait(compiler)
return Mom::SlotDefinition.new(Mom::IntegerConstant.new(1) , [])
return Mom::SlotDefinition.new(Mom::BlockConstant.new(parfait_block(compiler)) , [])
end
# create a block, a compiler for it, comile the bock and add the compiler(code)
# to the method compiler for further processing
def to_mom( compiler )
# raise "should not be called "
parfait_block = self.parfait_block(compiler)
block_compiler = Risc::BlockCompiler.new( parfait_block , compiler.method )
compiler.add_block_compiler(block_compiler)
puts "BODY #{body}"
head = body.to_mom( block_compiler )
block_compiler.add_mom(head)
block_compiler
end
def each(&block)
@ -31,10 +38,14 @@ module Vool
BlockStatement.new( @args , @body.normalize)
end
private
def to_parfait(compiler)
compiler.method.create_block( make_arg_type , make_frame)
# create the parfait block (parfait representation of the block, a Callable similar
# to CallableMethod)
def parfait_block(compiler)
return @parfait_block if @parfait_block
@parfait_block = compiler.method.create_block( make_arg_type , make_frame)
end
private
def make_arg_type( )
type_hash = {}
@args.each {|arg| type_hash[arg] = :Object }

View File

@ -11,7 +11,11 @@ module Vool
def to_mom(clazz)
@clazz = clazz || raise( "no class in #{self}")
method = @clazz.add_method_for(name , make_arg_type , make_frame , body )
method.compiler_for(clazz.instance_type)
compiler = method.compiler_for(clazz.instance_type)
each do |node| ## TODO: must account for nested blocks (someday)
node.to_mom(compiler) if node.is_a?(BlockStatement)
end
compiler
end
def each(&block)

View File

@ -37,7 +37,10 @@ module MomCompile
assert_equal Risc::MethodCompiler , compiler.class
@method.source.to_mom( compiler )
end
def compile_first_block( block_input )
mom = compile_first_method( "main_local = 5 ; self.main{|val| #{block_input}}")
mom.next(4) # ignore local assign (1) and call (3)
end
def compile_mom(input)
Risc.boot!
RubyX::RubyXCompiler.new(input).ruby_to_mom

View File

View File

@ -0,0 +1,91 @@
require_relative "../helper"
module VoolBlocks
class TestAssignMom < MiniTest::Test
include MomCompile
def setup
Parfait.boot!
Risc::Builtin.boot_functions
@ins = compile_first_block( "local = 5")
end
def test_class_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins
end
def pest_slot_is_set
assert @ins.left
end
def pest_slot_starts_at_message
assert_equal :message , @ins.left.known_object
end
def pest_slot_gets_self
assert_equal :frame , @ins.left.slots[0]
end
def pest_slot_assigns_to_local
assert_equal :main_local , @ins.left.slots[-1]
end
def pest_slot_assigns_something
assert @ins.right
end
def pest_slot_assigns_int
assert_equal Mom::IntegerConstant , @ins.right.known_object.class
end
end
#otherwise as above, but assigning instance, so should get a SlotLoad
class TestAssignMomInstanceToLocal < MiniTest::Test
include MomCompile
def setup
Parfait.boot!
@ins = compile_first_method( "@a = 5 ; local = @a")
end
def pest_class_compiles
assert_equal Mom::SlotLoad , @ins.next.class , @ins
end
end
#compiling to an argument should result in different second parameter in the slot array
class TestAssignToArg < MiniTest::Test
include MomCompile
def setup
Parfait.boot!
@ins = compile_first_method( "arg = 5")
end
def pest_class_compiles
assert_equal Mom::SlotLoad , @ins.class , @ins
end
def pest_slot_is_set
assert @ins.left
end
def pest_slot_starts_at_message
assert_equal :message , @ins.left.known_object
end
def pest_slot_gets_self
assert_equal :arguments , @ins.left.slots[0]
end
def pest_slot_assigns_to_local
assert_equal :arg , @ins.left.slots[-1]
end
end
class TestAssignMomToInstance < MiniTest::Test
include MomCompile
def setup
Parfait.boot!
end
def pest_assigns_const
@ins = compile_first_method( "@a = 5")
assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::IntegerConstant , @ins.right.known_object.class , @ins
end
def pest_assigns_move
@ins = compile_first_method( "@a = arg")
assert_equal Mom::SlotLoad , @ins.class , @ins
assert_equal Mom::SlotDefinition , @ins.right.class , @ins
end
end
end