Removing the SlotLanguage

I turned out that that layer did not provide benefit. Data was being shuffled, without gain, so i moved the compiler to the SlotMachine
This commit is contained in:
Torsten Rüger 2020-02-19 02:13:50 +07:00
parent 2d11078a37
commit b88c5ff498
17 changed files with 0 additions and 677 deletions

View File

@ -4,7 +4,6 @@ require_relative "util"
require_relative "elf/object_writer"
require_relative "risc"
require_relative "slot_machine/slot_machine"
require_relative "slot_language/slot_compiler"
require_relative "arm/arm_machine"
require_relative "arm/arm_platform"
require_relative "sol/statement"

View File

@ -1,62 +0,0 @@
# Slot Language
This is a new layer / approach to create code for the next level, the slot_machine.
In essence the slot_language lets us code the slot_machine.
## Problem
The Problem with the current way is that some of the slot_machine instructions are
really quite complex. They are really more functions than instructions.
This is especially true for everything around the dynamic call. Dynamic call itself
is still ok, but resolve_method is too much. And it even uses method_missing, another
instruction that is too much, which in turn should use raise and now we really see
the point.
I thought about making those "super" instruction real methods and just calling them,
but the calling overhead is just too much, and really it is the wrong tool for the
job. Calling implies switching of context, while resolve_method and raise and mm
really all operate on the same context.
## The Slot Machine
The Slot Machine is a kind of memory based, oo abstraction of the risc machine, that in
turn mirrors a cpu relatively closely. The machine "knows" the message in the way a
cpu knows its registers. And the oo part means it also knows the parfait object
model.
While Ruby/Sol code only ever assumes the type of self, the Slot Machine assumes types
of the whole of Parfait. The main instruction after which the machine is named is
the SlotLoad, which moves one instance variable to another.
## Code for the Machine
Since the Slot and SlotMachine have proven useful abstractions, this introduces the
SlotLanguage as a way to create code for the SlotMachine.
The SlotMachine defines no methods on objects and passes no objects. While it has call
and return, these are defined in terms of jumps and use, like all Slot instructions,
the message.
## Syntax (projection)
Since we are not defining methods, there is no separate scope. We create objects that
will transform to SlotMachine Instructions _in_ the scope of the current method.
In other words they will have access to the compiler and the callable, when transforming
to SlotMachine (similar to Sol in that way). This means at compile time we
can use the frame type and constants, while we can always assume the Message (and not
much else) at runtime.
As the scope is "fixed", we will use the file scope, ie one file defines one
instruction/macro, by convention of the same name.
For starters we will use ruby syntax, with these semantics:
- only globals and message (the literal) are valid variable names
- dot will mean pointer traversal, sort of like c (no calling)
- names on right hand of dot must be instance variables of left
- equal will mean assignment in the SlotLoad kind of sense
- some macro mechanism is called for (tbd)
- maybe labels have to be created (sort of like the risc dsl)
The result of a compilation will be a SlotMachine Macro

View File

@ -1,25 +0,0 @@
module SlotLanguage
# A Assignment makes SlotLoad. That means it stores the information
# to be able to create a SlotLoad
#
# Just like the SlotLoad stores two Slots, here we store two Variables
#
class Assignment
# The two Variables that become Slots in to_slot
attr_reader :left , :right
def initialize(left , right)
@left = left
@right = right
raise "No Slot #{left}" unless left.is_a?(Variable)
raise "No Slot #{right}" unless right.is_a?(Variable)
end
# create the SlotLoad, by creating the two Slots from the Variables
def to_slot(compiler)
left_d = @left.to_slot(compiler)
right_d = @right.to_slot(compiler)
SlotMachine::SlotLoad.new("source" , left_d , right_d)
end
end
end

View File

@ -1,22 +0,0 @@
# passed in? name and cache_entry
word! = name_
cache_entry! = cache_entry_
# local var assignment
callable_method! = cache_entry.cached_type.methods
while_start_label
goto(exit_label) if( nil == callable_method)
goto(ok_label) if(callable_method.name == word)
callable_method = callable_method.next_callable
goto(while_start_label)
exit_label
MethodMissing.new(compiler.source_name , word.symbol).to_risc(compiler)
ok_label
cache_entry.cached_method = callable_method

View File

@ -1,13 +0,0 @@
module SlotLanguage
class EqualGoto
attr_reader :left , :right, :goto
def initialize( left , right)
@left = left
@right = right
end
def set_goto(go)
@goto = go
end
end
end

View File

@ -1,10 +0,0 @@
module SlotLanguage
class Goto
attr_reader :label
def initialize(label)
@label = label
end
end
end

View File

