introduce constant class and add block to compile signature(wip, work in progress)

This commit is contained in:
Torsten Ruger 2014-05-13 16:24:19 +03:00
parent dd05b30230
commit d7f31e7f39
15 changed files with 145 additions and 140 deletions

View File

@ -31,6 +31,14 @@ module Arm
end end
end end
def integer_less_or_equal left , right
cmp(:left => left , :right => right )
end
def integer_plus left , right
add(:left => left , :right => right )
end
def word_load value , reg def word_load value , reg
raise "not a register :#{reg}:" unless reg.class == Symbol raise "not a register :#{reg}:" unless reg.class == Symbol
mov( :left => reg , :right => value ) mov( :left => reg , :right => value )

View File

@ -39,11 +39,11 @@ module Arm
# Add a string to the string table. Strings are global and constant. So only one copy of each # Add a string to the string table. Strings are global and constant. So only one copy of each
# string exists # string exists
# Internally StringLiterals are created and stored and during assembly written after the blocks # Internally StringConstants are created and stored and during assembly written after the blocks
def add_string str def add_string str
code = @string_table[str] code = @string_table[str]
return code if code return code if code
data = Vm::StringLiteral.new(str) data = Vm::StringConstant.new(str)
@string_table[str] = data @string_table[str] = data
end end

View File

@ -22,7 +22,7 @@ module Arm
#(stays in subclases, while build is overriden to provide different arguments) #(stays in subclases, while build is overriden to provide different arguments)
def do_build(arg) def do_build(arg)
if arg.is_a?(Vm::StringLiteral) if arg.is_a?(Vm::StringConstant)
# do pc relative addressing with the difference to the instuction # do pc relative addressing with the difference to the instuction
# 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute) # 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
arg = Arm::NumLiteral.new( arg.position - self.position - 8 ) arg = Arm::NumLiteral.new( arg.position - self.position - 8 )
@ -31,7 +31,7 @@ module Arm
if( arg.is_a? Fixnum ) #HACK to not have to change the code just now if( arg.is_a? Fixnum ) #HACK to not have to change the code just now
arg = Arm::NumLiteral.new( arg ) arg = Arm::NumLiteral.new( arg )
end end
if( arg.is_a? Vm::Signed ) #HACK to not have to change the code just now if( arg.is_a? Vm::Integer ) #HACK to not have to change the code just now
arg = Arm::NumLiteral.new( arg.value ) arg = Arm::NumLiteral.new( arg.value )
end end
if (arg.is_a?(Arm::NumLiteral)) if (arg.is_a?(Arm::NumLiteral))

View File

@ -50,7 +50,7 @@ module Arm
raise "reference offset too large/small (max 4095) #{arg} #{inspect}" raise "reference offset too large/small (max 4095) #{arg} #{inspect}"
end end
end end
elsif (arg.is_a?(Vm::StringLiteral)) #use pc relative elsif (arg.is_a?(Vm::StringConstant)) #use pc relative
@rn = :pc @rn = :pc
@operand = arg.position - self.position - 8 #stringtable is after code @operand = arg.position - self.position - 8 #stringtable is after code
@add_offset = 1 @add_offset = 1

View File

@ -13,8 +13,8 @@ module Ast
def to_s def to_s
value.to_s value.to_s
end end
def compile context def compile context , into
Vm::Signed.new value Vm::Integer.new value
end end
def attributes def attributes
[:value] [:value]
@ -26,10 +26,10 @@ module Ast
def initialize name def initialize name
@name = name @name = name
end end
def compile context # compiling a variable resolves it.
variable = Vm::Variable.new(@name) # if it wasn't defined, nli is returned
context.locals[@name] = variable def compile context , into
variable context.locals[name]
end end
def inspect def inspect
self.class.name + '.new("' + name + '")' self.class.name + '.new("' + name + '")'
@ -51,8 +51,8 @@ module Ast
self.class.name + '.new("' + string + '")' self.class.name + '.new("' + string + '")'
end end
def compile context def compile context , into
value = Vm::StringLiteral.new(string) value = Vm::StringConstant.new(string)
context.program.add_object value context.program.add_object value
value value
end end

View File

@ -12,7 +12,7 @@ module Ast
def eval def eval
raise "abstract #{self}" raise "abstract #{self}"
end end
def compile context def compile context , into
raise "abstract #{self}" raise "abstract #{self}"
end end
def attributes def attributes

View File

