Permalink
Browse files

Working closuressss

  • Loading branch information...
1 parent eed83a6 commit 321c1190c805e7ad68235ceb4402a4a77979bbd0 @txus committed Sep 24, 2012
Showing with 227 additions and 97 deletions.
  1. +4 −2 examples/fn.clj
  2. +122 −93 lib/lambra/bytecode_compiler.rb
  3. +93 −0 lib/lambra/syntax/ast.rb
  4. +8 −2 spec/compiler_spec.rb
View
@@ -1,6 +1,8 @@
; Add two numbers.
+(def x 42)
+(def z 4)
(def adder
(fn [x y]
- (+ x y)))
+ (+ x y z)))
-(adder 123 123)
+(println (adder 123 123))
@@ -1,65 +1,65 @@
module Lambra
class BytecodeCompiler
- class Scope
- attr_reader :variables, :generator
- alias g generator
-
- def initialize(generator, parent=nil)
- @parent = parent
- @variables = []
- @generator = generator
- end
-
- def slot_for(name)
- if existing = @variables.index(name)
- existing
- else
- @variables << name
- @variables.size - 1
- end
- end
-
- def push_variable(name, current_depth = 0, g = self.g)
- if existing = @variables.index(name)
- if current_depth.zero?
- g.push_local existing
- else
- g.push_local_depth current_depth, existing
- end
- else
- @parent.push_variable(name, current_depth + 1, g)
- end
- end
-
- def set_variable(name, current_depth = 0, g = self.g)
- if existing = @variables.index(name)
- if current_depth.zero?
- g.set_local existing
- else
- g.set_local_depth current_depth, existing
- end
- else
- @parent.set_variable(name, current_depth + 1, g)
- end
- end
-
- def set_local(name)
- slot = slot_for(name)
- g.set_local slot
- end
- end
-
- attr_reader :generator, :scope
+ # class Scope
+ # attr_reader :variables, :generator
+ # alias g generator
+
+ # def initialize(generator, parent=nil)
+ # @parent = parent
+ # @variables = []
+ # @generator = generator
+ # end
+
+ # def slot_for(name)
+ # if existing = @variables.index(name)
+ # existing
+ # else
+ # @variables << name
+ # @variables.size - 1
+ # end
+ # end
+
+ # def push_variable(name, current_depth = 0, g = self.g)
+ # if existing = @variables.index(name)
+ # if current_depth.zero?
+ # g.push_local existing
+ # else
+ # g.push_local_depth current_depth, existing
+ # end
+ # else
+ # @parent.push_variable(name, current_depth + 1, g)
+ # end
+ # end
+
+ # def set_variable(name, current_depth = 0, g = self.g)
+ # if existing = @variables.index(name)
+ # if current_depth.zero?
+ # g.set_local existing
+ # else
+ # g.set_local_depth current_depth, existing
+ # end
+ # else
+ # @parent.set_variable(name, current_depth + 1, g)
+ # end
+ # end
+
+ # def set_local(name)
+ # slot = slot_for(name)
+ # g.set_local slot
+ # end
+ # end
+
+ attr_reader :generator #, :scope
alias g generator
- alias s scope
+ # alias s scope
SPECIAL_FORMS = %w(def fn)
PRIMITIVE_FORMS = %w(println + - / *)
- def initialize(parent=nil)
- @generator = Rubinius::Generator.new
- parent_scope = parent ? parent.scope : nil
- @scope = Scope.new(@generator, parent_scope)
+ def initialize(generator=nil)
+ @generator = generator || Rubinius::Generator.new
+ # parent_scope = parent ? parent.scope : nil
+ # @scope = Scope.new(@generator, parent_scope)
end
def compile(ast, debugging=false)
@@ -74,7 +74,10 @@ def compile(ast, debugging=false)
g.file = :"(lambra)"
end
- g.set_line ast.line || 1
+ line = ast.line || 1
+ g.set_line line
+
+ g.push_state Rubinius::AST::ClosedScope.new(line)
ast.accept(self)
@@ -110,33 +113,52 @@ def visit_SpecialForm(car, cdr)
name = cdr.shift.name
cdr.first.accept(self)
- s.set_local name
+
+ local = g.state.scope.new_local(name)
+ g.set_local local.slot
when 'fn'
- args = cdr.shift # a Vector
- argcount = args.elements.size
-
- # Get a new compiler
- block = BytecodeCompiler.new(self)
-
- # Configures the new generator
- # TODO Move this to a method on the compiler
- block.generator.for_block = true
- block.generator.total_args = argcount
- block.generator.required_args = argcount
- block.generator.post_args = argcount
- block.generator.cast_for_multi_block_arg unless argcount.zero?
- block.generator.set_line args.line
-
- block.visit_arguments(args.elements)
- cdr.shift.accept(block)
- block.generator.ret
-
- g.push_const :Function
- # Invoke the create block instruction
- # with the generator of the block compiler
- g.create_block block.finalize
- g.send :new, 1
+ args_vector = cdr.shift
+ arguments = Lambra::AST::ClosureArguments.new(args_vector.line, args_vector.column, args_vector)
+ closure = Lambra::AST::Closure.new(arguments.line, arguments.column, arguments, cdr.shift)
+
+ closure.accept(self)
+ end
+ end
+
+ def visit_Closure(o)
+ set_line(o)
+
+ state = g.state
+ state.scope.nest_scope o
+
+ blk_compiler = BytecodeCompiler.new(new_block_generator g, o.arguments)
+ blk = blk_compiler.generator
+
+ blk.push_state o
+ blk.state.push_super state.super
+ blk.state.push_eval state.eval
+
+ blk.state.push_name blk.name
+
+ o.arguments.accept(blk_compiler)
+ blk.state.push_block
+ o.body.accept(blk_compiler)
+ blk.state.pop_block
+ blk.ret
+ blk_compiler.finalize
+
+ g.create_block blk
+ end
+
+ def visit_ClosureArguments(o)
+ args = o.arguments
+ args.each_with_index do |a, i|
+ g.shift_array
+ local = g.state.scope.new_local(a.name)
+ g.set_local local.slot
+ g.pop
end
+ g.pop unless args.empty?
end
def visit_PrimitiveForm(car, cdr)
@@ -152,18 +174,10 @@ def visit_PrimitiveForm(car, cdr)
g.send :call, cdr.count
end
- def visit_arguments(args)
- args.each_with_index do |a, i|
- g.shift_array
- s.set_local a.name
- g.pop
- end
- g.pop unless args.empty?
- end
-
def visit_Symbol(o)
set_line(o)
- s.push_variable o.name
+ local = g.state.scope.search_local(o.name)
+ local.get_bytecode(g)
end
def visit_Number(o)
@@ -265,8 +279,9 @@ def visit_Map(o)
end
def finalize
- g.local_names = s.variables
- g.local_count = s.variables.size
+ g.local_names = g.state.scope.local_names
+ g.local_count = g.state.scope.local_count
+ g.pop_state
g.close
g
end
@@ -295,5 +310,19 @@ def special_form?(name)
def primitive_form?(name)
PRIMITIVE_FORMS.include?(name.to_s)
end
+
+ def new_block_generator(g, arguments)
+ blk = g.class.new
+ blk.name = g.state.name || :__block__
+ blk.file = g.file
+ blk.for_block = true
+
+ blk.required_args = arguments.count
+ blk.post_args = arguments.count
+ blk.total_args = arguments.count
+ blk.cast_for_multi_block_arg unless arguments.count.zero?
+
+ blk
+ end
end
end
View
@@ -1,5 +1,66 @@
module Lambra
module AST
+ module Scope
+ attr_accessor :parent
+
+ def self.included(base)
+ base.send :include, Rubinius::Compiler::LocalVariables
+ end
+
+ def nest_scope(scope)
+ scope.parent = self
+ end
+
+ # A nested scope is looking up a local variable. If the variable exists
+ # in our local variables hash, return a nested reference to it. If it
+ # exists in an enclosing scope, increment the depth of the reference
+ # when it passes through this nested scope (i.e. the depth of a
+ # reference is a function of the nested scopes it passes through from
+ # the scope it is defined in to the scope it is used in).
+ def search_local(name)
+ if variable = variables[name]
+ variable.nested_reference
+ elsif block_local?(name)
+ new_local name
+ elsif reference = @parent.search_local(name)
+ reference.depth += 1
+ reference
+ end
+ end
+
+ def block_local?(name)
+ @locals.include?(name) if @locals
+ end
+
+ def new_local(name)
+ variable = Rubinius::Compiler::LocalVariable.new allocate_slot
+ variables[name] = variable
+ end
+
+ def new_nested_local(name)
+ new_local(name).nested_reference
+ end
+
+ # If the local variable exists in this scope, set the local variable
+ # node attribute to a reference to the local variable. If the variable
+ # exists in an enclosing scope, set the local variable node attribute to
+ # a nested reference to the local variable. Otherwise, create a local
+ # variable in this scope and set the local variable node attribute.
+ def assign_local_reference(var)
+ if variable = variables[var.name]
+ var.variable = variable.reference
+ elsif block_local?(var.name)
+ variable = new_local var.name
+ var.variable = variable.reference
+ elsif reference = @parent.search_local(var.name)
+ reference.depth += 1
+ var.variable = reference
+ else
+ variable = new_local var.name
+ var.variable = variable.reference
+ end
+ end
+ end
module Visitable
def accept(visitor)
@@ -22,6 +83,12 @@ def to_sexp
end
end
+ class Sequence < Node
+ def to_sexp
+ @elements.map(&:to_sexp)
+ end
+ end
+
class Number < Node
def to_sexp
[sexp_name, @value]
@@ -89,5 +156,31 @@ def to_sexp
*@elements.map(&:to_sexp)]
end
end
+
+ class Closure < Node
+ include Scope
+
+ attr_reader :arguments, :body
+
+ def initialize(line, column, closure_arguments, body)
+ @line = line
+ @column = column
+ @arguments = closure_arguments
+ @body = body
+ end
+ end
+
+ class ClosureArguments < Node
+ attr_reader :arguments
+ def initialize(line, column, vector)
+ @line = line
+ @column = column
+ @arguments = vector.elements
+ end
+
+ def count
+ @arguments.count
+ end
+ end
end
end
Oops, something went wrong.

0 comments on commit 321c119

Please sign in to comment.