move ast to compiler

new compile module does just that: compile
Dispatch with visitor pattern
no more patching into ast
This commit is contained in:
Torsten Ruger 2015-05-04 14:22:22 +03:00
parent 92bbd70c77
commit ff22c17784
17 changed files with 151 additions and 186 deletions

View File

@ -1,11 +0,0 @@
require "ast/expression"
require_relative "basic_expressions"
require_relative "call_site_expression"
require_relative "compound_expressions"
require_relative "if_expression"
require_relative "function_expression"
require_relative "module_expression"
require_relative "operator_expressions"
require_relative "return_expression"
require_relative "while_expression"
require_relative "expression_list"

View File

@ -1,20 +0,0 @@
module Ast
class ArrayExpression < Expression
# attr_reader :values
def compile context
to.do
end
end
class AssociationExpression < Expression
# attr_reader :key , :value
def compile context
to.do
end
end
class HashExpression < ArrayExpression
def compile context
to.do
end
end
end

View File

@ -1,8 +0,0 @@
module Ast
class ExpressionList < Expression
# attr_reader :expressions
def compile method , message
expressions.collect { |part| part.compile( method, message ) }
end
end
end

View File

@ -1,9 +0,0 @@
module Ast
class OperatorExpression < Expression
# attr_reader :operator, :left, :right
def compile method , message
call = CallSiteExpression.new( operator , [right] , left )
call.compile(method,message)
end
end
end

20
lib/compiler.rb Normal file
View File

@ -0,0 +1,20 @@
module Compiler
def self.compile expression , method , message
exp_name = expression.class.split("::").last.sub("Expression","").downcase
puts "Expression #{exp_name}"
self.send exp_name.to_sym , method , message
end
end
require_relative "compiler/basic_expressions"
require_relative "compiler/call_site_expression"
require_relative "compiler/compound_expressions"
require_relative "compiler/if_expression"
require_relative "compiler/function_expression"
require_relative "compiler/module_expression"
require_relative "compiler/operator_expressions"
require_relative "compiler/return_expression"
require_relative "compiler/while_expression"
require_relative "compiler/expression_list"

View File

