adding the blocks to virtual machine and store instructions in array not list
This commit is contained in:
@ -1,110 +0,0 @@
|
||||
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
|
||||
|
||||
# Blocks form a linked list
|
||||
|
||||
# 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
|
||||
|
||||
# Codes then get assembled into bytes (after linking)
|
||||
|
||||
class Block < Code
|
||||
|
||||
def initialize(name , function , next_block )
|
||||
super()
|
||||
@function = function
|
||||
@name = name.to_sym
|
||||
@next = next_block
|
||||
@branch = nil
|
||||
@codes = []
|
||||
# keeping track of register usage, left (assigns) or right (uses)
|
||||
@assigns = []
|
||||
@uses = []
|
||||
end
|
||||
|
||||
attr_reader :name , :next , :codes , :function , :assigns , :uses
|
||||
attr_accessor :branch
|
||||
|
||||
def reachable ret = []
|
||||
add_next ret
|
||||
add_branch ret
|
||||
ret
|
||||
end
|
||||
|
||||
def add_code kode
|
||||
kode.assigns.each { |a| (@assigns << a) unless @assigns.include?(a) }
|
||||
kode.uses.each { |use| (@uses << use) unless (@assigns.include?(use) or @uses.include?(use)) }
|
||||
#puts "IN ADD #{name}#{uses}"
|
||||
@codes << kode
|
||||
end
|
||||
|
||||
def set_next next_b
|
||||
@next = next_b
|
||||
end
|
||||
|
||||
# returns if this is a block that ends in a call (and thus needs local variable handling)
|
||||
def call_block?
|
||||
return false unless codes.last.is_a?(CallInstruction)
|
||||
return false unless codes.last.opcode == :call
|
||||
codes.dup.reverse.find{ |c| c.is_a? StackInstruction }
|
||||
end
|
||||
|
||||
# Code interface follows. Note position is inheitted as is from Code
|
||||
|
||||
# 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
|
||||
|
||||
private
|
||||
# helper for determining reachable blocks
|
||||
def add_next ret
|
||||
return if @next.nil?
|
||||
return if ret.include? @next
|
||||
ret << @next
|
||||
@next.reachable ret
|
||||
end
|
||||
# helper for determining reachable blocks
|
||||
def add_branch ret
|
||||
return if @branch.nil?
|
||||
return if ret.include? @branch
|
||||
ret << @branch
|
||||
@branch.reachable ret
|
||||
end
|
||||
end
|
||||
end
|
@ -1,64 +0,0 @@
|
||||
module Vm
|
||||
#Plock (Proc-Block) is mostly a Block but also somewhat Proc-ish: A Block that carries data.
|
||||
#
|
||||
# Data in a Block is usefull in the same way data in objects is. Plocks being otherwise just code.
|
||||
#
|
||||
# But the concept is not quite straigtforwrd: If one think of an Plock enbedded in a normal function,
|
||||
# the a data in the Plock would be static data. In OO terms this comes quite close to a Proc, if the data is the local
|
||||
# variables. Quite possibly they shall be used to implement procs, but that is not the direction now.
|
||||
#
|
||||
# For now we use Plocks behaind the scenes as it were. In the code that you never see, method invocation mainly.
|
||||
#
|
||||
# In terms of implementation the Plock is a Block with data (Not too much data, mainly a couple of references).
|
||||
# The block writes it's instructions as normal, but a jump is inserted as the last instruction. The jump is to the
|
||||
# next block, over the data that is inserted after the block code (and so before the next)
|
||||
#
|
||||
# It follows that Plocks should be linear blocks.
|
||||
class Plock < Block
|
||||
|
||||
def initialize(name , function , next_block )
|
||||
super
|
||||
@data = []
|
||||
@branch_code = RegisterMachine.instance.b next_block
|
||||
end
|
||||
|
||||
def set_next next_b
|
||||
super
|
||||
@branch_code = RegisterMachine.instance.b next_block
|
||||
end
|
||||
|
||||
# Data gets assembled after functions
|
||||
def add_data o
|
||||
return if @objects.include? o
|
||||
raise "must be derived from Code #{o.inspect}" unless o.is_a? Vm::Code
|
||||
@data << o # TODO check type , no basic values allowed (must be wrapped)
|
||||
end
|
||||
|
||||
# Code interface follows. Note position is inheitted as is from Code
|
||||
|
||||
# length of the Plock is the length of the block, plus the branch, plus data.
|
||||
def length
|
||||
len = @data.inject(super) {| sum , item | sum + item.length}
|
||||
len + @branch_code.length
|
||||
end
|
||||
|
||||
# again, super + branch plus data
|
||||
def link_at pos , context
|
||||
super(pos , context)
|
||||
@branch_code.link_at pos , context
|
||||
@data.each do |code|
|
||||
code.link_at(pos , context)
|
||||
pos += code.length
|
||||
end
|
||||
end
|
||||
|
||||
# again, super + branch plus data
|
||||
def assemble(io)
|
||||
super
|
||||
@branch_code.assemble(io)
|
||||
@data.each do |obj|
|
||||
obj.assemble io
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user