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
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
raise "not a register :#{reg}:" unless reg.class == Symbol
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
# 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
code = @string_table[str]
return code if code
data = Vm::StringLiteral.new(str)
data = Vm::StringConstant.new(str)
@string_table[str] = data
end

View File

@ -22,7 +22,7 @@ module Arm
#(stays in subclases, while build is overriden to provide different arguments)
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
# 8 is for the funny pipeline adjustment (ie oc pointing to fetch and not execute)
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
arg = Arm::NumLiteral.new( arg )
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 )
end
if (arg.is_a?(Arm::NumLiteral))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,6 +13,14 @@ module Ast
def attributes
[:condition, :body]
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

View File

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

View File

@ -22,7 +22,7 @@ require 'parslet/pattern'
#
# class Example < Parslet::Transform
# rule(:string => simple(:x)) { # (1)
# StringLiteral.new(x)
# StringConstant.new(x)
# }
# 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
class FunctionCall < Block
class FunctionCall < Value
def initialize(name , args)
super(name)
@name = name
@args = args
@function = nil
end
attr_reader :function , :args
attr_reader :function , :args , :name
def assign_function context
@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
def load_args into
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
def do_call
add_code Machine.instance.function_call self
def do_call into
into.add_code Machine.instance.function_call self
end
end
end

View File

@ -14,7 +14,7 @@ module Vm
# 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
# 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
# (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)
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 self.type name
parts = name.split("_")
t = "Basic"
if parts[1]
t = parts[1]
end
t
def type
self.class
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
def load reg
Machine.instance.word_load self , reg
attr_accessor :register
def initialize reg
register = reg
end
def length
4
end
@ -54,59 +50,15 @@ module Vm
end
end
class Signed < Word
def plus signed
Machine.instance.signed_plus self , signed
end
class Integer < Word
def less_or_equal right
Machine.instance.integer_less_or_equal self , right
end
class Variable < Value
attr_reader :name , :register
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
def plus right
Machine.instance.integer_plus self , right
end
end
end
require_relative "constants"