copies of the old code to start the new layer
This commit is contained in:
parent
9b39a3a816
commit
e41bb8027d
10
lib/ast/all.rb
Normal file
10
lib/ast/all.rb
Normal file
@ -0,0 +1,10 @@
|
||||
require "ast/expression"
|
||||
require_relative "basic_expressions"
|
||||
require_relative "call_site_expression"
|
||||
require_relative "compound_expressions"
|
||||
require_relative "if_expression"
|
||||
require_relative "function_expression"
|
||||
require_relative "module_expression"
|
||||
require_relative "operator_expressions"
|
||||
require_relative "return_expression"
|
||||
require_relative "while_expression"
|
42
lib/ast/basic_expressions.rb
Normal file
42
lib/ast/basic_expressions.rb
Normal file
@ -0,0 +1,42 @@
|
||||
# collection of the simple ones, int and strings and such
|
||||
|
||||
module Ast
|
||||
|
||||
class IntegerExpression < Expression
|
||||
# attr_reader :value
|
||||
def compile context
|
||||
Vm::IntegerConstant.new value
|
||||
end
|
||||
end
|
||||
|
||||
class NameExpression < Expression
|
||||
# attr_reader :name
|
||||
|
||||
# compiling a variable resolves it. if it wasn't defined, raise an exception
|
||||
def compile context
|
||||
raise "Undefined variable #{name}, defined locals #{context.locals.keys.join('-')}" unless context.locals.has_key?(name)
|
||||
context.locals[name]
|
||||
end
|
||||
end
|
||||
|
||||
class ModuleName < NameExpression
|
||||
|
||||
def compile context
|
||||
clazz = context.object_space.get_or_create_class name
|
||||
raise "uups #{clazz}.#{name}" unless clazz
|
||||
#class qualifier, means call from metaclass
|
||||
clazz = clazz.meta_class
|
||||
puts "CLAZZ #{clazz}"
|
||||
clazz
|
||||
end
|
||||
end
|
||||
|
||||
class StringExpression < Expression
|
||||
# attr_reader :string
|
||||
def compile context
|
||||
value = Vm::StringConstant.new(string)
|
||||
context.object_space.add_object value
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
59
lib/ast/call_site_expression.rb
Normal file
59
lib/ast/call_site_expression.rb
Normal file
@ -0,0 +1,59 @@
|
||||
module Ast
|
||||
# assignment, like operators are really function calls
|
||||
|
||||
class CallSiteExpression < Expression
|
||||
# attr_reader :name, :args , :receiver
|
||||
@@counter = 0
|
||||
def compile context
|
||||
into = context.function
|
||||
params = args.collect{ |a| a.compile(context) }
|
||||
puts "compiling receiver #{receiver} (call #{name})"
|
||||
if receiver.is_a? ModuleName
|
||||
clazz = context.object_space.get_or_create_class receiver.name
|
||||
value_receiver = clazz.meta_class
|
||||
function = value_receiver.resolve_function name
|
||||
elsif receiver.is_a?(StringExpression) or receiver.is_a?(IntegerExpression)
|
||||
#TODO obviously the class is wrong, but you gotta start somewhere
|
||||
clazz = context.object_space.get_or_create_class :Object
|
||||
function = clazz.resolve_function name
|
||||
value_receiver = receiver.compile(context)
|
||||
elsif receiver.is_a?(NameExpression)
|
||||
if(receiver.name == :self)
|
||||
function = context.current_class.resolve_function(name)
|
||||
value_receiver = Vm::Integer.new(Vm::RegisterMachine.instance.receiver_register)
|
||||
else
|
||||
value_receiver = receiver.compile(context)
|
||||
# TODO HACK warning: should determine class dynamically
|
||||
function = context.current_class.resolve_function(name)
|
||||
end
|
||||
elsif receiver.is_a? VariableExpression
|
||||
value_receiver = receiver.compile(context)
|
||||
function = context.current_class.resolve_function(name)
|
||||
else
|
||||
#This , how does one say nowadays, smells. Smells of unused polymorphism actually
|
||||
raise "Not sure this is possible, but never good to leave elses open #{receiver} #{receiver.class}"
|
||||
end
|
||||
raise "No such method error #{inspect}" if (function.nil?)
|
||||
raise "No receiver error #{inspect}:#{receiver}" if (value_receiver.nil?)
|
||||
call = Vm::CallSite.new( name , value_receiver , params , function)
|
||||
current_function = context.function
|
||||
into.push([]) unless current_function.nil?
|
||||
call.load_args into
|
||||
call.do_call into
|
||||
|
||||
after = into.new_block("#{name}#{@@counter+=1}")
|
||||
into.insert_at after
|
||||
into.pop([]) unless current_function.nil?
|
||||
function.return_type
|
||||
end
|
||||
end
|
||||
|
||||
class VariableExpression < CallSiteExpression
|
||||
# super( :_get_instance_variable , [StringExpression.new(name)] )
|
||||
def make_setter
|
||||
@name = :_set_instance_variable
|
||||
@args << StringExpression.new("value")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
20
lib/ast/compound_expressions.rb
Normal file
20
lib/ast/compound_expressions.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Ast
|
||||
|
||||
class ArrayExpression < Expression
|
||||
# attr_reader :values
|
||||
def compile context
|
||||
to.do
|
||||
end
|
||||
end
|
||||
class AssociationExpression < Expression
|
||||
# attr_reader :key , :value
|
||||
def compile context
|
||||
to.do
|
||||
end
|
||||
end
|
||||
class HashExpression < ArrayExpression
|
||||
def compile context
|
||||
to.do
|
||||
end
|
||||
end
|
||||
end
|
51
lib/ast/function_expression.rb
Normal file
51
lib/ast/function_expression.rb
Normal file
@ -0,0 +1,51 @@
|
||||
module Ast
|
||||
class FunctionExpression < Expression
|
||||
# attr_reader :name, :params, :body , :receiver
|
||||
def compile context
|
||||
args = []
|
||||
locals = {}
|
||||
params.each_with_index do |param , index|
|
||||
arg = param.name
|
||||
register = Vm::RegisterReference.new(Vm::RegisterMachine.instance.receiver_register).next_reg_use(index + 1)
|
||||
arg_value = Vm::Integer.new(register)
|
||||
locals[arg] = arg_value
|
||||
args << arg_value
|
||||
end
|
||||
# class depends on receiver
|
||||
me = Vm::Integer.new( Vm::RegisterMachine.instance.receiver_register )
|
||||
if receiver.nil?
|
||||
clazz = context.current_class
|
||||
else
|
||||
c = context.object_space.get_or_create_class receiver.name.to_sym
|
||||
clazz = c.meta_class
|
||||
end
|
||||
|
||||
function = Vm::Function.new(name , me , args )
|
||||
clazz.add_function function
|
||||
|
||||
parent_locals = context.locals
|
||||
parent_function = context.function
|
||||
context.locals = locals
|
||||
context.function = function
|
||||
|
||||
last_compiled = nil
|
||||
body.each do |b|
|
||||
puts "compiling in function #{b}"
|
||||
last_compiled = b.compile(context)
|
||||
raise "alarm #{last_compiled} \n #{b}" unless last_compiled.is_a? Vm::Word
|
||||
end
|
||||
|
||||
return_reg = Vm::Integer.new(Vm::RegisterMachine.instance.return_register)
|
||||
if last_compiled.is_a?(Vm::IntegerConstant) or last_compiled.is_a?(Vm::ObjectConstant)
|
||||
return_reg.load function , last_compiled if last_compiled.register_symbol != return_reg.register_symbol
|
||||
else
|
||||
return_reg.move( function, last_compiled ) if last_compiled.register_symbol != return_reg.register_symbol
|
||||
end
|
||||
function.set_return return_reg
|
||||
|
||||
context.locals = parent_locals
|
||||
context.function = parent_function
|
||||
function
|
||||
end
|
||||
end
|
||||
end
|
41
lib/ast/if_expression.rb
Normal file
41
lib/ast/if_expression.rb
Normal file
@ -0,0 +1,41 @@
|
||||
module Ast
|
||||
class IfExpression < Expression
|
||||
# attr_reader :cond, :if_true, :if_false
|
||||
def compile context
|
||||
f = context.function
|
||||
# to execute the logic as the if states it, the blocks are the other way around
|
||||
# so we can the jump over the else if true ,and the else joins unconditionally after the true_block
|
||||
merge_block = f.new_block "if_merge"
|
||||
true_block = f.new_block "if_true"
|
||||
false_block = f.new_block "if_false"
|
||||
|
||||
puts "compiling if condition #{cond}"
|
||||
cond_val = cond.compile(context)
|
||||
unless cond_val.is_a? Vm::BranchCondition
|
||||
cond_val = cond_val.is_true? f
|
||||
end
|
||||
f.b true_block , condition_code: cond_val.operator
|
||||
f.insertion_point.branch = true_block
|
||||
|
||||
f.insert_at false_block
|
||||
if_false.each do |part|
|
||||
puts "compiling in if false #{part}"
|
||||
last = part.compile(context )
|
||||
end
|
||||
f.b merge_block
|
||||
f.insertion_point.branch = false_block
|
||||
|
||||
f.insert_at true_block
|
||||
last = nil
|
||||
if_true.each do |part|
|
||||
puts "compiling in if true #{part}"
|
||||
last = part.compile(context )
|
||||
end
|
||||
|
||||
puts "compiled if: end"
|
||||
f.insert_at merge_block
|
||||
|
||||
return last
|
||||
end
|
||||
end
|
||||
end
|
24
lib/ast/module_expression.rb
Normal file
24
lib/ast/module_expression.rb
Normal file
@ -0,0 +1,24 @@
|
||||
module Ast
|
||||
class ModuleExpression < Expression
|
||||
# attr_reader :name ,:expressions
|
||||
def compile context
|
||||
clazz = context.object_space.get_or_create_class name
|
||||
puts "Created class #{clazz.name.inspect}"
|
||||
context.current_class = clazz
|
||||
expressions.each do |expression|
|
||||
# check if it's a function definition and add
|
||||
# if not, execute it, but that does means we should be in crystal (executable), not ruby. ie throw an error for now
|
||||
raise "only functions for now #{expression.inspect}" unless expression.is_a? Ast::FunctionExpression
|
||||
puts "compiling expression #{expression}"
|
||||
expression_value = expression.compile(context )
|
||||
#puts "compiled expression #{expression_value.inspect}"
|
||||
end
|
||||
|
||||
return clazz
|
||||
end
|
||||
end
|
||||
|
||||
class ClassExpression < ModuleExpression
|
||||
|
||||
end
|
||||
end
|
56
lib/ast/operator_expressions.rb
Normal file
56
lib/ast/operator_expressions.rb
Normal file
@ -0,0 +1,56 @@
|
||||
module Ast
|
||||
class OperatorExpression < Expression
|
||||
# attr_reader :operator, :left, :right
|
||||
def compile context
|
||||
into = context.function
|
||||
puts "compiling operator #{to_s}"
|
||||
r_val = right.compile(context)
|
||||
#puts "compiled right #{r_val.inspect}"
|
||||
if operator == "=" # assignment, value based
|
||||
if(left.is_a? VariableExpression)
|
||||
left.make_setter
|
||||
l_val = left.compile(context)
|
||||
elsif left.is_a?(NameExpression)
|
||||
puts context.inspect unless context.locals
|
||||
l_val = context.locals[left.name]
|
||||
if( l_val ) #variable existed, move data there
|
||||
l_val = l_val.move( into , r_val)
|
||||
else
|
||||
l_val = context.function.new_local.move( into , r_val )
|
||||
end
|
||||
context.locals[left.name] = l_val
|
||||
else
|
||||
raise "Can only assign variables, not #{left}"
|
||||
end
|
||||
return l_val
|
||||
end
|
||||
|
||||
l_val = left.compile(context)
|
||||
case operator
|
||||
when ">"
|
||||
code = l_val.greater_than into , r_val
|
||||
when "<"
|
||||
code = l_val.less_than into , r_val
|
||||
when ">="
|
||||
code = l_val.greater_or_equal into , r_val
|
||||
when "<="
|
||||
code = l_val.less_or_equal into , r_val
|
||||
when "=="
|
||||
code = l_val.equals into , r_val
|
||||
when "+"
|
||||
res = context.function.new_local
|
||||
into.add res , l_val , r_val
|
||||
code = res
|
||||
when "-"
|
||||
res = context.function.new_local
|
||||
code = res.minus into , l_val , r_val
|
||||
when "<<"
|
||||
res = context.function.new_local
|
||||
code = res.left_shift into , l_val , r_val
|
||||
else
|
||||
raise "unimplemented operator #{operator} #{self}"
|
||||
end
|
||||
code
|
||||
end
|
||||
end
|
||||
end
|
22
lib/ast/return_expression.rb
Normal file
22
lib/ast/return_expression.rb
Normal file
@ -0,0 +1,22 @@
|
||||
module Ast
|
||||
class ReturnExpression < Expression
|
||||
# attr_reader :expression
|
||||
def compile context
|
||||
into = context.function
|
||||
puts "compiling return expression #{expression}, now return in return_regsiter"
|
||||
expression_value = expression.compile(context)
|
||||
# copied from function expression: TODO make function
|
||||
|
||||
return_reg = Vm::Integer.new(Vm::RegisterMachine.instance.return_register)
|
||||
if expression_value.is_a?(Vm::IntegerConstant) or expression_value.is_a?(Vm::ObjectConstant)
|
||||
return_reg.load into , expression_value
|
||||
else
|
||||
return_reg.move( into, expression_value ) if expression_value.register_symbol != return_reg.register_symbol
|
||||
end
|
||||
#function.set_return return_reg
|
||||
return return_reg
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
31
lib/ast/while_expression.rb
Normal file
31
lib/ast/while_expression.rb
Normal file
@ -0,0 +1,31 @@
|
||||
module Ast
|
||||
class WhileExpression < Expression
|
||||
# attr_reader :condition, :body
|
||||
def compile context
|
||||
into = context.function
|
||||
ret = into.new_block "while_end"
|
||||
while_block = into.new_block "while_start"
|
||||
into.insert_at while_block
|
||||
|
||||
puts "compiling while condition #{condition}"
|
||||
cond_val = condition.compile(context)
|
||||
into.b ret , condition_code: cond_val.not_operator
|
||||
into.insertion_point.branch = ret
|
||||
|
||||
last = nil
|
||||
|
||||
body.each do |part|
|
||||
puts "compiling in while #{part}"
|
||||
last = part.compile(context)
|
||||
end
|
||||
into.b while_block
|
||||
into.insertion_point.branch = while_block
|
||||
|
||||
puts "compile while end"
|
||||
into.insert_at ret
|
||||
return last
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
62
lib/neumann/constants.rb
Normal file
62
lib/neumann/constants.rb
Normal file
@ -0,0 +1,62 @@
|
||||
module Vm
|
||||
|
||||
# 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
|
||||
|
||||
end
|
||||
|
||||
# another abstract "marker" class (so we can check for it)
|
||||
# derived classes are Boot/Meta Clas and StringConstant
|
||||
class ObjectConstant < Constant
|
||||
end
|
||||
|
||||
class IntegerConstant < Constant
|
||||
def initialize int
|
||||
@integer = int
|
||||
end
|
||||
attr_reader :integer
|
||||
def value
|
||||
@integer
|
||||
end
|
||||
def to_asm
|
||||
@integer.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# The name really says it all.
|
||||
# The only interesting thing is storage.
|
||||
# Currently string are stored "inline" , ie in the code segment.
|
||||
# Mainly because that works an i aint no elf expert.
|
||||
|
||||
class StringConstant < ObjectConstant
|
||||
attr_reader :string
|
||||
# currently aligned to 4 (ie padded with 0) and off course 0 at the end
|
||||
def initialize str
|
||||
str = str.to_s if str.is_a? Symbol
|
||||
length = str.length
|
||||
# rounding up to the next 4 (always adding one for zero pad)
|
||||
pad = ((length / 4 ) + 1 ) * 4 - length
|
||||
raise "#{pad} #{self}" unless pad >= 1
|
||||
@string = str + " " * pad
|
||||
end
|
||||
|
||||
def result= value
|
||||
class_for(MoveInstruction).new(value , self , :opcode => :mov)
|
||||
end
|
||||
|
||||
# the strings length plus padding
|
||||
def length
|
||||
string.length
|
||||
end
|
||||
|
||||
# just writing the string
|
||||
def assemble(io)
|
||||
io << string
|
||||
end
|
||||
end
|
||||
|
||||
end
|
51
lib/neumann/integer.rb
Normal file
51
lib/neumann/integer.rb
Normal file
@ -0,0 +1,51 @@
|
||||
module Vm
|
||||
class Integer < Word
|
||||
# needs to be here as Word's constructor is private (to make it abstract)
|
||||
def initialize reg
|
||||
super
|
||||
end
|
||||
|
||||
def less_or_equal block , right
|
||||
block.cmp( self , right )
|
||||
Vm::BranchCondition.new :le
|
||||
end
|
||||
def greater_or_equal block , right
|
||||
block.cmp( self , right )
|
||||
Vm::BranchCondition.new :ge
|
||||
end
|
||||
def greater_than block , right
|
||||
block.cmp( self , right )
|
||||
Vm::BranchCondition.new :gt
|
||||
end
|
||||
def less_than block , right
|
||||
block.cmp( self , right )
|
||||
Vm::BranchCondition.new :lt
|
||||
end
|
||||
def plus block , first , right
|
||||
block.add( self , left , right )
|
||||
self
|
||||
end
|
||||
def minus block , left , right
|
||||
block.sub( self , left , right )
|
||||
self
|
||||
end
|
||||
def left_shift block , left , right
|
||||
block.mov( self , left , shift_lsr: right )
|
||||
self
|
||||
end
|
||||
def equals block , right
|
||||
block.cmp( self , right )
|
||||
Vm::BranchCondition.new :eq
|
||||
end
|
||||
|
||||
def is_true? function
|
||||
function.cmp( self , 0 )
|
||||
Vm::BranchCondition.new :ne
|
||||
end
|
||||
|
||||
def move block , right
|
||||
block.mov( self , right )
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
8
lib/neumann/mystery.rb
Normal file
8
lib/neumann/mystery.rb
Normal file
@ -0,0 +1,8 @@
|
||||
module Vm
|
||||
class Mystery < Word
|
||||
# needs to be here as Word's constructor is private (to make it abstract)
|
||||
def initilize reg
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
30
lib/neumann/reference.rb
Normal file
30
lib/neumann/reference.rb
Normal file
@ -0,0 +1,30 @@
|
||||
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)
|
||||
@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
|
||||
end
|
||||
|
||||
end
|
||||
end
|
36
lib/neumann/word.rb
Normal file
36
lib/neumann/word.rb
Normal file
@ -0,0 +1,36 @@
|
||||
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
|
10
lib/vm/instruction.rb
Normal file
10
lib/vm/instruction.rb
Normal file
@ -0,0 +1,10 @@
|
||||
module Vm
|
||||
|
||||
# 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
|
||||
#
|
||||
class Instruction
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user