moved all compile to ast, vm does link and assemble. Getting there

This commit is contained in:
Torsten Ruger 2014-05-05 11:03:43 +03:00
parent 99da6f5be3
commit de66238a9e
8 changed files with 58 additions and 127 deletions

View File

@ -36,23 +36,18 @@ module Arm
end end
def function_call call def function_call call
raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall
e = Vm::Block.new("call_#{call.name}") bl( :function => call.function )
e.add_code( bl( :function => call.function ))
end end
def main_entry def main_entry
e = Vm::Block.new("main_entry") mov( :left => :fp , :right => 0 )
e.add_code( mov( :left => :fp , :right => 0 ))
end end
def main_exit def main_exit
e = Vm::Block.new("main_exit") syscall(0)
e.join( syscall(0) )
end end
def syscall num def syscall num
e = Vm::Block.new("syscall") mov( :left => 7 , :right => num )
e.add_code( MoveInstruction.new( :left => 7 , :right => num ) ) swi( {} )
e.add_code( CallInstruction.new( :swi => 0 ) )
e
end end
end end
end end

View File

@ -1,4 +1,5 @@
# collection of the simple ones, int and strings and such # collection of the simple ones, int and strings and such
module Ast module Ast
class IntegerExpression < Expression class IntegerExpression < Expression
@ -29,7 +30,7 @@ module Ast
def compile context def compile context
# TODO check if needst to be added? # TODO check if needst to be added?
ObjectReference.new( StringValue.new(string) ) Vm::ObjectReference.new( Vm::StringValue.new(string) )
end end
def == other def == other
compare other , [:string] compare other , [:string]

View File

@ -16,8 +16,7 @@ module Vm
# See Value description on how to create code/instructions # See Value description on how to create code/instructions
# Blocks have a list of expressions, that they compile into a list of codes # Codes then get assembled into bytes (after linking)
# Codes then get assembled into bytes (after positioning)
class Block < Code class Block < Code
@ -25,53 +24,35 @@ module Vm
super() super()
@name = name.to_sym @name = name.to_sym
@next = nil @next = nil
@values = []
@codes = [] @codes = []
end end
attr_reader :name , :next , :codes , :values attr_reader :name , :next , :codes
def verify
end
def add_value v
@values << v
end
def length def length
@codes.inject(0) {| sum , item | sum + item.length} @codes.inject(0) {| sum , item | sum + item.length}
end end
def add_code(kode) def add_code(kode)
kode.at(@position)
length = kode.length
@position += length
@codes << kode @codes << kode
self self
end end
def compile context def link_at pos , context
@values.each do |value| @position = pos
value.compile(context) @codes.each do |code|
code.link_at(pos , context)
pos += code.length
end end
pos
end end
def assemble(io) def assemble(io)
@codes.each do |obj| @codes.each do |obj|
obj.assemble io obj.assemble io
end end
end end
# all machine methods produce blocks so it's a unified interface. But often they are just linear
# code after linear code, so then they can be joined.
# The other block is useless after, all instructions move here
def join other
raise "block is chained already, can't join #{inspect}" if @next
other.codes.each do |code|
add_code code
end
other.values.each do |value|
add_value value
end
self
end
# set the next executed block after self. # set the next executed block after self.
# why is this useful? if it's unconditional, why not merge them: # why is this useful? if it's unconditional, why not merge them:
# So the second block can be used as a jump target. You standard loop needs a block to setup # So the second block can be used as a jump target. You standard loop needs a block to setup

View File

@ -1,9 +1,7 @@
require_relative "values"
module Vm module Vm
# Base class for anything that we can assemble # Base class for anything that we can assemble
# Derived classes include instructions and data(strings) # Derived classes include instructions and data(values)
# The commonality abstracted here is the length and position # The commonality abstracted here is the length and position
# and the ability to assemble itself into the stream # and the ability to assemble itself into the stream
@ -11,7 +9,7 @@ module Vm
# All code is position independant once assembled. # All code is position independant once assembled.
# But for jumps and calls two passes are neccessary. # But for jumps and calls two passes are neccessary.
# The first setting the position, the second assembling # The first setting the position, the second assembling
class Code < Value class Code
# set the position to zero, will have to reset later # set the position to zero, will have to reset later
def initialize def initialize
@ -29,7 +27,7 @@ module Vm
# The containing class (assembler/function) call this to tell the instruction/data where it is in the # The containing class (assembler/function) call this to tell the instruction/data where it is in the
# stream. During assembly the position is then used to calculate pc relative addresses. # stream. During assembly the position is then used to calculate pc relative addresses.
def at address def link_at address , context
@address = address @address = address
end end

View File

@ -28,18 +28,13 @@ module Vm
@entry.length + @exit.length + super @entry.length + @exit.length + super
end end
def compile context def compiled context
function = context.program.get_function(name) function = context.program.get_function(name)
unless function unless function
function = Vm::Kernel.send(name) function = Vm::Kernel.send(name)
context.program.get_or_create_function( name , function , arity ) context.program.get_or_create_function( name , function , arity )
end end
end end
def verify
@entry.verify
@exit.verify
end
private private
def add_arg value def add_arg value

