From d7f31e7f397b57304f279e2e3232e0bcf5c606d9 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Tue, 13 May 2014 16:24:19 +0300 Subject: [PATCH] introduce constant class and add block to compile signature(wip, work in progress) --- lib/arm/arm_machine.rb | 10 +++- lib/arm/assembler.rb | 4 +- lib/arm/logic_helper.rb | 4 +- lib/arm/memory_instruction.rb | 2 +- lib/ast/basic_expressions.rb | 16 +++---- lib/ast/expression.rb | 2 +- lib/ast/function_expression.rb | 20 ++++---- lib/ast/operator_expressions.rb | 54 +++++++++------------ lib/ast/while_expression.rb | 8 ++++ lib/parser/operators.rb | 4 +- lib/parslet/transform.rb | 2 +- lib/vm/constants.rb | 51 ++++++++++++++++++++ lib/vm/function_call.rb | 22 +++------ lib/vm/machine.rb | 2 +- lib/vm/values.rb | 84 +++++++-------------------------- 15 files changed, 145 insertions(+), 140 deletions(-) create mode 100644 lib/vm/constants.rb diff --git a/lib/arm/arm_machine.rb b/lib/arm/arm_machine.rb index c2b98001..b81941b2 100644 --- a/lib/arm/arm_machine.rb +++ b/lib/arm/arm_machine.rb @@ -30,7 +30,15 @@ module Arm 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 raise "not a register :#{reg}:" unless reg.class == Symbol mov( :left => reg , :right => value ) diff --git a/lib/arm/assembler.rb b/lib/arm/assembler.rb index 3fb4850b..e4217468 100644 --- a/lib/arm/assembler.rb +++ b/lib/arm/assembler.rb @@ -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 diff --git a/lib/arm/logic_helper.rb b/lib/arm/logic_helper.rb index e5eea858..4ba66278 100644 --- a/lib/arm/logic_helper.rb +++ b/lib/arm/logic_helper.rb @@ -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)) diff --git a/lib/arm/memory_instruction.rb b/lib/arm/memory_instruction.rb index 3144eaee..c8b84f54 100644 --- a/lib/arm/memory_instruction.rb +++ b/lib/arm/memory_instruction.rb @@ -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 diff --git a/lib/ast/basic_expressions.rb b/lib/ast/basic_expressions.rb index 624697d6..2256aa5d 100644 --- a/lib/ast/basic_expressions.rb +++ b/lib/ast/basic_expressions.rb @@ -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 diff --git a/lib/ast/expression.rb b/lib/ast/expression.rb index 728374a6..795bc79c 100644 --- a/lib/ast/expression.rb +++ b/lib/ast/expression.rb @@ -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 diff --git a/lib/ast/function_expression.rb b/lib/ast/function_expression.rb index 4b57e5d8..ce5b5b8a 100644 --- a/lib/ast/function_expression.rb +++ b/lib/ast/function_expression.rb @@ -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 diff --git a/lib/ast/operator_expressions.rb b/lib/ast/operator_expressions.rb index 1793a365..30201c03 100644 --- a/lib/ast/operator_expressions.rb +++ b/lib/ast/operator_expressions.rb @@ -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 = [] - - #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 + def compile context , into + r_val = right.compile(context , into) + + 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 - else - function.body.add_code compiled - end - puts compiled.inspect + 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 + raise "unimplemented operator #{operator} #{self}" end - context.locals = parent_locals if parent_locals - function end end end \ No newline at end of file diff --git a/lib/ast/while_expression.rb b/lib/ast/while_expression.rb index e20e4def..5b0039a2 100644 --- a/lib/ast/while_expression.rb +++ b/lib/ast/while_expression.rb @@ -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 diff --git a/lib/parser/operators.rb b/lib/parser/operators.rb index 2d0a6d36..dac03e63 100644 --- a/lib/parser/operators.rb +++ b/lib/parser/operators.rb @@ -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], diff --git a/lib/parslet/transform.rb b/lib/parslet/transform.rb index b04fa29d..5d999639 100644 --- a/lib/parslet/transform.rb +++ b/lib/parslet/transform.rb @@ -22,7 +22,7 @@ require 'parslet/pattern' # # class Example < Parslet::Transform # rule(:string => simple(:x)) { # (1) -# StringLiteral.new(x) +# StringConstant.new(x) # } # end # diff --git a/lib/vm/constants.rb b/lib/vm/constants.rb new file mode 100644 index 00000000..bd00d521 --- /dev/null +++ b/lib/vm/constants.rb @@ -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 \ No newline at end of file diff --git a/lib/vm/function_call.rb b/lib/vm/function_call.rb index 939075b1..da621a34 100644 --- a/lib/vm/function_call.rb +++ b/lib/vm/function_call.rb @@ -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 diff --git a/lib/vm/machine.rb b/lib/vm/machine.rb index 9d3f60c2..68715776 100644 --- a/lib/vm/machine.rb +++ b/lib/vm/machine.rb @@ -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, diff --git a/lib/vm/values.rb b/lib/vm/values.rb index 71d692fc..05883d6c 100644 --- a/lib/vm/values.rb +++ b/lib/vm/values.rb @@ -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 - end + class Integer < Word - 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 + def less_or_equal right + Machine.instance.integer_less_or_equal self , right 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" \ No newline at end of file