@ -16,27 +16,31 @@ module Ast
def to_s def to_s
"def #{name}( " + params.join(",") + ") \n" + block.join("\n") + "end\n" "def #{name}( " + params.join(",") + ") \n" + block.join("\n") + "end\n"
end end
def compile context def compile context , into
raise self.to_s raise "function does not compile into anything #{self}" if into
parent_locals = context.locals parent_locals = context.locals
context.locals = {} context.locals = {}
args = [] args = []
params.each do |param| params.each_with_index do |param , index|
args << param.compile(context) # making the argument a local arg = param.name
arg_value = Vm::Integer.new(index)
context.locals[arg] = arg_value
args << arg_value
end end
# args = params.collect{|p| Vm::Value.type p.name } function = Vm::Function.new(name , args )
function = Vm::Function.new(name ,args )
context.program.add_function function context.program.add_function function
into = function.entry
block.each do |b| block.each do |b|
compiled = b.compile context compiled = b.compile(context , into)
if compiled.is_a? Vm::Block if compiled.is_a? Vm::Block
into = compiled
he.breaks.loose he.breaks.loose
else else
function.body.add_code compiled function.body.add_code compiled
end end
puts compiled.inspect puts compiled.inspect
end end
context.locals = parent_locals if parent_locals context.locals = parent_locals
function function
end end

View File

@ -7,13 +7,14 @@ module Ast
def initialize name, args def initialize name, args
@name , @args = name , args @name , @args = name , args
end end
def compile context def compile context , into
fun = Vm::FunctionCall.new( name , args.collect{ |a| a.compile(context) } ) params = args.collect{ |a| a.compile(context, into) }
fun.assign_function context fun = Vm::FunctionCall.new( name , params )
fun.load_args fun.load_args into
fun.do_call fun.do_call into
fun fun
end end
def inspect def inspect
self.class.name + ".new(" + name.inspect + ", ["+ self.class.name + ".new(" + name.inspect + ", ["+
args.collect{|m| m.inspect }.join( ",") +"] )" args.collect{|m| m.inspect }.join( ",") +"] )"
@ -41,35 +42,24 @@ module Ast
def to_s def to_s
"#{left} #{operator} #{right}" "#{left} #{operator} #{right}"
end end
def compile context def compile context , into
parent_locals = context.locals r_val = right.compile(context , into)
context.locals = {}
args = []
#assignemnt if operator == "=" # assignemnt
value = @assigned.compile(context) raise "Can only assign variables, not #{left}" unless left.is_a?(NameExpression)
variable = Vm::Variable.new @assignee , :r0 , value context.locals[left.name] = r_val
context.locals[@assignee] = variable return r_val
variable
params.each do |param|
args << param.compile(context) # making the argument a local
end end
# args = params.collect{|p| Vm::Value.type p.name } l_val = left.compile(context , into)
function = Vm::Function.new(name ,args )
context.program.add_function function case operator
block.each do |b| when ">"
compiled = b.compile context code = l_val.less_or_equal r_val
if compiled.is_a? Vm::Block when "+"
he.breaks.loose code = l_val.plus r_val
else else
function.body.add_code compiled raise "unimplemented operator #{operator} #{self}"
end end
puts compiled.inspect
end
context.locals = parent_locals if parent_locals
function
end end
end end
end end

View File

@ -13,6 +13,14 @@ module Ast
def attributes def attributes
[:condition, :body] [:condition, :body]
end end
def compile context , into
cond_val = condition.compile(context , into)
#set up branches for bodies
body.each do |part|
part.compile(context , into )
end
return cond_val
end
end end

View File

@ -8,7 +8,7 @@ module Parser
rule(:bit_and) { str('&') >> space?} rule(:bit_and) { str('&') >> space?}
rule(:bit_or) { str('|') >> space?} rule(:bit_or) { str('|') >> space?}
rule(:greater_equal) { str('>=') >> space?} rule(:greater_equal) { str('>=') >> space?}
rule(:smaller_equal) { str('<=') >> space?} rule(:less_or_equal) { str('<=') >> space?}
rule(:larger) { str('>') >> space?} rule(:larger) { str('>') >> space?}
rule(:smaller) { str('<') >> space?} rule(:smaller) { str('<') >> space?}
rule(:identity) { str('===') >> space?} rule(:identity) { str('===') >> space?}
@ -31,7 +31,7 @@ module Parser
[bit_and, 90, :left], [bit_and, 90, :left],
[bit_or, 90, :right], [bit_or, 90, :right],
[greater_equal, 80, :left], [greater_equal, 80, :left],
[smaller_equal, 80, :left], [less_or_equal, 80, :left],
[larger, 80, :left], [larger, 80, :left],
[smaller, 80, :left], [smaller, 80, :left],
[identity, 70, :right], [identity, 70, :right],

