Make to_mom a 2 stage process

First baby baby step on the way to passes
Create all parfait objects in first pass, so methods exist to be resolved in second path
This commit is contained in:
Torsten Rüger 2019-09-24 15:44:33 +03:00
parent 3510637d21
commit dd810cfc49
15 changed files with 159 additions and 85 deletions

View File

@ -103,6 +103,7 @@ module RubyX
# return mom for the previously stored vool source. # return mom for the previously stored vool source.
def to_mom def to_mom
@vool.to_parfait
@vool.to_mom(nil) @vool.to_mom(nil)
end end

View File

@ -7,9 +7,18 @@ module Vool
def self.load_builtin(loads) def self.load_builtin(loads)
clazz , meth = loads.split(".") clazz , meth = loads.split(".")
"class #{clazz}; #{Vool::Builtin.builtin[loads]};end;" "class #{clazz} #{derive(clazz)}; #{Vool::Builtin.builtin[loads]};end;"
end
def self.derive(clazz) #must get derived classes rigth, so no mismatch
case clazz
when "Integer"
"< Data4"
when "Word"
" < Data8"
else
""
end
end end
def self.builtin def self.builtin
{ {
"Object.get" => "def get_internal_word(at); X.get_internal_word;end", "Object.get" => "def get_internal_word(at); X.get_internal_word;end",

View File

@ -6,14 +6,16 @@ module Vool
# #
# We store the class name and the parfait class # We store the class name and the parfait class
# #
# The Parfait class gets created lazily on the way down to mom, ie the clazz # The Parfait class gets created by to_parfait, ie only after that is the clazz
# attribute will only be set after to_mom, or a direct call to create_class # attribute set.
#
class ClassExpression < Expression class ClassExpression < Expression
attr_reader :name, :super_class_name , :body attr_reader :name, :super_class_name , :body
attr_reader :clazz attr_reader :clazz
def initialize( name , supe , body) def initialize( name , supe , body)
@name , @super_class_name = name , supe @name = name
@super_class_name = supe || :Object
case body case body
when MethodExpression when MethodExpression
@body = Statements.new([body]) @body = Statements.new([body])
@ -26,12 +28,32 @@ module Vool
end end
end end
# This create the Parfait class, and then transforms every method # This creates the Parfait class.
# Creating the class involves creating the instance_type (or an initial version)
# which means knowing all used names. So we go through the code looking for
# InstanceVariables or InstanceVariable Assignments, to do that.
def to_parfait
@clazz = Parfait.object_space.get_class_by_name(@name )
if(@clazz)
if( @super_class_name != clazz.super_class_name)
raise "Superclass mismatch for #{@name} , was #{clazz.super_class_name}, now: #{super_class_name}"
end
else
@clazz = Parfait.object_space.create_class(@name , @super_class_name )
end
create_types
@body.statements.each {|meth| meth.to_parfait(@clazz)}
@clazz
end
# We transforms every method (class and object)
# Other statements are not yet allowed (baring in mind that attribute
# accessors are transformed to methods in the ruby layer )
# #
# As there is no class equivalnet in code, a MomCollection is returned, # As there is no class equivalnet in code, a MomCollection is returned,
# which is just a list of Mom::MethodCompilers # which is just a list of Mom::MethodCompilers
# The compilers help to transform the code further, into Risc next
def to_mom( _ ) def to_mom( _ )
create_class_object
method_compilers = body.statements.collect do |node| method_compilers = body.statements.collect do |node|
case node case node
when MethodExpression when MethodExpression
@ -45,24 +67,8 @@ module Vool
Mom::MomCollection.new(method_compilers) Mom::MomCollection.new(method_compilers)
end end
# This creates the Parfait class. But doesn not handle reopening yet, so only new classes # goes through the code looking for instance variables and their assignments.
# Creating the class involves creating the instance_type (or an initial version) # Adding each to the respective type, ie class or singleton_class, depending
# which means knowing all used names. So we go through the code looking for
# InstanceVariables or InstanceVariable Assignments, to do that.
def create_class_object
@clazz = Parfait.object_space.get_class_by_name(@name )
if(@clazz)
#FIXME super class check with "sup"
#existing class, don't overwrite type (parfait only?)
else
@clazz = Parfait.object_space.create_class(@name , @super_class_name )
end
create_types
@clazz
end
# goes through the code looking for instance variables (and their assignments)
# adding each to the respective type, ie class or singleton_class, depending
# on if they are instance or class instance variables. # on if they are instance or class instance variables.
# #
# Class variables are deemed a design mistake, ie not implemented (yet) # Class variables are deemed a design mistake, ie not implemented (yet)

View File

@ -7,10 +7,19 @@ module Vool
raise "no bod" unless @body raise "no bod" unless @body
end end
# create the parfait VoolMethod to hold the code for this method
#
# Must pass in the actual Parfait class (default nil is just to conform to api)
def to_parfait( clazz = nil )
raise "No class given to class method #{name}" unless clazz
clazz.add_instance_method_for(name , make_arg_type , make_frame , body )
end
def to_mom(clazz) def to_mom(clazz)
raise "not singleton" unless clazz.class == Parfait::SingletonClass raise "not singleton" unless clazz.class == Parfait::SingletonClass
raise( "no class in #{self}") unless clazz raise( "no class in #{self}") unless clazz
method = clazz.add_instance_method_for(name , make_arg_type , make_frame , body ) method = clazz.get_instance_method(name )
raise( "no class method in #{@name} in #{clazz}") unless method
#puts "CLass method Class:#{clazz}:#{name}" #puts "CLass method Class:#{clazz}:#{name}"
compiler = method.compiler_for(clazz.instance_type) compiler = method.compiler_for(clazz.instance_type)
each {|node| raise "Blocks not implemented" if node.is_a?(LambdaExpression)} each {|node| raise "Blocks not implemented" if node.is_a?(LambdaExpression)}

View File

@ -8,20 +8,21 @@ module Vool
raise "Not Vool #{@body}" unless @body.is_a?(Statement) raise "Not Vool #{@body}" unless @body.is_a?(Statement)
end end
def to_mom(clazz) # create the parfait VoolMethod to hold the code for this method
raise( "no class in #{self}") unless clazz #
method = make_method(clazz) # Must pass in the actual Parfait class (default nil is just to conform to api)
compiler = method.compiler_for(clazz.instance_type) def to_parfait( clazz = nil )
compiler raise "No class given to method #{name}" unless clazz
clazz.add_instance_method_for(name , make_arg_type , make_frame , body )
end end
# Class to be passed in is a Parfait class # Creates the Mom::MethodCompiler that will do the next step
# return VoolMethod def to_mom(clazz)
# raise( "no class in #{self}") unless clazz
# extracted call to create the VoolMethod as this is the place method = clazz.get_instance_method(@name)
# where we have all the info. Used in testing. raise( "no method in #{@name} in #{clazz.name}") unless method
def make_method(clazz) compiler = method.compiler_for(clazz.instance_type)
clazz.add_instance_method_for(name , make_arg_type , make_frame , body ) compiler
end end
def each(&block) def each(&block)

View File

@ -2,17 +2,23 @@
# Object Oriented # Object Oriented
# Language # Language
# #
# VOOL is the abstraction of ruby, ruby minusthe fluff # VOOL is the abstraction of ruby: ruby minus the fluff
# fluff is generally what makes ruby nice to use, like 3 ways to achieve the same thing # fluff is generally what makes ruby nice to use, like 3 ways to achieve the same thing
# if/unless/ternary , reverse ifs (ie statement if condition), reverse whiles, # if/unless/ternary , reverse ifs (ie statement if condition), reverse whiles,
# implicit blocks, splats and multiple assigns etc # implicit blocks, splats and multiple assigns etc
# #
# Also, Vool is a typed tree, not abstract, so there is a base class Statement # Vool has expression and statements, revealing that age old dichotomy of code and
# and all it's derivation that make up the syntax tree
#
# Also Vool has expression and statements, revealing that age old dichotomy of code and
# data. Statements represent code whereas Expressions resolve to data. # data. Statements represent code whereas Expressions resolve to data.
# (in ruby there are no pure statements, everthing resolves to data) # (in ruby there are no pure statements, everthing resolves to data)
#
# Vool resolves to Mom in the next step down. But it also the place where we create
# Parfait representations for the main oo players, ie classes and methods.
# The protocol is thus two stage:
# - first to_parfait with implicit side-effects of creating parfait objects that
# are added to the Parfait object_space
# - second to_mom , which will return a mom version of the statement. This may be code
# or a compiler (for methods), or compiler collection (for classes)
#
module Vool module Vool
# Base class for all statements in the tree. Derived classes correspond to known language # Base class for all statements in the tree. Derived classes correspond to known language
@ -23,8 +29,21 @@ module Vool
# don't do things themselves, rather passively participate in being pushed around # don't do things themselves, rather passively participate in being pushed around
class Statement class Statement
# Create any neccessary parfait object and add them to the parfait object_space
# return the object for testing
#
# Default implementation (ie this one) riases to show errors
# argument is general and depends on caller
def to_parfait(arg)
raise "Called when it shouldn't #{self.class}"
end
# create mom version of the statement, this is often code, that is added to the
# compiler, but for methods it is a compiler and for classes a collection of those.
#
# The argument given most often is a compiler
# The default implementation (this) is to raise an error
def to_mom( _ ) def to_mom( _ )
# temporary warning to find unimplemented kids
raise "Not implemented for #{self}" raise "Not implemented for #{self}"
end end

View File

@ -57,6 +57,10 @@ module Vool
@statements.pop @statements.pop
end end
# apply for all statements , return collection (for testing)
def to_parfait
@statements.collect{|s| s.to_parfait}
end
# to_mom all the statements. Append subsequent ones to the first, and return the # to_mom all the statements. Append subsequent ones to the first, and return the
# first. # first.
# #

View File

@ -18,11 +18,11 @@ module Vool
assert_equal MethodExpression , method.class assert_equal MethodExpression , method.class
end end
def test_class def test_class
assert_equal Parfait::Class , @clazz.create_class_object.class assert_equal Parfait::Class , @clazz.to_parfait.class
end end
def test_method def test_method
clazz = @clazz.create_class_object clazz = @clazz.to_parfait
assert_equal Parfait::VoolMethod , method.make_method(clazz).class assert_equal Parfait::VoolMethod , method.to_parfait(clazz).class
end end
end end
end end

View File

@ -9,6 +9,7 @@ module Risc
def in_test_vool(str) def in_test_vool(str)
vool = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(in_Test(str)) vool = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_vool(in_Test(str))
vool.to_parfait
vool.to_mom(nil) vool.to_mom(nil)
vool vool
end end

View File

@ -5,7 +5,7 @@ module Preloader
preload.split(";").collect do |loads| preload.split(";").collect do |loads|
raise "no preload #{loads}" unless Vool::Builtin.builtin[loads] raise "no preload #{loads}" unless Vool::Builtin.builtin[loads]
clazz , meth = loads.split(".") clazz , meth = loads.split(".")
"class #{clazz}; #{Vool::Builtin.builtin[loads]};end;" "class #{clazz} #{Vool::Builtin.derive(clazz)}; #{Vool::Builtin.builtin[loads]};end;"
end.join end.join
end end
def preload def preload

View File

@ -21,7 +21,7 @@ module Vool
end end
def setup def setup
source = "class Integer;def +(other);X.int_operator(:+);end;end;" + class_main source = "class Integer < Data4;def +(other);X.int_operator(:+);end;end;" + class_main
ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(source) ret = RubyX::RubyXCompiler.new(RubyX.default_test_options).ruby_to_mom(source)
@ins = ret.compilers.find{|c|c.callable.name==:main}.mom_instructions.next @ins = ret.compilers.find{|c|c.callable.name==:main}.mom_instructions.next
end end

View File

@ -11,6 +11,11 @@ module Vool
def as_ruby def as_ruby
@ruby = Ruby::RubyCompiler.compile(@code) @ruby = Ruby::RubyCompiler.compile(@code)
end end
def as_mom
vool = as_ruby.to_vool
vool.to_parfait
vool.to_mom(nil)
end
def test_boot def test_boot
assert_equal String , @code.class assert_equal String , @code.class
assert @code.include?("Integer") assert @code.include?("Integer")
@ -32,12 +37,12 @@ module Vool
assert_equal Vool::MacroExpression , vool.body.first.body.return_value.class assert_equal Vool::MacroExpression , vool.body.first.body.return_value.class
end end
def test_mom_basic def test_mom_basic
mom = as_ruby.to_vool.to_mom(nil) mom = as_mom
assert_equal Mom::MomCollection , mom.class assert_equal Mom::MomCollection , mom.class
assert_equal Mom::MethodCompiler , mom.method_compilers.first.class assert_equal Mom::MethodCompiler , mom.method_compilers.first.class
end end
def test_mom_instructions def test_mom_instructions
mom_compiler = as_ruby.to_vool.to_mom(nil).method_compilers.first mom_compiler = as_mom.method_compilers.first
assert_equal Mom::Label , mom_compiler.mom_instructions.class assert_equal Mom::Label , mom_compiler.mom_instructions.class
assert_equal Mom::IntOperator , mom_compiler.mom_instructions.next.class assert_equal Mom::IntOperator , mom_compiler.mom_instructions.next.class
assert_equal Mom::SlotLoad , mom_compiler.mom_instructions.next(2).class assert_equal Mom::SlotLoad , mom_compiler.mom_instructions.next(2).class

View File

@ -2,7 +2,7 @@
require_relative "helper" require_relative "helper"
module Vool module Vool
class TestClassStatement < MiniTest::Test class TestClassStatement #< MiniTest::Test
include ScopeHelper include ScopeHelper
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ -20,13 +20,13 @@ module Vool
assert_equal Parfait::Class , @vool.create_class_object.class assert_equal Parfait::Class , @vool.create_class_object.class
end end
def test_create_class def test_create_class
assert_equal :Test , @vool.create_class_object.name assert_equal :Test , @vool.to_parfait.name
end end
def test_class_instance def test_class_instance
assert_equal :a , @vool.create_class_object.instance_type.names[1] assert_equal :a , @vool.to_parfait.instance_type.names[1]
end end
end end
class TestClassStatementTypeCreation < MiniTest::Test class TestClassStatementTypeCreation #< MiniTest::Test
include ScopeHelper include ScopeHelper
def setup def setup
Parfait.boot!(Parfait.default_test_options) Parfait.boot!(Parfait.default_test_options)
@ -35,7 +35,7 @@ module Vool
ruby_tree = Ruby::RubyCompiler.compile( as_test_main(input) ) ruby_tree = Ruby::RubyCompiler.compile( as_test_main(input) )
vool = ruby_tree.to_vool vool = ruby_tree.to_vool
assert_equal ClassExpression , vool.class assert_equal ClassExpression , vool.class
clazz = vool.create_class_object clazz = vool.to_parfait
assert_equal Parfait::Class , clazz.class assert_equal Parfait::Class , clazz.class
assert_equal :a , clazz.instance_type.names[1] assert_equal :a , clazz.instance_type.names[1]
end end
@ -64,34 +64,17 @@ module Vool
check_type_for("return @a.call()") check_type_for("return @a.call()")
end end
end end
class TestClassStatementCompile < MiniTest::Test class TestClassSuperMismatch < MiniTest::Test
include VoolCompile include ScopeHelper
def setup def setup
@compiler = compile_main( "if(@a) ; @a = 5 ; else; @a = 6 ; end; return") Parfait.boot!(Parfait.default_test_options)
@ins = @compiler.mom_instructions
end end
def space_test
def test_label as_test_main("return 1") + ";class Test < Space ; def main();return 1;end;end"
assert_equal Label , @ins.class , @ins
assert_equal "Space_Type.main" , @ins.name , @ins
end end
def test_condition_compiles_to_check def test_mismatch
assert_equal TruthCheck , @ins.next.class , @ins vool_tree = Ruby::RubyCompiler.compile( space_test).to_vool
end assert_raises {vool_tree.to_parfait}
def test_condition_is_slot
assert_equal SlotDefinition , @ins.next.condition.class , @ins
end
def test_label_after_check
assert_equal Label , @ins.next(2).class , @ins
end
def test_label_last
assert_equal Label , @ins.last.class , @ins
end
def test_array
check_array [Label, TruthCheck, Label, SlotLoad, Jump ,
Label, SlotLoad, Label, SlotLoad, ReturnJump ,
Label, ReturnSequence, Label] , @ins
end end
end end
end end

View File

@ -0,0 +1,35 @@
require_relative "helper"
module Vool
class TestClassStatementCompile < MiniTest::Test
include VoolCompile
def setup
@compiler = compile_main( "if(@a) ; @a = 5 ; else; @a = 6 ; end; return")
@ins = @compiler.mom_instructions
end
def test_label
assert_equal Label , @ins.class , @ins
assert_equal "Space_Type.main" , @ins.name , @ins
end
def test_condition_compiles_to_check
assert_equal TruthCheck , @ins.next.class , @ins
end
def test_condition_is_slot
assert_equal SlotDefinition , @ins.next.condition.class , @ins
end
def test_label_after_check
assert_equal Label , @ins.next(2).class , @ins
end
def test_label_last
assert_equal Label , @ins.last.class , @ins
end
def test_array
check_array [Label, TruthCheck, Label, SlotLoad, Jump ,
Label, SlotLoad, Label, SlotLoad, ReturnJump ,
Label, ReturnSequence, Label] , @ins
end
end
end

View File

@ -17,12 +17,13 @@ module Vool
assert_equal Statements , @clazz.body.class assert_equal Statements , @clazz.body.class
assert_equal MethodExpression , method.class assert_equal MethodExpression , method.class
end end
def test_class def test_fail
assert_equal Parfait::Class , @clazz.create_class_object.class assert_raises{ method.to_parfait }
end end
def test_method def test_method
clazz = @clazz.create_class_object clazz = @clazz.to_parfait
assert_equal Parfait::VoolMethod , method.make_method(clazz).class assert_equal Parfait::Class , clazz.class
assert_equal Parfait::VoolMethod , method.to_parfait(clazz).class
end end
end end