getting better, but somethings off

This commit is contained in:
Torsten Ruger 2014-05-05 09:35:40 +03:00
parent a61170942f
commit 7c0aa8ae7d
10 changed files with 138 additions and 51 deletions

View File

@ -30,11 +30,14 @@ module Arm
end end
end end
def word_load value def word_load value , reg
"word" e = Vm::Block.new("load_#{value}")
e.add_code( MoveInstruction.new( :left => reg , :right => value ) )
end end
def function_call call_value def function_call 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 ))
end end
def main_entry def main_entry
@ -47,8 +50,8 @@ module Arm
end end
def syscall num def syscall num
e = Vm::Block.new("syscall") e = Vm::Block.new("syscall")
e.add_code( MoveInstruction.new( 7 , num ) ) e.add_code( MoveInstruction.new( :left => 7 , :right => num ) )
e.add_code( CallInstruction.new( :swi ) ) e.add_code( CallInstruction.new( :swi => 0 ) )
e e
end end
end end

View File

@ -7,7 +7,7 @@ module Vm
# Blocks must end in control instructions (jump/call/return). # Blocks must end in control instructions (jump/call/return).
# And the only valid argument for a jump is a Block # And the only valid argument for a jump is a Block
# Blocks for a double linked list so one can traverse back and forth # Blocks form a linked list
# There are four ways for a block to get data (to work on) # There are four ways for a block to get data (to work on)
# - hard coded constants (embedded in code) # - hard coded constants (embedded in code)
@ -16,43 +16,70 @@ 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 positioning)
class Block < Code class Block < Code
def initialize(name) def initialize(name)
super() super()
@name = name.to_sym @name = name.to_sym
@next = nil @next = nil
@previous = nil @values = []
@codes = [] @codes = []
end end
attr_reader :name , :previous , :next attr_reader :name , :next , :codes , :values
def verify def verify
end end
def add_value v
@values << v
end
def length
@codes.inject(0) {| sum , item | sum + item.length}
end
def add_code(kode) def add_code(kode)
kode.at(@position) kode.at(@position)
length = kode.length length = kode.length
puts "length #{length}"
@position += length @position += length
@codes << kode @codes << kode
self
end end
def compile context
@values.each do |value|
value.compile(context)
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
# and at least one to do the calculation # and at least one to do the calculation
def next block def next block
block.previous = self @next = block
self.next = block
end end
end end
end end

View File

@ -13,9 +13,9 @@ module Vm
# The first setting the position, the second assembling # The first setting the position, the second assembling
class Code < Value class Code < Value
# just sets position to nil, so we can sell that it has not been set # set the position to zero, will have to reset later
def initialize def initialize
@position = nil @position = 0
end end
# the position in the stream. Think of it as an address if you want. The difference is small. # the position in the stream. Think of it as an address if you want. The difference is small.
@ -35,7 +35,7 @@ module Vm
# length for this code in bytes # length for this code in bytes
def length def length
throw "Not implemented #{self}" raise "Not implemented #{self}"
end end
# so currently the interface passes the io (usually string_io) in for the code to assemble itself. # so currently the interface passes the io (usually string_io) in for the code to assemble itself.

View File

@ -14,7 +14,7 @@ module Vm
FunctionCall.new( name , args.collect{ |a| a.to_value } ) FunctionCall.new( name , args.collect{ |a| a.to_value } )
end end
def string_value def string_value
ObjectReference.new( string ) ObjectReference.new( StringValue.new(string) )
end end
end end

View File

@ -10,25 +10,25 @@ module Vm
# but there is no branch created between them, this must be done by the programmer. # but there is no branch created between them, this must be done by the programmer.
class Function < Value class Function < Block
def initialize(name , args = []) def initialize(name , args = [])
super() super(name)
@name = name
@args = args @args = args
@entry = Block.new("entry_#{name}") @entry = Block.new("entry_#{name}")
@exit = Block.new("exit_#{name}") @exit = Block.new("exit_#{name}")
end end
attr_reader :name , :args , :entry , :exit attr_reader :args , :entry , :exit
def arity def arity
@args.length @args.length
end end
def compile function_expression , context def length
arguments = function_expression.args.collect do |arg| @entry.length + @exit.length + super
add_arg arg.to_value end
end
def compile 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)