View File

@ -23,24 +23,25 @@ module Vm
def initialize machine def initialize machine
super("start") super("start")
Machine.instance = eval("#{machine}::#{machine}Machine").new Machine.instance = eval("#{machine}::#{machine}Machine").new
@context = Context.new(self) @context = Context.new(self)
#global objects (data)
@objects = [] @objects = []
# global functions
@functions = []
@entry = Vm::Kernel::start @entry = Vm::Kernel::start
#main gets executed between entry and exit
@main = nil
@exit = Vm::Kernel::exit @exit = Vm::Kernel::exit
end end
attr_reader :context attr_reader :context , :main , :functions
def functions
@values
end
def add_object o def add_object o
return if @objects.include? o return if @objects.include? o
@objects << o # TODO check type , no basic values allowed (must be wrapped) @objects << o # TODO check type , no basic values allowed (must be wrapped)
end end
def get_function name def get_function name
@values.detect{ |f| (f.name == name) && (f.class == Function) } @functions.detect{ |f| (f.name == name) && (f.class == Function) }
end end
# preferred way of creating new functions (also forward declarations, will flag unresolved later) # preferred way of creating new functions (also forward declarations, will flag unresolved later)
@ -48,57 +49,33 @@ module Vm
fun = get_function name fun = get_function name
unless fun unless fun
fun = Function.new(name) fun = Function.new(name)
@values << fun @functions << fun
end end
fun fun
end end
def compile(context) def link_at( start , context)
@values.each do |function| @position = start
function.compile(context) @entry.link_at( start , context )
end start += @entry.length
@entry.compile(context) @main.link_at( start , context )
super(context) start += @main.length
@exit.compile(context) @functions.each do |function|
#string_table function.link_at(start , context)
@objects.each do |o| start += function.length
o.compile(context)
end
fix_positions
fix_positions # second time for forward references
end
def fix_positions
@position = 0
@entry.at( @position )
@position += @entry.length
@values.each do |function|
function.at(@position)
@position += function.length
end end
@objects.each do |o| @objects.each do |o|
o.at(@position) o.link_at(start , context)
@position += o.length start += o.length
end end
@exit.at( @position ) @exit.link_at( start , context)
@position += @exit.length start += @exit.length
end end
def wrap_as_main block def main= code
# #blockify @main = code
unless block.is_a? Block
raise "No block, start coding torsten"
end
add_value block
@entry.next block
block.next @exit
end end
def verify
@values.each do |funct|
funct.verify
end
end
private private
# the main function # the main function
def create_main def create_main

View File

@ -1,3 +1,5 @@
require_relative "code"
module Vm module Vm
# Values represent the information as it is processed. Different subclasses for different types, # Values represent the information as it is processed. Different subclasses for different types,
@ -16,29 +18,14 @@ module Vm
# Word Values are what fits in a register. Derived classes # Word Values are what fits in a register. Derived classes
# Float, Reference , Integer(s) must fit the same registers # Float, Reference , Integer(s) must fit the same registers
class Value # just a base class for data. not sure how this will be usefull (may just have read too much llvm)
def bit_size class Value < Code
8 * byte_size
end
def byte_size
raise "abstract method called #{self.inspect}"
end
# since we convert ast to values in conversion, value itself is responsible for compiling itself
# Compile must return a value, usually used in the next level up
def compile(context)
raise "abstract method called #{self.inspect}"
end
end end
class Word < Value class Word < Value
def load reg def load reg
Machine.instance.word_load self , reg Machine.instance.word_load self , reg
end end
def compile context
#nothing to do here
end
end end
class Unsigned < Word class Unsigned < Word
@ -63,15 +50,9 @@ module Vm
def initialize string def initialize string
@string = string @string = string
end end
def at pos
@pos = pos
end
def length def length
@string.length + 3 @string.length + 3
end end
def compile context
#nothing to do here
end
attr_reader :string attr_reader :string
end end
@ -84,7 +65,7 @@ module Vm
end end
attr_reader :object attr_reader :object
def compile context def compiled context
if object.is_a? StringValue if object.is_a? StringValue
context.program.add_object object context.program.add_object object
else else

View File

@ -19,13 +19,16 @@ class TestRunner < MiniTest::Test
def execute file def execute file
string = File.read(file) string = File.read(file)
syntax = Parser::Composed.new.parse(string) syntax = Parser::Composed.new.parse(string)
tree = Parser::Transform.new.apply(syntax) main = Parser::Transform.new.apply(syntax)
program = Vm::Program.new "Arm" program = Vm::Program.new "Arm"
value = tree.to_value
program.wrap_as_main value program.main = main.compile( program.context )
compiled = program.compile( program.context )
program.verify program.link_at( 0 , program.context )
binary = program.assemble(StringIO.new )
puts program.to_yaml puts program.to_yaml
end end