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 = <