View File

@ -2,31 +2,37 @@ module Vm
# name and args , return # name and args , return
class FunctionCall < Value class FunctionCall < Block
def initialize(name , args) def initialize(name , args)
super(name)
@name = name @name = name
@args = args @values = args
@function = nil @function = nil
end end
attr_reader :name , :args , :function attr_reader :function
def args
values
end
def compile context def compile context
@function = context.program.get_function @name @function = context.program.get_function @name
if @function if @function
raise "error #{self}" unless function.arity != @args.length raise "error #{self}" unless @function.arity != @values.length
else else
@function = context.program.get_or_create_function @name @function = context.program.get_or_create_function @name
end end
args.each_with_index do |arg , index| args.each_with_index do |arg |
arg.load
arg.compile context arg.compile context
end end
args.each_with_index do |arg , index|
arg.load index
end
#puts "funcall #{self.inspect}" #puts "funcall #{self.inspect}"
self.call self.do_call
end end
def call def do_call
Machine.instance.function_call self Machine.instance.function_call self
end end
end end

View File

@ -10,7 +10,7 @@ module Vm
end end
def self.puts string def self.puts string
# should unwrap from string to char* # should unwrap from string to char*
Machine.instance.puts Machine.instance.puts string
end end
end end
end end

View File

@ -25,19 +25,22 @@ module Vm
Machine.instance = eval("#{machine}::#{machine}Machine").new Machine.instance = eval("#{machine}::#{machine}Machine").new
@context = Context.new(self) @context = Context.new(self)
@functions = []
@objects = [] @objects = []
@entry = Vm::Kernel::start @entry = Vm::Kernel::start
@exit = Vm::Kernel::exit @exit = Vm::Kernel::exit
end end
attr_reader :context attr_reader :context
def functions
@values
end
def add_object o def add_object 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
@functions.detect{ |f| f.name == name } @values.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)
@ -45,21 +48,53 @@ module Vm
fun = get_function name fun = get_function name
unless fun unless fun
fun = Function.new(name) fun = Function.new(name)
@functions << fun @values << fun
end end
fun fun
end end
def compile def compile(context)
super @values.each do |function|
string_table 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 end
def wrap_as_main value def fix_positions
@position = 0
@entry.at( @position )
@position += @entry.length
@values.each do |function|
function.at(@position)
@position += function.length
end
@objects.each do |o|
o.at(@position)
@position += o.length
end
@exit.at( @position )
@position += @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
end end
def verify def verify
@functions.each do |funct| @values.each do |funct|
funct.verify funct.verify
end end
end end

View File

@ -33,8 +33,11 @@ module Vm
end end
class Word < Value class Word < Value
def load def load reg
Machine.instance.word_load self Machine.instance.word_load self , reg
end
def compile context
#nothing to do here
end end
end end
@ -56,6 +59,21 @@ module Vm
class Reference < Word class Reference < Word
end end
class StringValue < Value
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
class MemoryReference < Reference class MemoryReference < Reference
end end
@ -67,7 +85,7 @@ module Vm
attr_reader :object attr_reader :object
def compile context def compile context
if object.is_a? String if object.is_a? StringValue
context.program.add_object object context.program.add_object object
else else
#TODO define object layout more generally and let objects lay themselves out #TODO define object layout more generally and let objects lay themselves out

View File

@ -22,13 +22,11 @@ class TestRunner < MiniTest::Test
tree = Parser::Transform.new.apply(syntax) tree = Parser::Transform.new.apply(syntax)
program = Vm::Program.new "Arm" program = Vm::Program.new "Arm"
expression = tree.to_value value = tree.to_value
compiled = expression.compile( program.context ) program.wrap_as_main value
# do some stuff with mains and what not ?? compiled = program.compile( program.context )
program.wrap_as_main compiled
puts program.to_yaml
program.verify program.verify
# puts program.to_yaml puts program.to_yaml
end end
end end