moved all compile to ast, vm does link and assemble. Getting there
This commit is contained in:
parent
99da6f5be3
commit
de66238a9e
@ -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
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user