@ -1,12 +1,10 @@
### Ast ### Compiling
The Ast (abstract syntax tree) is created by salama-reader gem and the classes defined there The Ast (abstract syntax tree) is created by salama-reader gem and the classes defined there
### Compiling The code in this directory compiles the AST to the virtual machine code.
The code in this directory compiles the AST to the virtual machine code. If this were an interpreter, we would just walk the tree and do what it says.
If this were an intrepreter, we would just walk the tree and do what it says.
Since it's not things are a little more difficult, especially in time. Since it's not things are a little more difficult, especially in time.
When compiling we deal with two times, compile-time and run-time. When compiling we deal with two times, compile-time and run-time.
@ -17,11 +15,11 @@ Similarly, the result of compiling is two-fold: a static and a dynamic part.
- the static part are objects like the constants, but also defined classes and their methods - the static part are objects like the constants, but also defined classes and their methods
- the dynamic part is the code, which is stored as streams of instructions in the CompiledMethod - the dynamic part is the code, which is stored as streams of instructions in the CompiledMethod
Too make things a little simpler, we create a very high level instruction stream at first and then run Too make things a little simpler, we create a very high level instruction stream at first and then
transformation and optimisation passes on the stream to improve it. run transformation and optimization passes on the stream to improve it.
Each ast class gets a compile method that does the compilation. Each ast class gets a compile method that does the compilation.
#### Compiled Method and Instructions #### Compiled Method and Instructions
The first argument to the compile method is the CompiledMethod. The first argument to the compile method is the CompiledMethod.
@ -30,23 +28,23 @@ Instructions are stored as a list of Blocks, and Blocks are the smallest unit of
which is always linear. which is always linear.
Code is added to the method (using add_code), rather than working with the actual instructions. Code is added to the method (using add_code), rather than working with the actual instructions.
This is so each compiling method can just do it's bit and be unaware of the larger structure that is being created. This is so each compiling method can just do it's bit and be unaware of the larger structure
The genearal structure of the instructions is a graph (what with if's and whiles and breaks and what), that is being created.
but we build it to have one start and *one* end (return). The genearal structure of the instructions is a graph
(with if's and whiles and breaks and what), but we build it to have one start and *one* end (return).
#### Messages and frames #### Messages and frames
The virtual machine instructions obviously operate on the virtual machine.
Since the machine is virtual, we have to define it, and since it is oo we define it in objects. Since the machine is virtual, we have to define it, and since it is oo we define it in objects.
Also it is important to define how instructions operate, which is is in a physical machine would Also it is important to define how instructions operate, which is is in a physical machine would
be by changing the contents of registers or some stack. be by changing the contents of registers or some stack.
Our machine is ot a register machine, but an object machine: it operates directly on objects and Our machine is not a register machine, but an object machine: it operates directly on objects and
also has no seperate stack, only objects. There are a number of objects which are accessible, also has no separate stack, only objects. There are a number of objects which are accessible,
and one can think of these (their addresses) as register contents. and one can think of these (their addresses) as register contents.
And one wouldn't be far off as that is the implementation (And one wouldn't be far off as that is the implementation.)
The objects the machine works on are: The objects the machine works on are:
@ -55,21 +53,22 @@ The objects the machine works on are:
- Self - Self
- NewMessage - NewMessage
and working on means, these are the only objects which the machine accesses. and working on means, these are the only objects which the machine accesses.
Ie all others would have to be moved first. Ie all others would have to be moved first.
When a Method needs to make a call, or send a Message, it creates a NewMessage object. When a Method needs to make a call, or send a Message, it creates a NewMessage object.
Messages contain return addresses and arguments. Messages contain return addresses and arguments.
Then the machine must find the method to call. Then the machine must find the method to call.
This is a function of the virtual machine and is implemented in ruby. This is a function of the virtual machine and is implemented in ruby.
Then a new Method receives the Message, creates a Frame for local and temporary variables and continues execution. Then a new Method receives the Message, creates a Frame for local and temporary variables
and continues execution.
The important thing here is that Messages and Frames are normal objects. The important thing here is that Messages and Frames are normal objects.
And interestingly we can partly use ruby to find the method, so in a way it is not just a top down transformation. And interestingly we can partly use ruby to find the method, so in a way it is not just a top
Instead the sending goes back up and then down again. down transformation. Instead the sending goes back up and then down again.
The Message object is the second parameter to the compile method, the run-time part as it were. The Message object is the second parameter to the compile method, the run-time part as it were.
Why? Since it only exists at runtime: to make compile time analysis possible Why? Since it only exists at runtime: to make compile time analysis possible
@ -77,9 +76,9 @@ Why? Since it only exists at runtime: to make compile time analysis possible
Especially for those times when we can resolve the method at compile time. Especially for those times when we can resolve the method at compile time.
* *
As ruby is a dynamic language, it also compiles at run-time. This line of thought does not help As ruby is a dynamic language, it also compiles at run-time. This line of thought does not help
though as it sort of mixes the seperate times up, even they are not. though as it sort of mixes the seperate times up, even they are not.
Even in a running ruby programm the stages of compile and run are seperate. Even in a running ruby programm the stages of compile and run are seperate.
Similarly it does not help to argue that the code is static too, not dynamic, Similarly it does not help to argue that the code is static too, not dynamic,
as that leaves us with a worse working model. as that leaves us with a worse working model.

View File

@ -1,6 +1,6 @@
# collection of the simple ones, int and strings and such # collection of the simple ones, int and strings and such
module Ast module Compiler
# Constant expressions can by definition be evaluated at compile time. # Constant expressions can by definition be evaluated at compile time.
# But that does not solve their storage, ie they need to be accessible at runtime from _somewhere_ # But that does not solve their storage, ie they need to be accessible at runtime from _somewhere_
@ -9,97 +9,78 @@ module Ast
# The current approach moves the constant into a variable before using it # The current approach moves the constant into a variable before using it
# But in the future (in the one that holds great things) we optimize those unneccesay moves away # But in the future (in the one that holds great things) we optimize those unneccesay moves away
class IntegerExpression < Expression
# attr_reader :value # attr_reader :value
def compile method , message def compile_integer expession , method , message
int = Virtual::IntegerConstant.new(value) int = Virtual::IntegerConstant.new(value)
to = Virtual::NewReturn.new(Virtual::Integer , int) to = Virtual::NewReturn.new(Virtual::Integer , int)
method.add_code Virtual::Set.new( to , int) method.add_code Virtual::Set.new( to , int)
to to
end end
end
class TrueExpression < Expression def compile_true expession , method , message
def compile method , message
value = Virtual::TrueConstant.new value = Virtual::TrueConstant.new
to = Virtual::Return.new(Virtual::Reference , value) to = Virtual::Return.new(Virtual::Reference , value)
method.add_code Virtual::Set.new( to , value ) method.add_code Virtual::Set.new( to , value )
to to
end end
end
def compile_false expession , method , message
class FalseExpression < Expression
def compile method , message
value = Virtual::FalseConstant.new value = Virtual::FalseConstant.new
to = Virtual::Return.new(Virtual::Reference , value) to = Virtual::Return.new(Virtual::Reference , value)
method.add_code Virtual::Set.new( to , value ) method.add_code Virtual::Set.new( to , value )
to to
end end
end
def compile_nil expession , method , message
class NilExpression < Expression
def compile method , message
value = Virtual::NilConstant.new value = Virtual::NilConstant.new
to = Virtual::Return.new(Virtual::Reference , value) to = Virtual::Return.new(Virtual::Reference , value)
method.add_code Virtual::Set.new( to , value ) method.add_code Virtual::Set.new( to , value )
to to
end end
end
class NameExpression < Expression
# attr_reader :name # attr_reader :name
# compiling name needs to check if it's a variable and if so resolve it # 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 ussued. # otherwise it's a method without args and a send is ussued.
# this makes the namespace static, ie when eval and co are implemented method needs recompilation # this makes the namespace static, ie when eval and co are implemented method needs recompilation
def compile method , message def compile_name expession , method , message
return Virtual::Self.new( Virtual::Mystery ) if name == :self return Virtual::Self.new( Virtual::Mystery ) if expession.name == :self
if method.has_var(name) if method.has_var(expession.name)
message.compile_get(method , name ) message.compile_get(method , expession.name )
else else
raise "Unimplemented #{self}" raise "Unimplemented #{self}"
message.compile_send( method , name , Virtual::Self.new( Virtual::Mystery ) ) message.compile_send( method , expession.name , Virtual::Self.new( Virtual::Mystery ) )
end end
end end
end
class ModuleName < NameExpression
def compile method , message def compile_module expession , method , message
clazz = Virtual::BootSpace.space.get_or_create_class name clazz = Virtual::BootSpace.space.get_or_create_class name
raise "uups #{clazz}.#{name}" unless clazz raise "uups #{clazz}.#{name}" unless clazz
to = Virtual::Return.new(Virtual::Reference , clazz ) to = Virtual::Return.new(Virtual::Reference , clazz )
method.add_code Virtual::Set.new( to , clazz ) method.add_code Virtual::Set.new( to , clazz )
to to
end end
end
class StringExpression < Expression
# attr_reader :string # attr_reader :string
def compile method , message def compile_string expession , method , message
value = Virtual::StringConstant.new(string) value = Virtual::StringConstant.new(expession.string)
to = Virtual::Return.new(Virtual::Reference , value) to = Virtual::Return.new(Virtual::Reference , value)
Virtual::BootSpace.space.add_object value Virtual::BootSpace.space.add_object value
method.add_code Virtual::Set.new( to , value ) method.add_code Virtual::Set.new( to , value )
to to
end end
end
class AssignmentExpression < Expression
#attr_reader :left, :right #attr_reader :left, :right
def compile_assignment expession , method , message
def compile method , message raise "must assign to NameExpression , not #{expession.left}" unless expession.left.instance_of? NameExpression
raise "must assign to NameExpression , not #{left}" unless left.instance_of? NameExpression
r = right.compile(method,message) r = right.compile(method,message)
raise "oh noo, nil from where #{right.inspect}" unless r raise "oh noo, nil from where #{expession.right.inspect}" unless r
message.compile_set( method , left.name , r ) message.compile_set( method , expession.left.name , r )
end end
end
class VariableExpression < NameExpression def compile_variable expession, method , message
def compile method , message method.add_code Virtual::InstanceGet.new(expession.name)
method.add_code Virtual::InstanceGet.new(name)
Virtual::NewReturn.new( Virtual::Mystery ) Virtual::NewReturn.new( Virtual::Mystery )
end end
end end
end

View File

@ -1,28 +1,26 @@
module Ast module Compiler
# operators are really function calls # operators are really function calls
class CallSiteExpression < Expression
# attr_reader :name, :args , :receiver
def compile method , message # call_site - attr_reader :name, :args , :receiver
me = receiver.compile( method, message )
def compile_call_site expession , method , message
me = expession.receiver.compile( method, message )
method.add_code Virtual::NewMessage.new method.add_code Virtual::NewMessage.new
method.add_code Virtual::Set.new(Virtual::NewSelf.new(me.type), me) method.add_code Virtual::Set.new(Virtual::NewSelf.new(me.type), me)
method.add_code Virtual::Set.new(Virtual::NewName.new(), Virtual::StringConstant.new(name)) method.add_code Virtual::Set.new(Virtual::NewName.new(), Virtual::StringConstant.new(expession.name))
compiled_args = [] compiled_args = []
args.each_with_index do |arg , i| expession.args.each_with_index do |arg , i|
#compile in the running method, ie before passing control #compile in the running method, ie before passing control
val = arg.compile( method, message) val = arg.compile( method, message)
# move the compiled value to it's slot in the new message # move the compiled value to it's slot in the new message
to = Virtual::NewMessageSlot.new(i ,val.type , val) to = Virtual::NewMessageSlot.new(i ,val.type , val)
# (doing this immediately, not after the loop, so if it's a return it won't get overwritten) # (doing this immediately, not after the loop, so if it's a return it won't get overwritten)
method.add_code Virtual::Set.new(to , val ) method.add_code Virtual::Set.new(to , val )
compiled_args << to compiled_args << to
end end
method.add_code Virtual::MessageSend.new(name , me , compiled_args) #and pass control method.add_code Virtual::MessageSend.new(expession.name , me , compiled_args) #and pass control
# the effect of the method is that the NewMessage Return slot will be filled, return it # the effect of the method is that the NewMessage Return slot will be filled, return it
# (this is what is moved _inside_ above loop for such expressions that are calls (or constants)) # (this is what is moved _inside_ above loop for such expressions that are calls (or constants))
Virtual::NewReturn.new( method.return_type ) Virtual::NewReturn.new( method.return_type )
end end
end end
end

View File

@ -0,0 +1,14 @@
module Compiler
# attr_reader :values
def compile_array expession, context
to.do
end
# attr_reader :key , :value
def compile_association context
to.do
end
def compile_hash context
to.do
end
end

View File

@ -0,0 +1,6 @@
module Compiler
# list - attr_reader :expressions
def compile_list expession , method , message
expession.expressions.collect { |part| part.compile( method, message ) }
end
end

View File

@ -1,20 +1,19 @@
module Ast module Compiler
class FunctionExpression < Expression # function attr_reader :name, :params, :body , :receiver
# attr_reader :name, :params, :body , :receiver def compile_function expression, method , message
def compile method , message args = expession.params.collect do |p|
args = params.collect do |p|
raise "error, argument must be a identifier, not #{p}" unless p.is_a? NameExpression raise "error, argument must be a identifier, not #{p}" unless p.is_a? NameExpression
p.name p.name
end end
r = receiver ? receiver.compile(method,message) : Virtual::Self.new() r = expession.receiver ? expession.receiver.compile(method,message) : Virtual::Self.new()
new_method = Virtual::CompiledMethod.new(name , args , r ) new_method = Virtual::CompiledMethod.new(expession.name , args , r )
new_method.class_name = r.is_a?(Virtual::BootClass) ? r.name : method.class_name new_method.class_name = r.is_a?(Virtual::BootClass) ? r.name : method.class_name
clazz = Virtual::BootSpace.space.get_or_create_class(new_method.class_name) clazz = Virtual::BootSpace.space.get_or_create_class(new_method.class_name)
clazz.add_instance_method new_method clazz.add_instance_method new_method
#frame = frame.new_frame #frame = frame.new_frame
return_type = nil return_type = nil
body.each do |ex| expession.body.each do |ex|
return_type = ex.compile(new_method,message ) return_type = ex.compile(new_method,message )
raise return_type.inspect if return_type.is_a? Virtual::Instruction raise return_type.inspect if return_type.is_a? Virtual::Instruction
end end
@ -24,7 +23,7 @@ module Ast
def scratch def scratch
args = [] args = []
locals = {} locals = {}
params.each_with_index do |param , index| expession.params.each_with_index do |param , index|
arg = param.name arg = param.name
register = Virtual::RegisterReference.new(Virtual::RegisterMachine.instance.receiver_register).next_reg_use(index + 1) register = Virtual::RegisterReference.new(Virtual::RegisterMachine.instance.receiver_register).next_reg_use(index + 1)
arg_value = Virtual::Integer.new(register) arg_value = Virtual::Integer.new(register)
@ -33,15 +32,15 @@ module Ast
end end
# class depends on receiver # class depends on receiver
me = Virtual::Integer.new( Virtual::RegisterMachine.instance.receiver_register ) me = Virtual::Integer.new( Virtual::RegisterMachine.instance.receiver_register )
if receiver.nil? if expession.receiver.nil?
clazz = context.current_class clazz = context.current_class
else else
c = context.object_space.get_or_create_class receiver.name.to_sym c = context.object_space.get_or_create_class expession.receiver.name.to_sym
clazz = c.meta_class clazz = c.meta_class
end end
function = Virtual::Function.new(name , me , args ) function = Virtual::Function.new(name , me , args )
clazz.add_code_function function clazz.add_code_function function
parent_locals = context.locals parent_locals = context.locals
parent_function = context.function parent_function = context.function
@ -49,12 +48,12 @@ module Ast
context.function = function context.function = function
last_compiled = nil last_compiled = nil
body.each do |b| expession.body.each do |b|
puts "compiling in function #{b}" puts "compiling in function #{b}"
last_compiled = b.compile(context) last_compiled = b.compile(context)
raise "alarm #{last_compiled} \n #{b}" unless last_compiled.is_a? Virtual::Word raise "alarm #{last_compiled} \n #{b}" unless last_compiled.is_a? Virtual::Word
end end
return_reg = Virtual::Integer.new(Virtual::RegisterMachine.instance.return_register) return_reg = Virtual::Integer.new(Virtual::RegisterMachine.instance.return_register)
if last_compiled.is_a?(Virtual::IntegerConstant) or last_compiled.is_a?(Virtual::ObjectConstant) if last_compiled.is_a?(Virtual::IntegerConstant) or last_compiled.is_a?(Virtual::ObjectConstant)
return_reg.load function , last_compiled if last_compiled.register_symbol != return_reg.register_symbol return_reg.load function , last_compiled if last_compiled.register_symbol != return_reg.register_symbol
@ -62,10 +61,9 @@ module Ast
return_reg.move( function, last_compiled ) if last_compiled.register_symbol != return_reg.register_symbol return_reg.move( function, last_compiled ) if last_compiled.register_symbol != return_reg.register_symbol
end end
function.set_return return_reg function.set_return return_reg
context.locals = parent_locals context.locals = parent_locals
context.function = parent_function context.function = parent_function
function function
end end
end end
end

View File

@ -1,17 +1,16 @@
module Ast module Compiler
class IfExpression < Expression # if - attr_reader :cond, :if_true, :if_false
# attr_reader :cond, :if_true, :if_false
def compile method , message def compile_if expression , method , message
# to execute the logic as the if states it, the blocks are the other way around # to execute the logic as the if states it, the blocks are the other way around
# so we can the jump over the else if true ,and the else joins unconditionally after the true_block # so we can the jump over the else if true ,and the else joins unconditionally after the true_block
merge_block = method.new_block "if_merge" # last one, created first merge_block = method.new_block "if_merge" # last one, created first
true_block = method.new_block "if_true" # second, linked in after current, before merge true_block = method.new_block "if_true" # second, linked in after current, before merge
false_block = method.new_block "if_false" # directly next in order, ie if we don't jump we land here false_block = method.new_block "if_false" # directly next in order, ie if we don't jump we land here
is = cond.compile(method,message) is = cond.compile(method,message)
# TODO should/will use different branches for different conditions. # TODO should/will use different branches for different conditions.
# just a scetch : cond_val = cond_val.is_true?(method) unless cond_val.is_a? Virtual::BranchCondition # just a scetch : cond_val = cond_val.is_true?(method) unless cond_val.is_a? Virtual::BranchCondition
method.add_code Virtual::IsTrueBranch.new( true_block ) method.add_code Virtual::IsTrueBranch.new( true_block )
@ -38,5 +37,4 @@ module Ast
#TODO should return the union of the true and false types #TODO should return the union of the true and false types
last last
end end
end end
end

View File

@ -1,26 +1,22 @@
module Ast module Compiler
class ModuleExpression < Expression # module attr_reader :name ,:expressions
# attr_reader :name ,:expressions def compile_module expression , context
def compile context
return clazz return clazz
end end
end
class ClassExpression < ModuleExpression def compile_class expression , method , message
def compile method , message
clazz = ::Virtual::BootSpace.space.get_or_create_class name clazz = ::Virtual::BootSpace.space.get_or_create_class name
puts "Created class #{clazz.name.inspect}" puts "Created class #{clazz.name.inspect}"
expressions.each do |expression| expression.expressions.each do |expr|
# check if it's a function definition and add # check if it's a function definition and add
# if not, execute it, but that does means we should be in salama (executable), not ruby. ie throw an error for now # if not, execute it, but that does means we should be in salama (executable), not ruby. ie throw an error for now
raise "only functions for now #{expression.inspect}" unless expression.is_a? Ast::FunctionExpression raise "only functions for now #{expr.inspect}" unless expr.is_a? Ast::FunctionExpression
#puts "compiling expression #{expression}" #puts "compiling expression #{expression}"
expression_value = expression.compile(method,message ) expression_value = expr.compile(method,message )
clazz.add_instance_method(expression_value) clazz.add_instance_method(expression_value)
#puts "compiled expression #{expression_value.inspect}" #puts "compiled expression #{expression_value.inspect}"
end end
return clazz return clazz
end end
end end
end

View File

@ -0,0 +1,7 @@
module Compiler
# operator attr_reader :operator, :left, :right
def compile_operator expression, method , message
call = CallSiteExpression.new( operator , [right] , left )
call.compile(method,message)
end
end

View File

@ -1,10 +1,10 @@
module Ast module Compiler
class ReturnExpression < Expression
# attr_reader :expression # return attr_reader :expression
def compile scope ,method def compile_return expression, scope ,method
Virtual::Reference.new Virtual::Reference.new
end end
def sc def old
into = context.function into = context.function
puts "compiling return expression #{expression}, now return in return_regsiter" puts "compiling return expression #{expression}, now return in return_regsiter"
expression_value = expression.compile(context) expression_value = expression.compile(context)
@ -12,14 +12,11 @@ module Ast
return_reg = Virtual::Integer.new(Virtual::RegisterMachine.instance.return_register) return_reg = Virtual::Integer.new(Virtual::RegisterMachine.instance.return_register)
if expression_value.is_a?(Virtual::IntegerConstant) or expression_value.is_a?(Virtual::ObjectConstant) if expression_value.is_a?(Virtual::IntegerConstant) or expression_value.is_a?(Virtual::ObjectConstant)
return_reg.load into , expression_value return_reg.load into , expression_value
else else
return_reg.move( into, expression_value ) if expression_value.register_symbol != return_reg.register_symbol return_reg.move( into, expression_value ) if expression_value.register_symbol != return_reg.register_symbol
end end
#function.set_return return_reg #function.set_return return_reg
return return_reg return return_reg
end end
end end
end

View File

@ -1,16 +1,16 @@
module Ast module Compiler
class WhileExpression < Expression
# attr_reader :condition, :body # while- attr_reader :condition, :body
def compile method , message def compile_while expression, method , message
start = Virtual::Label.new("while_start") start = Virtual::Label.new("while_start")
method.add_code start method.add_code start
is = condition.compile(method,message) is = expression.condition.compile(method,message)
branch = Virtual::IsTrueBranch.new "while" branch = Virtual::IsTrueBranch.new "while"
merge = Virtual::Label.new(branch.name) merge = Virtual::Label.new(branch.name)
branch.other = merge #false jumps to end of while branch.other = merge #false jumps to end of while
method.add_code branch method.add_code branch
last = is last = is
body.each do |part| expression.body.each do |part|
last = part.compile(method,message ) last = part.compile(method,message )
raise part.inspect if last.nil? raise part.inspect if last.nil?
end end
@ -22,5 +22,4 @@ module Ast
method.current = merge method.current = merge
last last
end end
end end
end

View File

@ -4,7 +4,7 @@ module Parfait
eval(File.open("./lib/parfait/hash.rb").read) eval(File.open("./lib/parfait/hash.rb").read)
end end
require "ast/all" require "compiler"
require "stream_reader" require "stream_reader"
require "elf/object_writer" require "elf/object_writer"
require 'salama-reader' require 'salama-reader'