@ -1,65 +0,0 @@
module SlotLanguage
# MacroMaker instances represent Macros at the Slot level
#
# Macros are like SlotInstruction instances in that they transform to Risc
# But all SlotInstructions form a whole that lets us reduce Sol to Slot to risc.
# Each Macro on the other hand represents a functionality (kind of method)
# that can not be coded in sol. It transforms to a sequence of risc Instructions
# that can not be coded any other way. They are not Methods, as they have no
# scope, hence the name Macro.
#
# This MacroMaker is an attempt to code these kind of sequences in SlotLanguage
# The SlotCompiler is used to transform a file of SlotLanguage code into and
# array of SlotLanguage constructs, which in turn can be transformed into
# SlotInstructions.
# To start with we work backwards from existing large SlotInstructions, to
# get a list of constructs that will transform to the same SlotInstructions
# that transform to the same risc as the current large instruction (+ some redundandency)
class MacroMaker
# an array of Makers
attr_reader :source
# load slot code from a file, in a subdir code/ + filename
# use load_string to compile the content
def self.load_file(relative_name)
path = File.expand_path( "../code/#{relative_name}" , __FILE__)
load_string( File.read(path))
end
# compile the given SlotLanguage source
# the compiler returns an array of Makers which a new MacroMaker
# instance stores
# return the MacroMaker that represents the source
def self.load_string(source_code)
MacroMaker.new( SlotCompiler.compile(source_code) )
end
# must initialize with an array of Makers, which is stored
def initialize( source )
@source = source
raise "undefined source #{source}" unless source.is_a?(Array)
end
# Basically calls to_slot on each Element of the source array
#
# Thus transforming an array of Makers into a linked list of
# SlotInstructions.
# Return the head of the linked list.
def to_slot(compiler)
chain = do_link( @source.first , compiler)
rest = @source.dup
rest.shift
rest.each do |link|
chain << do_link(link , compiler)
end
chain
end
private
def do_link(link,compiler)
return link if link.is_a?(SlotMachine::Instruction)
link.to_slot(compiler)
end
end
end

View File

@ -1,127 +0,0 @@
require "parser/current"
require "ast"
module SlotLanguage
class SlotCompiler < AST::Processor
DEBUG = false
# conditionals supported, currently only equal
def self.checks
[:==]
end
def self.compile(input)
ast = Parser::CurrentRuby.parse( input )
self.new.process(ast)
end
attr_reader :labels
def initialize
@labels = {}
end
def not_implemented(node)
raise "Not implemented #{node.type}"
end
# default to error, so non implemented stuff shows early
def handler_missing(node)
not_implemented(node)
end
def on_send(statement)
kids = statement.children.dup
receiver = kids.shift
name = kids.shift
return label(name) if(name.to_s.end_with?("_label"))
return goto(name,kids) if(name == :goto)
return check(name,receiver, kids) if SlotCompiler.checks.include?(name)
return assign(receiver, name , kids) if(name.to_s.end_with?("="))
puts "Send: #{statement} " if DEBUG
var = Variable.new( name )
if(receiver)
puts "receiver at #{name} #{receiver}" if DEBUG
process(receiver).chained(var)
else
var
end
end
def on_lvar(lvar)
puts "lvar #{lvar}" if DEBUG
Variable.new(lvar.children.first )
end
def on_lvasgn( expression)
puts "i/lvasgn #{expression}" if DEBUG
var = var_for(expression.children[0])
value = process(expression.children[1])
Assignment.new(var , value)
end
alias :on_ivasgn :on_lvasgn
def on_if(expression)
puts "if #{expression}" if DEBUG
condition = process(expression.children[0])
condition.set_goto( process(expression.children[1]) )
condition
end
def on_begin(exp)
if( exp.children.length == 1)
process(exp.first)
else
process_all(exp)
end
end
def on_ivar(expression)
puts "ivar #{expression}" if DEBUG
var_for(expression.children.first)
end
private
def var_for( name )
name = name.to_s
if(name[0] == "@")
MessageVariable.new(name.to_s[1 .. -1].to_sym)
else
Variable.new(name.to_sym)
end
end
def label(name)
raise "no label #{name}" unless(name.to_s.end_with?("_label"))
if @labels.has_key?(name)
return @labels[name]
else
@labels[name] = SlotMachine::Label.new(name.to_s , name)
end
end
def goto(name , args)
# error handling would not hurt
puts "goto #{name} , #{args}" if DEBUG
label = process(args.first)
Goto.new( label )
end
def check(name , receiver , kids)
raise "Familiy too large #{kids}" if kids.length > 1
puts "Kids " + kids.to_s if DEBUG
right = process(kids.first)
case name
when :==
return EqualGoto.new(process(receiver) , right)
else
raise "Only ==, not #{name}" unless name == :==
end
end
def assign(receiver , name , kids)
receiver = process(receiver)
puts "Assign #{name} , #{receiver}" if DEBUG
raise "Only one arg #{kids}" unless kids.length == 1
right = process kids.shift
name = name.to_s[0...-1].to_sym
receiver.chained(Variable.new(name))
Assignment.new(receiver,right)
end
end
end
require_relative "variable"
require_relative "assignment"
require_relative "macro_maker"
require_relative "goto"
require_relative "equal_goto"

