hurra, a first test. looks a lot.... but lets not spoil th efeeling
This commit is contained in:
parent
525f9d45c5
commit
16ceb2a60d
@ -4,7 +4,7 @@ module Ast
|
||||
|
||||
class IntegerExpression < Expression
|
||||
# attr_reader :value
|
||||
def compile context
|
||||
def compile binding
|
||||
Virtual::IntegerConstant.new value
|
||||
end
|
||||
end
|
||||
|
@ -2,9 +2,7 @@ module Ast
|
||||
class ExpressionList < Expression
|
||||
# attr_reader :expressions
|
||||
def compile binding
|
||||
expressions.each do |part|
|
||||
expr = part.compile( binding )
|
||||
end
|
||||
expressions.collect { |part| part.compile( binding ) }
|
||||
end
|
||||
end
|
||||
end
|
@ -1,16 +1,11 @@
|
||||
module Vm
|
||||
module Virtual
|
||||
|
||||
# constants are the stuff that you embedd in the program as numbers or strings.
|
||||
# Another way to think about them is as Operands, they have no seperate "identity"
|
||||
# and usually end up embedded in the instructions. ie your basic foo + 4 will encode
|
||||
# the 4 in the instruction opcode. The 4 is not accessible anywhere else.
|
||||
# When it should be usable in other forms, the constant must become a Value first
|
||||
class Constant < Code
|
||||
class Constant < ::Virtual::Object
|
||||
|
||||
end
|
||||
|
||||
# another abstract "marker" class (so we can check for it)
|
||||
# derived classes are Boot/Meta Clas and StringConstant
|
||||
# derived classes are Boot/Meta Class and StringConstant
|
||||
class ObjectConstant < Constant
|
||||
end
|
||||
|
||||
@ -19,11 +14,11 @@ module Vm
|
||||
@integer = int
|
||||
end
|
||||
attr_reader :integer
|
||||
def value
|
||||
@integer
|
||||
def attributes
|
||||
[:integer]
|
||||
end
|
||||
def to_asm
|
||||
@integer.to_s
|
||||
def inspect
|
||||
self.class.name + ".new(#{@integer})"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,9 +1,13 @@
|
||||
module Vm
|
||||
module Virtual
|
||||
|
||||
# Instruction is an abstract for all the code of the object-machine. Derived classe make up the actual functionality
|
||||
# of the machine.
|
||||
# All functions on the machine are captured as instances of instructions
|
||||
#
|
||||
# It is actully the point of the virtual machine layer to express oo functionality in the set of instructions, thus
|
||||
# defining a minimal set of instructions needed to implement oo.
|
||||
|
||||
# This is partly because jumping over this layer and doing in straight in assember was too bi a step
|
||||
class Instruction
|
||||
end
|
||||
|
||||
|
@ -1,9 +1,5 @@
|
||||
module Vm
|
||||
class Integer < Word
|
||||
# needs to be here as Word's constructor is private (to make it abstract)
|
||||
def initialize reg
|
||||
super
|
||||
end
|
||||
class Integer
|
||||
|
||||
def less_or_equal block , right
|
||||
block.cmp( self , right )
|
||||
|
@ -57,3 +57,8 @@ module Virtual
|
||||
end
|
||||
|
||||
require_relative "list"
|
||||
require_relative "instruction"
|
||||
require_relative "value"
|
||||
require_relative "mystery"
|
||||
require_relative "object"
|
||||
require_relative "constants"
|
||||
|
@ -1,8 +1,11 @@
|
||||
module Vm
|
||||
class Mystery < Word
|
||||
# needs to be here as Word's constructor is private (to make it abstract)
|
||||
def initilize reg
|
||||
super
|
||||
module Virtual
|
||||
class Mystery < Value
|
||||
def initilize
|
||||
end
|
||||
|
||||
def as type
|
||||
type.new
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
42
lib/virtual/object.rb
Normal file
42
lib/virtual/object.rb
Normal file
@ -0,0 +1,42 @@
|
||||
module Virtual
|
||||
# our machine is made up of objects, some of which are code, some data
|
||||
#
|
||||
# during compilation objects are module Virtual objects, but during execution they are not scoped
|
||||
#
|
||||
# functions on these classes express their functionality as function objects
|
||||
class Object
|
||||
def initialize
|
||||
@layout = Layout.new([:layout])
|
||||
end
|
||||
def attributes
|
||||
raise "abstract #{self}"
|
||||
end
|
||||
def == other
|
||||
return false unless other.class == self.class
|
||||
attributes.each do |a|
|
||||
left = send(a)
|
||||
right = other.send(a)
|
||||
return false unless left.class == right.class
|
||||
return false unless left == right
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
class Layout < Object
|
||||
def initialize members
|
||||
@members = members
|
||||
end
|
||||
def attributes
|
||||
[:members]
|
||||
end
|
||||
end
|
||||
|
||||
class Class < Object
|
||||
def initialize name , sup = :Object
|
||||
@name = name
|
||||
@super_class = sup
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,26 +1,11 @@
|
||||
module Vm
|
||||
class Reference < Word
|
||||
# needs to be here as Word's constructor is private (to make it abstract)
|
||||
def initialize reg , clazz = nil
|
||||
super(reg)
|
||||
class Reference
|
||||
|
||||
def initialize clazz = nil
|
||||
@clazz = clazz
|
||||
end
|
||||
attr_accessor :clazz
|
||||
|
||||
def load block , right
|
||||
if(right.is_a? IntegerConstant)
|
||||
block.mov( self , right ) #move the value
|
||||
elsif right.is_a? StringConstant
|
||||
block.add( self , right , nil) #move the address, by "adding" to pc, ie pc relative
|
||||
block.mov( Integer.new(self.register.next_reg_use) , right.length ) #and the length HACK TODO
|
||||
elsif right.is_a?(Boot::BootClass) or right.is_a?(Boot::MetaClass)
|
||||
block.add( self , right , nil) #move the address, by "adding" to pc, ie pc relative
|
||||
else
|
||||
raise "unknown #{right.inspect}"
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def at_index block , left , right
|
||||
block.ldr( self , left , right )
|
||||
self
|
||||
|
16
lib/virtual/value.rb
Normal file
16
lib/virtual/value.rb
Normal file
@ -0,0 +1,16 @@
|
||||
module Virtual
|
||||
|
||||
# the virtual machine is implemented in values. Values have types which are represented as classes, but it is still
|
||||
# important to make the distinction. Values are immutable, passed by value and machine word sized.
|
||||
|
||||
# Integer and (Object) References are the main derived classes, but float will come and ...
|
||||
# The Mystery Value has unknown type and has only casting methods. So it must be cast to be useful.
|
||||
class Value
|
||||
def type
|
||||
self.class
|
||||
end
|
||||
private
|
||||
def initialize
|
||||
end
|
||||
end
|
||||
end
|
@ -1,36 +0,0 @@
|
||||
module Vm
|
||||
# Word is an abstract base class for the obvious values, ie those that fit into a register
|
||||
# Marked as abstract by private constructor
|
||||
#
|
||||
# Integer and (Object) References are the main derived classes, but float will come and ...
|
||||
# The Mystery Value has unknown type and has only casting methods. So it must be cast to be useful.
|
||||
# Types are stored at runtime when needed in TYPE_REGISTER (r1 on arm), which is mostly before calls,
|
||||
# so that the called function can do casts / branching correctly
|
||||
class Word < Value
|
||||
attr_accessor :register
|
||||
def register_symbol
|
||||
@register.symbol
|
||||
end
|
||||
def inspect
|
||||
"#{self.class.name} (#{register_symbol})"
|
||||
end
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
def length
|
||||
4
|
||||
end
|
||||
# aka to string
|
||||
def to_asm
|
||||
"#{register_symbol}"
|
||||
end
|
||||
private
|
||||
def initialize reg
|
||||
if reg.is_a? RegisterReference
|
||||
@register = reg
|
||||
else
|
||||
@register = RegisterReference.new(reg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -6,7 +6,7 @@ class TestBasic < MiniTest::Test
|
||||
|
||||
def test_number
|
||||
@string_input = '42 '
|
||||
@output = Ast::IntegerExpression.new(42)
|
||||
@output = [Virtual::IntegerConstant.new(42)]
|
||||
check
|
||||
end
|
||||
|
||||
|
@ -12,9 +12,8 @@ module VirtualHelper
|
||||
syntax = parser.parse_with_debug(@string_input)
|
||||
parts = Parser::Transform.new.apply(syntax)
|
||||
machine = Virtual::Machine.new
|
||||
puts parts.class.inspect
|
||||
parts.compile(machine.bindings)
|
||||
|
||||
expressions = parts.compile(machine.bindings)
|
||||
assert_equal @output , expressions
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user