141 lines
4.4 KiB
Ruby
141 lines
4.4 KiB
Ruby
module RubyX
|
|
# The RubyXCompiler provides the main interface to create binaries, and also
|
|
# give helper functions to create any intermediate layer.
|
|
# Layers are:
|
|
# - ruby , always needed as input, string
|
|
# - sol - intermediate language layer
|
|
# - slot_machine - intermediate machine layer
|
|
# - risc - "last" intermediate machine layer
|
|
# - target - arm or interpreter binary code
|
|
# - binary - "linked" code, everything need to create an elf binary
|
|
#
|
|
#
|
|
# There are methods to go from ruby to any of the layers in the system
|
|
# (mainly for testing). ruby_to_binary creates actual binary code
|
|
# for a given platform.
|
|
# The compiler keeps the sol source as an instance.
|
|
# To compile several sources, more sol can be added, ie ruby_to_sol
|
|
# can be called several times.
|
|
#
|
|
# All other methods come in pairs, one takes ruby source (those are for testing)
|
|
# and the other uses the stored sol source for further processing.
|
|
#
|
|
# Only builtin is loaded, so no runtime , but the compiler
|
|
# can be used to read the runtime and then any other code
|
|
#
|
|
class RubyXCompiler
|
|
|
|
attr_reader :sol , :options
|
|
|
|
# initialize boots Parfait and Risc (ie load Builin)
|
|
def initialize(options)
|
|
@options = options
|
|
Parfait.boot!(options[:parfait] || {})
|
|
Risc.boot!(options[:risc] || {})
|
|
end
|
|
|
|
# The highest level function creates binary code for the given ruby code
|
|
# for the given platform (see Platform). Binary code means that sol/slot_machine/risc
|
|
# are created and then assembled into BinaryCode objects.
|
|
# (no executable is generated, only the binary code and objects needed for a binary)
|
|
#
|
|
# A Linker is returned that may be used to create an elf binay
|
|
#
|
|
# The compiling is done by to_binary
|
|
def ruby_to_binary(ruby , platform)
|
|
ruby_to_sol(ruby)
|
|
to_binary(platform)
|
|
end
|
|
|
|
# ruby_to_target creates Target instructions (but does not link)
|
|
#
|
|
# After creating sol, we call to_target
|
|
# Return a Linker
|
|
def ruby_to_target(ruby , platform)
|
|
ruby_to_sol(ruby)
|
|
to_target( platform )
|
|
end
|
|
|
|
# ruby_to_risc creates Risc instructions
|
|
#
|
|
# After creating sol, we call to_risc
|
|
# Return a RiscCollection
|
|
def ruby_to_risc(ruby)
|
|
ruby_to_sol(ruby)
|
|
to_risc()
|
|
end
|
|
|
|
# Transform the incoming ruby source (string) to slot
|
|
#
|
|
# The sol is stored using ruby_to_sol,the to_slot is called
|
|
# Return SlotMachine Statement
|
|
def ruby_to_slot(ruby)
|
|
ruby_to_sol(ruby)
|
|
to_slot
|
|
end
|
|
|
|
# Process previously stored sol source to binary.
|
|
# Binary code is generated by calling to_risc, then positioning and calling
|
|
# create_binary on the linker. The linker may then be used to creat a binary file.
|
|
# The biary the method name refers to is binary code in memory, or in BinaryCode
|
|
# objects to be precise.
|
|
def to_binary(platform)
|
|
linker = to_target(platform)
|
|
linker.position_all
|
|
linker.create_binary
|
|
linker
|
|
end
|
|
|
|
# transform stored sol to target code
|
|
# return a linker
|
|
def to_target(platform)
|
|
raise "No platform given" unless platform
|
|
collection = to_risc
|
|
collection.translate(platform)
|
|
end
|
|
|
|
# Process previously stored sol source to risc.
|
|
# return a Risc::RiscCollection , a collection of MethodCompilers
|
|
def to_risc()
|
|
slot = to_slot
|
|
slot.to_risc()
|
|
end
|
|
|
|
# return slot_machine for the previously stored sol source.
|
|
def to_slot
|
|
@sol.to_parfait
|
|
@sol.to_slot(nil)
|
|
end
|
|
|
|
# ruby_to_sol compiles the ruby to ast, and then to sol
|
|
def ruby_to_sol(ruby_source)
|
|
ruby_tree = Ruby::RubyCompiler.compile( ruby_source )
|
|
unless(@sol)
|
|
@sol = ruby_tree.to_sol
|
|
return @sol
|
|
end
|
|
# TODO: should check if this works with reopening classes
|
|
# or whether we need to unify the sol for a class
|
|
unless(@sol.is_a?(Sol::ScopeStatement))
|
|
@sol = Sol::ScopeStatement.new([@sol])
|
|
end
|
|
@sol << ruby_tree.to_sol
|
|
end
|
|
|
|
def load_parfait
|
|
parfait = ["object"]
|
|
parfait.each do |file|
|
|
path = File.expand_path("../../parfait/#{file}.rb",__FILE__)
|
|
ruby_to_sol(File.read(path))
|
|
end
|
|
end
|
|
|
|
def self.ruby_to_binary( ruby , options)
|
|
compiler = RubyXCompiler.new(options)
|
|
compiler.load_parfait if options[:load_parfait]
|
|
compiler.ruby_to_sol(ruby)
|
|
compiler.to_binary(options[:platform])
|
|
end
|
|
end
|
|
end
|