View File

@ -1,43 +0,0 @@
module SlotLanguage
# A Variable makes Slots. A Slot is the central SlotMachines description of a
# variable in an object. At the Language level this holds the information
# (names of variables) to be able to create the Slot instance
#
# In the SlotLanguage this is used in the Assignment. Just as a Slotload stores
# two slots to define what is loaded where, the Assignment, that creates a SlotLoad,
# uses two Variables.
class Variable
# stores the (instance) names that allow us to create a Slot
attr_reader :name , :chain
def initialize(name)
@name = name
raise "No name given #{name}" unless name.is_a?(Symbol)
end
def chained(to)
raise "Must chain to variable #{to}" unless to.is_a?(Variable)
if(@chain)
@chain.chained(to)
else
@chain = to
end
self
end
def to_slot(compiler)
SlotMachine::Slotted.for(:message , [name])
end
def to_s
str = "message.#{name}"
str += chain.to_s if @chain
str
end
end
class MessageVariable < Variable
end
class Constant < Variable
end
end

View File

@ -1,18 +0,0 @@
require_relative "../helper"
module SlotLanguage
module SlotHelper
def compile(input)
SlotCompiler.compile(input)
end
def compile_class(input)
compile(input).class
end
end
module SlotToHelper
def setup
Parfait.boot!({})
@compiler = SlotMachine::SlotCollection.compiler_for( :Space , :main,{},{})
end
end
end

View File

@ -1,2 +0,0 @@
start_label
a = b

View File

@ -1,64 +0,0 @@
require_relative "helper"
module SlotLanguage
class TestAssignment < MiniTest::Test
include SlotHelper
def compile_assign(str)
assign = compile(str)
assert_equal Assignment , assign.class
assert_equal :a , assign.left.name
assert_equal :b , assign.right.name
assign
end
def test_slot_load_rinst
assign = compile_assign("a = @b")
end
def test_slot_load_linst
assign = compile_assign("@a = b")
end
def test_slot_load_lrinst
compile_assign("@a = @b")
end
def test_assign
assign = compile_assign("a = b")
assert_equal Assignment , assign.class
end
end
class TestAssignment2 < MiniTest::Test
include SlotHelper
def test_slot_load_linst_trav
assert_equal Assignment , compile_class("@a = b.c")
end
def test_assign1
assign = compile("c = c.next")
assert_equal Assignment , assign.class
end
def test_shift
load = compile("a = b.c")
assert_equal Assignment , load.class
assert_equal :a , load.left.name
assert_equal Variable , load.right.class
end
end
class TestAssignment3 < MiniTest::Test
include SlotHelper
def test_inst_ass
assign = compile("@a.b = c")
assert_equal Assignment , assign.class
assert_equal MessageVariable , assign.left.class
assert_equal :a , assign.left.name
assert_equal Variable , assign.left.chain.class
assert_equal :b , assign.left.chain.name
end
def test_local_ass
assign = compile("a.b = c")
assert_equal Assignment , assign.class
assert_equal Variable , assign.left.class
assert_equal :a , assign.left.name
assert_equal Variable , assign.left.chain.class
assert_equal :b , assign.left.chain.name
end
end
end

View File

