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

View File

@ -7,7 +7,7 @@ module Vm
# Blocks must end in control instructions (jump/call/return).
# 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)
# - hard coded constants (embedded in code)
@ -16,43 +16,70 @@ 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)
class Block < Code
def initialize(name)
super()
@name = name.to_sym
@next = nil
@previous = nil
@values = []
@codes = []
end
attr_reader :name , :previous , :next
attr_reader :name , :next , :codes , :values
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
puts "length #{length}"
@position += length
@codes << kode
self
end
def compile context
@values.each do |value|
value.compile(context)
end
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
# and at least one to do the calculation
def next block
block.previous = self
self.next = block
@next = block
end
end
end

View File

@ -13,9 +13,9 @@ module Vm
# The first setting the position, the second assembling
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
@position = nil
@position = 0
end
# 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
def length
throw "Not implemented #{self}"
raise "Not implemented #{self}"
end
# 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 } )
end
def string_value
ObjectReference.new( string )
ObjectReference.new( StringValue.new(string) )
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.
class Function < Value
class Function < Block
def initialize(name , args = [])
super()
@name = name
super(name)
@args = args
@entry = Block.new("entry_#{name}")
@exit = Block.new("exit_#{name}")
end
attr_reader :name , :args , :entry , :exit
attr_reader :args , :entry , :exit
def arity
@args.length
end
def compile function_expression , context
arguments = function_expression.args.collect do |arg|
add_arg arg.to_value
def length
@entry.length + @exit.length + super
end
def compile context
function = context.program.get_function(name)
unless function
function = Vm::Kernel.send(name)

View File

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

View File

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

View File

@ -25,19 +25,22 @@ module Vm
Machine.instance = eval("#{machine}::#{machine}Machine").new
@context = Context.new(self)
@functions = []
@objects = []
@entry = Vm::Kernel::start
@exit = Vm::Kernel::exit
end
attr_reader :context
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
@functions.detect{ |f| f.name == name }
@values.detect{ |f| (f.name == name) && (f.class == Function) }
end
# preferred way of creating new functions (also forward declarations, will flag unresolved later)
@ -45,21 +48,53 @@ module Vm
fun = get_function name
unless fun
fun = Function.new(name)
@functions << fun
@values << fun
end
fun
end
def compile
super
string_table
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 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
def verify
@functions.each do |funct|
@values.each do |funct|
funct.verify
end
end

View File

@ -33,8 +33,11 @@ module Vm
end
class Word < Value
def load
Machine.instance.word_load self
def load reg
Machine.instance.word_load self , reg
end
def compile context
#nothing to do here
end
end
@ -56,6 +59,21 @@ module Vm
class Reference < Word
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
end
@ -67,7 +85,7 @@ module Vm
attr_reader :object
def compile context
if object.is_a? String
if object.is_a? StringValue
context.program.add_object object
else
#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)
program = Vm::Program.new "Arm"
expression = tree.to_value
compiled = expression.compile( program.context )
# do some stuff with mains and what not ??
program.wrap_as_main compiled
puts program.to_yaml
value = tree.to_value
program.wrap_as_main value
compiled = program.compile( program.context )
program.verify
# puts program.to_yaml
puts program.to_yaml
end
end