getting better, but somethings off
This commit is contained in:
parent
a61170942f
commit
7c0aa8ae7d
@ -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
|
||||
|
@ -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
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user