View File

@ -22,7 +22,7 @@ require 'parslet/pattern'
# #
# class Example < Parslet::Transform # class Example < Parslet::Transform
# rule(:string => simple(:x)) { # (1) # rule(:string => simple(:x)) { # (1)
# StringLiteral.new(x) # StringConstant.new(x)
# } # }
# end # end
# #

51
lib/vm/constants.rb Normal file
View File

@ -0,0 +1,51 @@
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 < Value
end
class IntegerConstant < Constant
def init int
@integer = int
end
attr_reader :integer
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 < Constant
attr_reader :string
# currently aligned to 4 (ie padded with 0) and off course 0 at the end
def initialize str
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 + "\x00" * pad
end
def load reg_num
Machine.instance.string_load self , reg_num
end
# the strings length plus padding
def length
string.length
end
# just writing the string
def assemble(io)
io << string
end
end
end

View File

@ -2,31 +2,23 @@ module Vm
# name and args , return # name and args , return
class FunctionCall < Block class FunctionCall < Value
def initialize(name , args) def initialize(name , args)
super(name) @name = name
@args = args @args = args
@function = nil @function = nil
end end
attr_reader :function , :args attr_reader :function , :args , :name
def assign_function context def load_args into
@function = context.program.get_function @name
if @function
raise "error #{self} , #{@function.args.length} != #{args.length}" if @function.arity != args.length
else
@function = context.program.get_or_create_function @name
end
end
def load_args
args.each_with_index do |arg , index| args.each_with_index do |arg , index|
add_code arg.load("r#{index}".to_sym) into.add_code arg.load("r#{index}".to_sym)
end end
end end
def do_call def do_call into
add_code Machine.instance.function_call self into.add_code Machine.instance.function_call self
end end
end end
end end

View File

@ -14,7 +14,7 @@ module Vm
# A Machines main responsibility in the framework is to instantiate Instruction # A Machines main responsibility in the framework is to instantiate Instruction
# Value functions are mapped to machines by concatenating the values class name + the methd name # Value functions are mapped to machines by concatenating the values class name + the methd name
# Example: SignedValue.plus( value ) -> Machine.signed_plus (value ) # Example: IntegerValue.plus( value ) -> Machine.signed_plus (value )
# Also, shortcuts are created to easily instantiate Instruction objects. The "standard" set of instructions # Also, shortcuts are created to easily instantiate Instruction objects. The "standard" set of instructions
# (arm-influenced) provides for normal operations on a register machine, # (arm-influenced) provides for normal operations on a register machine,

View File

@ -21,27 +21,23 @@ module Vm
# just a base class for data. not sure how this will be usefull (may just have read too much llvm) # just a base class for data. not sure how this will be usefull (may just have read too much llvm)
class Value < Code class Value < Code
def initialize value
@value = value
end
attr_reader :value
#naming convention to infer types in kernel functions. Kernel types are basic types, ie see below def type
# self.class
def self.type name
parts = name.split("_")
t = "Basic"
if parts[1]
t = parts[1]
end
t
end end
end end
# This is what it is when we don't know what it is.
# Must be promoted to A Word-Value to to anything
# remembering that our oo machine is typed, no overloading or stuff
class Word < Value class Word < Value
def load reg
Machine.instance.word_load self , reg attr_accessor :register
def initialize reg
register = reg
end end
def length def length
4 4
end end
@ -54,59 +50,15 @@ module Vm
end end
end end
class Signed < Word class Integer < Word
def plus signed
Machine.instance.signed_plus self , signed def less_or_equal right
end Machine.instance.integer_less_or_equal self , right
end end
class Variable < Value def plus right
attr_reader :name , :register Machine.instance.integer_plus self , right
def initialize name , register = nil , val = nil
super(val)
@register = register
@name = name
end
def length
@value.length
end
def assemble io
@value.load @register
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 StringLiteral < Value
# currently aligned to 4 (ie padded with 0) and off course 0 at the end
def initialize(str)
super(str)
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
@value = str + "\x00" * pad
end
def string
@value
end
def load reg_num
Machine.instance.string_load self , reg_num
end
# the strings length plus padding
def length
string.length
end
# just writing the string
def assemble(io)
io << string
end end
end end
end end
require_relative "constants"