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
def function_call call
raise "Not FunctionCall #{call.inspect}" unless call.is_a? Vm::FunctionCall
e = Vm::Block.new("call_#{call.name}")
e.add_code( bl( :function => call.function ))
bl( :function => call.function )
end
def main_entry
e = Vm::Block.new("main_entry")
e.add_code( mov( :left => :fp , :right => 0 ))
mov( :left => :fp , :right => 0 )
end
def main_exit
e = Vm::Block.new("main_exit")
e.join( syscall(0) )
syscall(0)
end
def syscall num
e = Vm::Block.new("syscall")
e.add_code( MoveInstruction.new( :left => 7 , :right => num ) )
e.add_code( CallInstruction.new( :swi => 0 ) )
e
mov( :left => 7 , :right => num )
swi( {} )
end
end
end

View File

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

View File

@ -16,8 +16,7 @@ module Vm
# 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 positioning)
# Codes then get assembled into bytes (after linking)
class Block < Code
@ -25,53 +24,35 @@ module Vm
super()
@name = name.to_sym
@next = nil
@values = []
@codes = []
end
attr_reader :name , :next , :codes , :values
attr_reader :name , :next , :codes
def verify
end
def add_value v
@values << v
end
def length
@codes.inject(0) {| sum , item | sum + item.length}
end
def add_code(kode)
kode.at(@position)
length = kode.length
@position += length
@codes << kode
self
end
def compile context
@values.each do |value|
value.compile(context)
def link_at pos , context
@position = pos
@codes.each do |code|
code.link_at(pos , context)
pos += code.length
end
pos
end
def assemble(io)
@codes.each do |obj|
obj.assemble io
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.
# 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

View File

@ -1,9 +1,7 @@
require_relative "values"
module Vm
# 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
# and the ability to assemble itself into the stream
@ -11,7 +9,7 @@ module Vm
# All code is position independant once assembled.
# But for jumps and calls two passes are neccessary.
# The first setting the position, the second assembling
class Code < Value
class Code
# set the position to zero, will have to reset later
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
# stream. During assembly the position is then used to calculate pc relative addresses.
def at address
def link_at address , context
@address = address
end

View File

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

View File

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

View File

@ -1,3 +1,5 @@
require_relative "code"
module Vm
# 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
# Float, Reference , Integer(s) must fit the same registers
class Value
def bit_size
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
# just a base class for data. not sure how this will be usefull (may just have read too much llvm)
class Value < Code
end
class Word < Value
def load reg
Machine.instance.word_load self , reg
end
def compile context
#nothing to do here
end
end
class Unsigned < Word
@ -63,15 +50,9 @@ module Vm
def initialize string
@string = string
end
def at pos
@pos = pos
end
def length
@string.length + 3
end
def compile context
#nothing to do here
end
attr_reader :string
end
@ -84,7 +65,7 @@ module Vm
end
attr_reader :object
def compile context
def compiled context
if object.is_a? StringValue
context.program.add_object object
else

View File

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