2014-05-03 14:13:44 +02:00
|
|
|
require_relative "values"
|
|
|
|
|
|
|
|
module Vm
|
|
|
|
|
|
|
|
# Think flowcharts: blocks are the boxes. The smallest unit of linear code
|
|
|
|
|
|
|
|
# Blocks must end in control instructions (jump/call/return).
|
|
|
|
# And the only valid argument for a jump is a Block
|
|
|
|
|
2014-05-05 08:35:40 +02:00
|
|
|
# Blocks form a linked list
|
2014-05-03 14:13:44 +02:00
|
|
|
|
|
|
|
# There are four ways for a block to get data (to work on)
|
|
|
|
# - hard coded constants (embedded in code)
|
|
|
|
# - memory move
|
|
|
|
# - values passed in (from previous blocks. ie local variables)
|
|
|
|
|
|
|
|
# See Value description on how to create code/instructions
|
|
|
|
|
2014-05-05 10:03:43 +02:00
|
|
|
# Codes then get assembled into bytes (after linking)
|
2014-05-05 08:35:40 +02:00
|
|
|
|
2014-05-03 21:18:04 +02:00
|
|
|
class Block < Code
|
2014-05-03 14:13:44 +02:00
|
|
|
|
2014-05-22 12:59:47 +02:00
|
|
|
def initialize(name , function , next_block = nil)
|
2014-05-03 14:13:44 +02:00
|
|
|
super()
|
2014-05-21 15:42:36 +02:00
|
|
|
@function = function
|
2014-05-03 14:13:44 +02:00
|
|
|
@name = name.to_sym
|
2014-05-22 12:59:47 +02:00
|
|
|
@next = next_block
|
2014-05-03 17:51:47 +02:00
|
|
|
@codes = []
|
2014-05-22 15:35:59 +02:00
|
|
|
@insert_at_end = false
|
2014-05-03 14:13:44 +02:00
|
|
|
end
|
|
|
|
|
2014-05-21 15:42:36 +02:00
|
|
|
attr_reader :name , :next , :codes , :function
|
2014-05-03 14:13:44 +02:00
|
|
|
|
2014-05-03 21:18:04 +02:00
|
|
|
def add_code(kode)
|
2014-05-19 14:44:12 +02:00
|
|
|
if kode.is_a? Hash
|
|
|
|
raise "Hack only for 1 element #{inspect} #{kode.inspect}" unless kode.length == 1
|
|
|
|
instruction , result = kode.first
|
2014-05-21 18:06:06 +02:00
|
|
|
instruction.assign result
|
2014-05-19 14:44:12 +02:00
|
|
|
kode = instruction
|
|
|
|
end
|
2014-05-14 09:47:30 +02:00
|
|
|
raise "alarm #{kode}" if kode.is_a? Word
|
2014-05-15 15:54:23 +02:00
|
|
|
raise "alarm #{kode}" unless kode.is_a? Code
|
2014-05-22 15:35:59 +02:00
|
|
|
insert_at.codes << kode
|
2014-05-05 08:35:40 +02:00
|
|
|
self
|
2014-05-03 21:18:04 +02:00
|
|
|
end
|
2014-05-18 09:15:43 +02:00
|
|
|
alias :<< :add_code
|
2014-05-19 14:44:12 +02:00
|
|
|
alias :a :add_code
|
2014-05-03 21:18:04 +02:00
|
|
|
|
2014-05-22 12:59:47 +02:00
|
|
|
# create a new linear block after this block. Linear means there is no brach needed from this one
|
|
|
|
# to the new one. Usually the new one just serves as jump address for a control statement
|
|
|
|
# In code generation (assembly) , new new_block is written after this one, ie zero runtime cost
|
|
|
|
def new_block name
|
|
|
|
new_b = Block.new( name , @function , @next )
|
|
|
|
@next = new_b
|
|
|
|
return new_b
|
2014-05-03 21:18:04 +02:00
|
|
|
end
|
2014-05-22 12:59:47 +02:00
|
|
|
|
2014-05-22 15:35:59 +02:00
|
|
|
# when control structures create new blocks (with new_block) control continues at the end of
|
|
|
|
# the chain of blocks that was created.
|
|
|
|
# the code using _this block should be unaware of the complexity of the block and just keep using this
|
|
|
|
# block as before (ie in a linear way)
|
|
|
|
# this switches that behaviour on, ie code is hence after inserted at the end of the last block
|
|
|
|
def insert_at_end
|
|
|
|
@insert_at_end = true
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
# returns the point at which code is added. See insert_at_end for explanation. Usually self, but...
|
|
|
|
def insert_at
|
|
|
|
return self unless @insert_at_end
|
|
|
|
@next ? @next.insert_at : self
|
|
|
|
end
|
|
|
|
|
2014-05-20 10:03:18 +02:00
|
|
|
# to use the assignment syntax (see method_missing) the scope must be set, so variables can be resolved
|
|
|
|
# The scope you set should be a binding (literally, the kernel.binding)
|
|
|
|
# The function return the block, so it can be chained into an assignment
|
|
|
|
# Example (coding a function ) and having variable int defined
|
|
|
|
# b = function.body.scope(binding)
|
|
|
|
# b.int = 5 will create a mov instruction to set the register that int points to
|
|
|
|
def scope where
|
|
|
|
@scope = where
|
|
|
|
self
|
2014-05-20 09:29:08 +02:00
|
|
|
end
|
2014-05-05 08:35:40 +02:00
|
|
|
|
2014-05-20 10:03:18 +02:00
|
|
|
# sugar to create instructions easily. Actually just got double sweet with two versions:
|
|
|
|
# 1 for any method that ends in = we evaluate the method name in the current scope (see scope())
|
|
|
|
# for the result we call assign with the right value. The resulting instruction is added to
|
|
|
|
# the block.
|
|
|
|
# Thus we emulate assignment,
|
|
|
|
# Example: block b
|
|
|
|
# b.variable = value looks like what it does, but actually generates
|
|
|
|
# an instruction for the block (mov or add)
|
|
|
|
#
|
2014-05-21 18:43:46 +02:00
|
|
|
# 2- any other method will be passed on to the RegisterMachine and the result added to the block
|
2014-05-20 10:03:18 +02:00
|
|
|
# With this trick we can write what looks like assembler,
|
|
|
|
# Example b.instance_eval
|
|
|
|
# mov( r1 , r2 )
|
|
|
|
# add( r1 , r2 , 4)
|
|
|
|
# end
|
|
|
|
# mov and add will be called on Machine and generate Inststuction that are then added
|
|
|
|
# to the block
|
2014-05-08 13:14:15 +02:00
|
|
|
def method_missing(meth, *args, &block)
|
2014-05-21 11:44:36 +02:00
|
|
|
var = meth.to_s[0 ... -1]
|
|
|
|
if( args.length == 1) and ( meth.to_s[-1] == "=" )
|
|
|
|
if @scope.local_variable_defined? var.to_sym
|
|
|
|
l_val = @scope.local_variable_get var.to_sym
|
|
|
|
return add_code l_val.assign(args[0])
|
|
|
|
else
|
|
|
|
return super
|
|
|
|
end
|
2014-05-20 09:29:08 +02:00
|
|
|
end
|
2014-05-21 18:43:46 +02:00
|
|
|
add_code RegisterMachine.instance.send(meth , *args)
|
2014-05-08 13:14:15 +02:00
|
|
|
end
|
|
|
|
|
2014-05-22 13:18:22 +02:00
|
|
|
# Code interface follows. Note position is inheitted as is from Code
|
2014-05-03 14:13:44 +02:00
|
|
|
|
2014-05-22 13:18:22 +02:00
|
|
|
# length of the block is the length of it's codes, plus any next block (ie no branch follower)
|
|
|
|
# Note, the next is in effect a linked list and as such may have many blocks behind it.
|
|
|
|
def length
|
|
|
|
cods = @codes.inject(0) {| sum , item | sum + item.length}
|
|
|
|
cods += @next.length if @next
|
|
|
|
cods
|
|
|
|
end
|
|
|
|
|
|
|
|
# to link we link the codes (instructions), plus any next in line block (non- branched)
|
|
|
|
def link_at pos , context
|
|
|
|
super(pos , context)
|
|
|
|
@codes.each do |code|
|
|
|
|
code.link_at(pos , context)
|
|
|
|
pos += code.length
|
|
|
|
end
|
|
|
|
if @next
|
|
|
|
@next.link_at pos , context
|
|
|
|
pos += @next.length
|
|
|
|
end
|
|
|
|
pos
|
|
|
|
end
|
|
|
|
|
|
|
|
# assemble the codes (instructions) and any next in line block
|
|
|
|
def assemble(io)
|
|
|
|
@codes.each do |obj|
|
|
|
|
obj.assemble io
|
|
|
|
end
|
|
|
|
@next.assemble(io) if @next
|
|
|
|
end
|
|
|
|
end
|
2014-05-03 14:13:44 +02:00
|
|
|
end
|