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
|
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
|
@ -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]
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ 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)
|
||||||
@ -36,11 +36,6 @@ module Vm
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify
|
|
||||||
@entry.verify
|
|
||||||
@exit.verify
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def add_arg value
|
def add_arg value
|
||||||
# TODO check
|
# TODO check
|
||||||
|
@ -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,55 +49,31 @@ 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
|
|
||||||
def verify
|
|
||||||
@values.each do |funct|
|
|
||||||
funct.verify
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user