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.
def to_mom
@vool.to_parfait
@vool.to_mom(nil)
end

View File

@ -7,9 +7,18 @@ module Vool
def self.load_builtin(loads)
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
def self.builtin
{
"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
#
# The Parfait class gets created lazily on the way down to mom, ie the clazz
# attribute will only be set after to_mom, or a direct call to create_class
# The Parfait class gets created by to_parfait, ie only after that is the clazz
# attribute set.
#
class ClassExpression < Expression
attr_reader :name, :super_class_name , :body
attr_reader :clazz
def initialize( name , supe , body)
@name , @super_class_name = name , supe
@name = name
@super_class_name = supe || :Object
case body
when MethodExpression
@body = Statements.new([body])
@ -26,12 +28,32 @@ module Vool
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,
# which is just a list of Mom::MethodCompilers
# The compilers help to transform the code further, into Risc next
def to_mom( _ )
create_class_object
method_compilers = body.statements.collect do |node|
case node
when MethodExpression
@ -45,24 +67,8 @@ module Vool
Mom::MomCollection.new(method_compilers)
end
# This creates the Parfait class. But doesn not handle reopening yet, so only new classes
# 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 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
# 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.
#
# Class variables are deemed a design mistake, ie not implemented (yet)

View File

@ -7,10 +7,19 @@ module Vool
raise "no bod" unless @body
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)
raise "not singleton" unless clazz.class == Parfait::SingletonClass
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}"
compiler = method.compiler_for(clazz.instance_type)
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)
end
def to_mom(clazz)
raise( "no class in #{self}") unless clazz
method = make_method(clazz)
compiler = method.compiler_for(clazz.instance_type)
compiler
# 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 method #{name}" unless clazz
clazz.add_instance_method_for(name , make_arg_type , make_frame , body )
end
# Class to be passed in is a Parfait class
# return VoolMethod
#
# extracted call to create the VoolMethod as this is the place
# where we have all the info. Used in testing.
def make_method(clazz)
clazz.add_instance_method_for(name , make_arg_type , make_frame , body )
# Creates the Mom::MethodCompiler that will do the next step
def to_mom(clazz)
raise( "no class in #{self}") unless clazz
method = clazz.get_instance_method(@name)
raise( "no method in #{@name} in #{clazz.name}") unless method
compiler = method.compiler_for(clazz.instance_type)
compiler
end
def each(&block)

View File

@ -2,17 +2,23 @@
# Object Oriented
# 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
# if/unless/ternary , reverse ifs (ie statement if condition), reverse whiles,
# implicit blocks, splats and multiple assigns etc
#
# Also, Vool is a typed tree, not abstract, so there is a base class Statement
# 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
# Vool has expression and statements, revealing that age old dichotomy of code and
# data. Statements represent code whereas Expressions resolve 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
# 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
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( _ )
# temporary warning to find unimplemented kids
raise "Not implemented for #{self}"
end

View File

@ -57,6 +57,10 @@ module Vool
@statements.pop
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
# first.
#

View File

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

View File

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

View File

@ -5,7 +5,7 @@ module Preloader
preload.split(";").collect do |loads|
raise "no preload #{loads}" unless Vool::Builtin.builtin[loads]
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
def preload

View File

@ -21,7 +21,7 @@ module Vool
end
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)
@ins = ret.compilers.find{|c|c.callable.name==:main}.mom_instructions.next
end

View File

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

View File

@ -2,7 +2,7 @@
require_relative "helper"
module Vool
class TestClassStatement < MiniTest::Test
class TestClassStatement #< MiniTest::Test
include ScopeHelper
def setup
Parfait.boot!(Parfait.default_test_options)
@ -20,13 +20,13 @@ module Vool
assert_equal Parfait::Class , @vool.create_class_object.class
end
def test_create_class
assert_equal :Test , @vool.create_class_object.name
assert_equal :Test , @vool.to_parfait.name
end
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
class TestClassStatementTypeCreation < MiniTest::Test
class TestClassStatementTypeCreation #< MiniTest::Test
include ScopeHelper
def setup
Parfait.boot!(Parfait.default_test_options)
@ -35,7 +35,7 @@ module Vool
ruby_tree = Ruby::RubyCompiler.compile( as_test_main(input) )
vool = ruby_tree.to_vool
assert_equal ClassExpression , vool.class
clazz = vool.create_class_object
clazz = vool.to_parfait
assert_equal Parfait::Class , clazz.class
assert_equal :a , clazz.instance_type.names[1]
end
@ -64,34 +64,17 @@ module Vool
check_type_for("return @a.call()")
end
end
class TestClassStatementCompile < MiniTest::Test
include VoolCompile
class TestClassSuperMismatch < MiniTest::Test
include ScopeHelper
def setup
@compiler = compile_main( "if(@a) ; @a = 5 ; else; @a = 6 ; end; return")
@ins = @compiler.mom_instructions
Parfait.boot!(Parfait.default_test_options)
end
def test_label
assert_equal Label , @ins.class , @ins
assert_equal "Space_Type.main" , @ins.name , @ins
def space_test
as_test_main("return 1") + ";class Test < Space ; def main();return 1;end;end"
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
def test_mismatch
vool_tree = Ruby::RubyCompiler.compile( space_test).to_vool
assert_raises {vool_tree.to_parfait}
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 MethodExpression , method.class
end
def test_class
assert_equal Parfait::Class , @clazz.create_class_object.class
def test_fail
assert_raises{ method.to_parfait }
end
def test_method
clazz = @clazz.create_class_object
assert_equal Parfait::VoolMethod , method.make_method(clazz).class
clazz = @clazz.to_parfait
assert_equal Parfait::Class , clazz.class
assert_equal Parfait::VoolMethod , method.to_parfait(clazz).class
end
end