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