@ -1,92 +0,0 @@
require_relative "helper"
module SlotLanguage
class TestEqualGoto < MiniTest::Test
include SlotHelper
def do_check(check)
assert_equal EqualGoto , check.class
assert_equal Goto , check.goto.class
assert check.left.is_a?(Variable)
assert check.right.is_a?(Variable)
assert_equal :a , check.left.name
assert_equal :b , check.right.name
end
def test_equal_local
check = compile("goto(exit_label) if(a == b)")
do_check(check)
end
def test_equal_inst_left
check = compile("goto(exit_label) if(@a == b)")
do_check(check)
end
def test_equal_inst_right
check = compile("goto(exit_label) if(a == @b)")
do_check(check)
end
end
class TestEqualGotoFull < MiniTest::Test
include SlotHelper
def setup
@expr = compile("start_label;goto(start_label) if( b == c)")
end
def test_2
assert_equal Array , @expr.class
assert_equal 2 , @expr.length
end
def test_label
assert_equal SlotMachine::Label , @expr.first.class
assert_equal :start_label , @expr.first.name
end
def test_conditional
assert_equal EqualGoto , @expr.last.class
assert_equal :start_label , @expr.last.goto.label.name
end
def test_same_label
assert_equal @expr.first.object_id , @expr.last.goto.label.object_id
end
def test_expression_left
assert_equal Variable , @expr.last.left.class
assert_equal :b , @expr.last.left.name
end
def test_expression_right
assert_equal Variable , @expr.last.right.class
assert_equal :c , @expr.last.right.name
end
end
class TestEqualGotoChain < MiniTest::Test
include SlotHelper
def setup
@expr = compile("goto(start_label) if( a.b == c)")
end
def test_eq
assert_equal EqualGoto , @expr.class
end
def test_left
assert_equal Variable , @expr.left.class
assert_equal :a , @expr.left.name
end
def test_left_chain
assert_equal Variable , @expr.left.chain.class
assert_equal :b , @expr.left.chain.name
end
end
class TestEqualGotoChain2 < MiniTest::Test
include SlotHelper
def setup
@expr = compile("goto(start_label) if( a == @b.c)")
end
def test_eq
assert_equal EqualGoto , @expr.class
end
def test_left
assert_equal MessageVariable , @expr.right.class
assert_equal :b , @expr.right.name
end
def test_left_chain
assert_equal Variable , @expr.right.chain.class
assert_equal :c , @expr.right.chain.name
end
end
end

View File

@ -1,36 +0,0 @@
require_relative "helper"
module SlotLanguage
class TestGoto < MiniTest::Test
include SlotHelper
def test_goto_class
assert_equal Goto , compile_class("goto(exit_label)")
end
def test_goto_label
goto = compile("goto(exit_label)")
assert_equal SlotMachine::Label , goto.label.class
assert_equal :exit_label , goto.label.name
end
def test_label
label = compile("while_label")
assert_equal SlotMachine::Label , label.class
assert_equal :while_label , label.name
end
def test_2_label
labels = compile("exit_label;exit_label")
assert_equal :exit_label , labels[0].name
assert_equal :exit_label , labels[1].name
assert_equal labels[0].object_id , labels[1].object_id
end
def test_goto_with_label
gotos = compile("exit_label;goto(exit_label)")
assert_equal :exit_label , gotos[0].name
assert_equal :exit_label , gotos[1].label.name
assert_equal gotos[0].object_id , gotos[1].label.object_id
end
end
end

View File

@ -1,45 +0,0 @@
require_relative "helper"
module SlotLanguage
class TestMacroMakerLoad < MiniTest::Test
include SlotToHelper
def setup
super
@slot = MacroMaker.load_string( mini_file ).to_slot(@compiler)
end
def test_label
assert_equal SlotMachine::Label , @slot.class
end
def test_assign
assert_equal SlotMachine::SlotLoad , @slot.next.class
assert_equal "message.a" , @slot.next.left.to_s
assert_equal "message.b" , @slot.next.right.to_s
end
def test_length
assert_equal 2 , @slot.length
end
end
class TestMacroMakerLoad < MiniTest::Test
include SlotHelper
def check_mini(maker)
assert_equal MacroMaker , maker.class
assert_equal Array , maker.source.class
assert_equal SlotMachine::Label , maker.source.first.class
assert_equal 2 , maker.source.length
end
def mini_file
File.read(File.expand_path( "../mini.slot" , __FILE__))
end
def test_mini_file
check_mini MacroMaker.load_file("../../../test/slot_language/mini.slot")
end
def test_mini_string
check_mini MacroMaker.load_string( mini_file )
end
def test_mini_source
check_mini MacroMaker.new( SlotCompiler.compile(mini_file))
end
end
end

View File

@ -1,17 +0,0 @@
require_relative "helper"
module SlotLanguage
class TestSlotCompiler < MiniTest::Test
include SlotHelper
def test_init
assert SlotCompiler.new
end
def test_labels
assert SlotCompiler.new.labels.empty?
end
def test_basic_compile
assert_equal Variable , compile("a").class
end
end
end

View File

@ -1,35 +0,0 @@
require_relative "helper"
module SlotLanguage
class TestVariable < MiniTest::Test
include SlotHelper
def compile_var(str)
var = compile(str)
assert var.is_a?(Variable)
assert_equal :a , var.name
var
end
def test_local
assert_equal Variable , compile_var("a").class
end
def test_inst
assert_equal MessageVariable , compile_var("@a").class
end
def test_local_chain
chain = compile_var("a.b")
assert_equal Variable , chain.chain.class
assert_equal :b , chain.chain.name
end
def test_local_chain2
chain = compile_var("a.b.c")
assert_equal Variable , chain.chain.chain.class
assert_equal :c , chain.chain.chain.name
end
def test_inst_chain
chain = compile_var("@a.b")
assert_equal MessageVariable , chain.class
assert_equal Variable , chain.chain.class
assert_equal :b , chain.chain.name
end
end
end