From 229f5896c6e021cf166c728c3586621a8610d6d9 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Mon, 7 Mar 2016 11:55:28 +0200 Subject: [PATCH] update to use new ast soml was updated to have a typed ast layer to make programatic creation easier this brings LOTS of syntax change with it, that does not really mean anything at all All tests pass again so back to the same --- Gemfile.lock | 2 +- lib/register/builtin/integer.rb | 8 ++--- lib/register/builtin/object.rb | 4 +-- lib/register/builtin/word.rb | 6 ++-- lib/register/instruction.rb | 2 +- lib/soml/compiler.rb | 37 ++++++++++++++++++++---- lib/soml/compiler/assignment.rb | 12 ++++---- lib/soml/compiler/basic_values.rb | 18 ++++++------ lib/soml/compiler/call_site.rb | 20 ++++++------- lib/soml/compiler/class_field.rb | 11 +++---- lib/soml/compiler/class_statement.rb | 7 ++--- lib/soml/compiler/field_access.rb | 8 ++--- lib/soml/compiler/field_def.rb | 10 +++---- lib/soml/compiler/function_definition.rb | 22 +++++++------- lib/soml/compiler/if_statement.rb | 20 ++++++------- lib/soml/compiler/name_expression.rb | 8 ++--- lib/soml/compiler/operator_value.rb | 10 +++---- lib/soml/compiler/return_statement.rb | 4 +-- lib/soml/compiler/while_statement.rb | 17 +++++------ test/soml/expressions/helper.rb | 3 +- test/soml/expressions/test_basic.rb | 2 +- test/soml/statements/test_class.rb | 13 --------- 22 files changed, 126 insertions(+), 118 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 95fe7ba5..bed1824f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,7 +12,7 @@ GIT GIT remote: git://github.com/salama/soml-parser.git - revision: 4a9b492dd9fb1d7cbc6f114aee226ff227753568 + revision: 5c03db709fa3a326288a57a65643bd9ddf905bec specs: soml-parser (0.5.0) ast (~> 2.1.0) diff --git a/lib/register/builtin/integer.rb b/lib/register/builtin/integer.rb index 5b8942ba..4749d400 100644 --- a/lib/register/builtin/integer.rb +++ b/lib/register/builtin/integer.rb @@ -18,10 +18,10 @@ module Register def div10 context s = "div_10" compiler = Soml::Compiler.new.create_method(:Integer,:div10 ).init_method - me = compiler.process( s(:name , :self) ) - tmp = compiler.process( s(:name , :self) ) - q = compiler.process( s(:name , :self) ) - const = compiler.process( s(:int , 1) ) + me = compiler.process( Soml::NameExpression.new( :self) ) + tmp = compiler.process( Soml::NameExpression.new( :self) ) + q = compiler.process( Soml::NameExpression.new( :self) ) + const = compiler.process( Soml::IntegerExpression.new(1) ) # int tmp = self >> 1 compiler.add_code Register.op( s , ">>" , tmp , const) # int q = self >> 2 diff --git a/lib/register/builtin/object.rb b/lib/register/builtin/object.rb index 439fdcaa..83bcaa11 100644 --- a/lib/register/builtin/object.rb +++ b/lib/register/builtin/object.rb @@ -13,7 +13,7 @@ module Register compiler = Soml::Compiler.new.create_method(:Object , :get_internal_word , {:index => :Integer}).init_method source = "get_internal_word" #Load self by "calling" on_name - me = compiler.process( s(:name , :self) ) + me = compiler.process( Soml::NameExpression.new( :self) ) # Load the argument index = compiler.use_reg :Integer compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index ) @@ -31,7 +31,7 @@ module Register {:index => :Integer, :value => :Object} ).init_method source = "set_internal_word" #Load self by "calling" on_name - me = compiler.process( s(:name , :self) ) + me = compiler.process( Soml::NameExpression.new( :self) ) # Load the index index = compiler.use_reg :Integer compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index ) diff --git a/lib/register/builtin/word.rb b/lib/register/builtin/word.rb index bfaadaeb..94cf4209 100644 --- a/lib/register/builtin/word.rb +++ b/lib/register/builtin/word.rb @@ -13,14 +13,14 @@ module Register Kernel.emit_syscall( compiler , :putstring ) compiler.method end - + # self[index] basically. Index is the first arg > 0 # return (and word sized int) is stored in return_value def get_internal_byte context compiler = Soml::Compiler.new.create_method(:Word , :get_internal_byte , {:index => :Integer }).init_method source = "get_internal_word" #Load self by "calling" on_name - me = compiler.process( s(:name , :self) ) + me = compiler.process( Soml::NameExpression.new( :self) ) # Load the argument index = compiler.use_reg :Integer compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index ) @@ -39,7 +39,7 @@ module Register {:index => :Integer, :value => :Integer } ).init_method source = "set_internal_word" #Load self by "calling" on_name - me = compiler.process( s(:name , :self) ) + me = compiler.process( Soml::NameExpression.new( :self) ) # Load the index index = compiler.use_reg :Integer compiler.add_code Register.get_slot(source , :message , Parfait::Message.get_indexed(1), index ) diff --git a/lib/register/instruction.rb b/lib/register/instruction.rb index 9e33823f..c85b855b 100644 --- a/lib/register/instruction.rb +++ b/lib/register/instruction.rb @@ -19,7 +19,7 @@ module Register @source = source @next = nekst return unless source - raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(AST::Node) + raise "Source must be string or ast node, not #{source.class}" unless source.is_a?(String) or source.is_a?(Soml::Code) end attr_reader :source diff --git a/lib/soml/compiler.rb b/lib/soml/compiler.rb index 1882b0c4..2938f1d3 100644 --- a/lib/soml/compiler.rb +++ b/lib/soml/compiler.rb @@ -32,10 +32,11 @@ module Soml # Helper function to create a new compiler and compie the statement(s) def self.compile statement compiler = Compiler.new - compiler.process statement + code = Soml.ast_to_code statement + compiler.process code end - class Compiler < AST::Processor + class Compiler def initialize( method = nil ) @regs = [] @@ -46,8 +47,33 @@ module Soml end attr_reader :clazz , :method - def handler_missing node - raise "No handler on_#{node.type}(node)" + + # Dispatches `code` according to it's class name, for class NameExpression + # a method named `on_NameExpression` is invoked with one argument, the `code` + # + # @param [Soml::Code, nil] code + def process(code) + name = code.class.name.split("::").last + # Invoke a specific handler + on_handler = :"on_#{name}" + if respond_to? on_handler + return send on_handler, code + else + raise "No handler on_#{name}(code) #{code.inspect}" + end + end + + # {#process}es each code from `codes` and returns an array of + # results. + # + def process_all(codes) + codes.to_a.map do |code| + process code + end + end + + def on_Statements(codes) + process_all codes.statements end # create the method, do some checks and set it as the current method to be added to @@ -150,7 +176,8 @@ module Soml def self.load_parfait each_parfait do |parts| - self.new.process( parts ) + code = Soml.ast_to_code parts + self.new.process( code ) end end diff --git a/lib/soml/compiler/assignment.rb b/lib/soml/compiler/assignment.rb index 34afb1bb..39913077 100644 --- a/lib/soml/compiler/assignment.rb +++ b/lib/soml/compiler/assignment.rb @@ -1,19 +1,19 @@ module Soml Compiler.class_eval do - def on_assignment statement + def on_Assignment statement reset_regs # statements reset registers, ie have all at their disposal #puts statement.inspect - name , value = *statement - name = no_space name.to_a.first - v = process(value) +# name , value = *statement + name_s = no_space statement.name + v = process(statement.value) raise "Not register #{v}" unless v.is_a?(Register::RegisterValue) code = nil - if( index = @method.has_arg(name)) + if( index = @method.has_arg(name_s.name)) # TODO, check type @method.arguments[index].type code = Register.set_slot(statement , v , :message , Parfait::Message.get_indexed(index) ) else # or a local so it is in the frame - index = @method.has_local( name ) + index = @method.has_local( name_s.name ) if(index) # TODO, check type @method.locals[index].type frame = use_reg(:Frame) diff --git a/lib/soml/compiler/basic_values.rb b/lib/soml/compiler/basic_values.rb index fb824596..8d0e02a2 100644 --- a/lib/soml/compiler/basic_values.rb +++ b/lib/soml/compiler/basic_values.rb @@ -10,41 +10,41 @@ module Soml # But in the future (in the one that holds great things) we optimize those unneccesay moves away - def on_int expression - int = expression.first + def on_IntegerExpression expression + int = expression.value reg = use_reg :Integer , int add_code Register::LoadConstant.new( expression, int , reg ) return reg end - def on_true expression + def on_TrueExpression expression reg = use_reg :Boolean add_code Register::LoadConstant.new( expression, true , reg ) return reg end - def on_false expression + def on_FalseExpression expression reg = use_reg :Boolean add_code Register::LoadConstant.new( expression, false , reg ) return reg end - def on_nil expression + def on_NilExpression expression reg = use_reg :NilClass add_code Register::LoadConstant.new( expression, nil , reg ) return reg end - def on_string expression - value = Parfait.new_word expression.first.to_sym + def on_StringExpression expression + value = Parfait.new_word expression.value.to_sym reg = use_reg :Word Register.machine.constants << value add_code Register::LoadConstant.new( expression, value , reg ) return reg end - def on_class_name expression - name = expression.first + def on_ClassExpression expression + name = expression.value clazz = Parfait::Space.object_space.get_class_by_name! name raise "No such class #{name}" unless clazz reg = use_reg :MetaClass , clazz diff --git a/lib/soml/compiler/call_site.rb b/lib/soml/compiler/call_site.rb index 55962d2a..d21190ad 100644 --- a/lib/soml/compiler/call_site.rb +++ b/lib/soml/compiler/call_site.rb @@ -1,16 +1,16 @@ module Soml Compiler.class_eval do - def on_call statement + def on_CallSite statement #puts statement - name_s , arguments , receiver = *statement +# name_s , arguments , receiver = *statement raise "not inside method " unless @method reset_regs #move the new message (that we need to populate to make a call) to std register new_message = Register.resolve_to_register(:new_message) add_code Register.get_slot(statement, :message , :next_message , new_message ) - if receiver - me = process( receiver.first ) + if statement.receiver + me = process( statement.receiver ) else me = use_reg @method.for_class.name add_code Register.get_slot(statement, :message , :receiver , me ) @@ -24,8 +24,8 @@ module Soml # move our receiver there add_code Register.set_slot( statement , me , :new_message , :receiver) - set_message_details(name_s , arguments) - set_arguments(arguments) + set_message_details(statement , statement.arguments) + set_arguments(statement.arguments) ret = use_reg( :Integer ) #TODO real return type do_call(clazz , statement) # the effect of the method is that the NewMessage Return slot will be filled, return it @@ -36,7 +36,7 @@ module Soml private def do_call clazz , statement - name = statement.first.first + name = statement.name #puts "clazz #{clazz.name}" raise "No such class" unless clazz method = clazz.resolve_method(name) @@ -45,15 +45,15 @@ module Soml Register.issue_call( self , method ) end def set_message_details name_s , arguments - name = name_s.to_a.first + name = name_s.name # load method name and set to new message (for exceptions/debug) name_tmp = use_reg(:Word) add_code Register::LoadConstant.new(name_s, name , name_tmp) add_code Register.set_slot( name_s , name_tmp , :new_message , :name) # next arguments. first length then args len_tmp = use_reg(:Integer , arguments.to_a.length ) - add_code Register::LoadConstant.new(arguments, arguments.to_a.length , len_tmp) - add_code Register.set_slot( arguments , len_tmp , :new_message , :indexed_length) + add_code Register::LoadConstant.new(name_s, arguments.to_a.length , len_tmp) + add_code Register.set_slot( name_s , len_tmp , :new_message , :indexed_length) end def set_arguments arguments # reset tmp regs for each and load result into new_message diff --git a/lib/soml/compiler/class_field.rb b/lib/soml/compiler/class_field.rb index 567884a2..1b14e051 100644 --- a/lib/soml/compiler/class_field.rb +++ b/lib/soml/compiler/class_field.rb @@ -1,19 +1,16 @@ module Soml Compiler.class_eval do - def on_class_field statement + def on_ClassField statement #puts statement.inspect - type , name , value = *statement + #type , name , value = *statement for_class = @clazz raise "no class" unless for_class - index = for_class.instance_type.variable_index(name) + index = for_class.instance_type.variable_index(statement.name) #raise "class field already defined:#{name} for class #{for_class.name}" if index #puts "Define field #{name} on class #{for_class.name}" - index = for_class.instance_type.add_instance_variable( name , type ) - - # not sure how to run class code yet. later - raise "value #{value}" if value + index = for_class.instance_type.add_instance_variable( statement.name , statement.type ) return nil # statements don't reurn values, only expressions end diff --git a/lib/soml/compiler/class_statement.rb b/lib/soml/compiler/class_statement.rb index f6c6f933..681e1f58 100644 --- a/lib/soml/compiler/class_statement.rb +++ b/lib/soml/compiler/class_statement.rb @@ -1,13 +1,12 @@ module Soml Compiler.class_eval do - def on_class statement + def on_ClassStatement statement #puts statement.inspect - name , derives , statements = *statement raise "classes dont yet play babushka, get coding #{name}" if @clazz - @clazz = Parfait::Space.object_space.get_class_by_name! name + @clazz = Parfait::Space.object_space.get_class_by_name! statement.name #puts "Compiling class #{@clazz.name.inspect}" - statement_value = process_all(statements).last + statement_value = process(statement.statements).last @clazz = nil return statement_value end diff --git a/lib/soml/compiler/field_access.rb b/lib/soml/compiler/field_access.rb index bc7bdeb6..345cbcb0 100644 --- a/lib/soml/compiler/field_access.rb +++ b/lib/soml/compiler/field_access.rb @@ -1,13 +1,13 @@ module Soml Compiler.class_eval do - def on_field_access statement - receiver_ast , field_ast = *statement - receiver = process(receiver_ast) + def on_FieldAccess statement +# receiver_ast , field_ast = *statement + receiver = process(statement.receiver) clazz = Register.machine.space.get_class_by_name receiver.type - field_name = field_ast.first_from(:name) + field_name = statement.field.name index = clazz.instance_type.variable_index(field_name) diff --git a/lib/soml/compiler/field_def.rb b/lib/soml/compiler/field_def.rb index eb79958a..e93438f8 100644 --- a/lib/soml/compiler/field_def.rb +++ b/lib/soml/compiler/field_def.rb @@ -2,14 +2,14 @@ module Soml Compiler.class_eval do include AST::Sexp - def on_field_def statement + def on_FieldDef statement reset_regs # field_def is a statement, no return and all regs #puts statement.inspect - type , name , value = *statement - name_s = no_space( name.first ) - @method.ensure_local( name_s, type ) unless( @method.has_arg(name_s)) +# type , name , value = *statement + name_s = no_space( statement.name.value ) + @method.ensure_local( name_s, statement.type ) unless( @method.has_arg(name_s)) # if there is a value assigned, process it as am assignemnt statement (kind of call on_assign) - process( s(:assignment , name , value ) ) if value + process( Soml::Assignment.new(statement.name , statement.value ) ) if statement.value return nil end end diff --git a/lib/soml/compiler/function_definition.rb b/lib/soml/compiler/function_definition.rb index 4251393c..0fcd0344 100644 --- a/lib/soml/compiler/function_definition.rb +++ b/lib/soml/compiler/function_definition.rb @@ -1,24 +1,23 @@ module Soml Compiler.class_eval do - def on_function statement + def on_FunctionStatement statement #puts statement.inspect - return_type , name , parameters, kids , receiver = *statement - name = name.to_a.first +# return_type , name , parameters, kids , receiver = *statement + name = statement.name raise "Already in method #{@method}" if @method - args = parameters.to_a.collect do |p| - raise "error, argument must be a identifier, not #{p}" unless p.type == :parameter - Parfait::Variable.new( *p) + args = statement.parameters.collect do |p| + Parfait::Variable.new( *p ) end class_method = nil - if(receiver ) - if( receiver.first == :self) #class method + if(statement.receiver ) + if( statement.receiver.first == :self) #class method class_method = @clazz @clazz = @clazz.meta else - raise "Not covered #{receiver}" + raise "Not covered #{statement.receiver}" end end @@ -33,9 +32,8 @@ module Soml @method.source = statement #puts "compile method #{@method.name}" - kids.to_a.each do |ex| - process(ex) - end + process(statement.statements) + @clazz = class_method if class_method @method = nil # function definition is a statement, does not return any value diff --git a/lib/soml/compiler/if_statement.rb b/lib/soml/compiler/if_statement.rb index 17a1e17f..e4b30012 100644 --- a/lib/soml/compiler/if_statement.rb +++ b/lib/soml/compiler/if_statement.rb @@ -4,27 +4,27 @@ module Soml # an if evaluates the condition and jumps to the true block if true # so the else block is automatically after that. # But then the else needs to jump over the true block unconditionally. - def on_if_statement statement - branch_type , condition , if_true , if_false = *statement - condition = condition.first + def on_IfStatement statement +# branch_type , condition , if_true , if_false = *statement +# condition = condition.first reset_regs - process(condition) + process(statement.condition) - branch_class = Object.const_get "Register::Is#{branch_type.capitalize}" - true_block = Register::Label.new(if_true, "if_true") - add_code branch_class.new( condition , true_block ) + branch_class = Object.const_get "Register::Is#{statement.branch_type.capitalize}" + true_block = Register::Label.new(statement, "if_true") + add_code branch_class.new( statement.condition , true_block ) # compile the false block reset_regs - process_all(if_false) if if_false + process(statement.if_false) if statement.if_false.statements merge = Register::Label.new(statement , "if_merge") - add_code Register::Branch.new(if_false, merge ) + add_code Register::Branch.new(statement.if_false, merge ) # compile the true block add_code true_block reset_regs - process_all(if_true) + process(statement.if_true) #puts "compiled if: end" add_code merge diff --git a/lib/soml/compiler/name_expression.rb b/lib/soml/compiler/name_expression.rb index f25f84ac..331316df 100644 --- a/lib/soml/compiler/name_expression.rb +++ b/lib/soml/compiler/name_expression.rb @@ -2,11 +2,11 @@ module Soml Compiler.class_eval do # attr_reader :name - # compiling name needs to check if it's a variable and if so resolve it - # otherwise it's a method without args and a send is issued. + # compiling name needs to check if it's a local variable + # or an argument # whichever way this goes the result is stored in the return slot (as all compiles) - def on_name statement - name = statement.to_a.first + def on_NameExpression statement + name = statement.name if( name == :self) ret = use_reg @clazz.name add_code Register.get_slot(statement , :message , :receiver , ret ) diff --git a/lib/soml/compiler/operator_value.rb b/lib/soml/compiler/operator_value.rb index 3bc2e48c..8ba0599f 100644 --- a/lib/soml/compiler/operator_value.rb +++ b/lib/soml/compiler/operator_value.rb @@ -1,17 +1,17 @@ module Soml Compiler.class_eval do - def on_operator_value statement + def on_OperatorExpression statement #puts "operator #{statement.inspect}" - operator , left_e , right_e = *statement +# operator , left_e , right_e = *statement # left and right must be expressions. Expressions return a register when compiled - left_reg = process(left_e) - right_reg = process(right_e) + left_reg = process(statement.left_expression) + right_reg = process(statement.right_expression) raise "Not register #{left_reg}" unless left_reg.is_a?(Register::RegisterValue) raise "Not register #{right_reg}" unless right_reg.is_a?(Register::RegisterValue) #puts "left #{left_reg}" #puts "right #{right_reg}" - add_code Register::OperatorInstruction.new(statement,operator,left_reg,right_reg) + add_code Register::OperatorInstruction.new(statement,statement.operator,left_reg,right_reg) return left_reg # though this has wrong value attached end end diff --git a/lib/soml/compiler/return_statement.rb b/lib/soml/compiler/return_statement.rb index 94a2d224..bd620b3e 100644 --- a/lib/soml/compiler/return_statement.rb +++ b/lib/soml/compiler/return_statement.rb @@ -1,8 +1,8 @@ module Soml Compiler.class_eval do - def on_return statement - reg = process(statement.first) + def on_ReturnStatement statement + reg = process(statement.return_value) add_code Register.set_slot( statement, reg , :message , :return_value) nil # statements don't return end diff --git a/lib/soml/compiler/while_statement.rb b/lib/soml/compiler/while_statement.rb index e5aa7aa2..8dba1c63 100644 --- a/lib/soml/compiler/while_statement.rb +++ b/lib/soml/compiler/while_statement.rb @@ -1,27 +1,26 @@ module Soml Compiler.class_eval do - def on_while_statement statement + def on_WhileStatement statement #puts statement.inspect - branch_type , condition , statements = *statement - condition = condition.first + #branch_type , condition , statements = *statement - condition_label = Register::Label.new(statement , "condition_label") + condition_label = Register::Label.new(statement.condition , "condition_label") # unconditionally branch to the condition upon entering the loop - add_code Register::Branch.new(statement,condition_label) + add_code Register::Branch.new(statement.condition,condition_label) add_code start = Register::Label.new(statement , "while_start" ) reset_regs - process_all(statements) + process(statement.statements) # This is where the loop starts, though in subsequent iterations it's in the middle add_code condition_label reset_regs - process(condition) + process(statement.condition) - branch_class = Object.const_get "Register::Is#{branch_type.capitalize}" + branch_class = Object.const_get "Register::Is#{statement.branch_type.capitalize}" # this is where the while ends and both branches meet - add_code branch_class.new( condition , start ) + add_code branch_class.new( statement.condition , start ) nil # statements don't return anything end diff --git a/test/soml/expressions/helper.rb b/test/soml/expressions/helper.rb index 455a4add..63608fa2 100644 --- a/test/soml/expressions/helper.rb +++ b/test/soml/expressions/helper.rb @@ -21,10 +21,11 @@ module ExpressionHelper parser = parser.send @root syntax = parser.parse_with_debug(@string_input, reporter: Parslet::ErrorReporter::Deepest.new) parts = Parser::Transform.new.apply(syntax) + codes = Soml.ast_to_code parts #puts parts.inspect compiler = Soml::Compiler.new set_main(compiler) - produced = compiler.process( parts ) + produced = compiler.process( codes ) assert @output , "No output given" assert_equal produced.class, @output , "Wrong class" produced diff --git a/test/soml/expressions/test_basic.rb b/test/soml/expressions/test_basic.rb index c0d3e0fb..5a69c8cb 100644 --- a/test/soml/expressions/test_basic.rb +++ b/test/soml/expressions/test_basic.rb @@ -30,7 +30,7 @@ class TestBasic < MiniTest::Test def test_var @string_input = 'int foo ' @root = :field_def - @output = AST::Node + @output = NilClass check end diff --git a/test/soml/statements/test_class.rb b/test/soml/statements/test_class.rb index d5649194..0dd8410d 100644 --- a/test/soml/statements/test_class.rb +++ b/test/soml/statements/test_class.rb @@ -40,19 +40,6 @@ HERE check end - def test_class_field_value - @string_input = <