Permalink
Browse files

implement special forms def and fn

  • Loading branch information...
1 parent e742674 commit b853be1d1edacf6444d5474b3de017b849c702ea @txus committed Sep 23, 2012
Showing with 164 additions and 29 deletions.
  1. +45 −12 lib/lambra/bootstrap.rb
  2. +114 −16 lib/lambra/bytecode_compiler.rb
  3. +1 −1 lib/lambra/library/code_loader.rb
  4. +4 −0 spec/compiler_spec.rb
View
@@ -1,6 +1,6 @@
require 'set'
-class Function
+class PrimitiveFunction
def initialize(&block)
@block = block
end
@@ -10,20 +10,53 @@ def call(*args)
end
end
+class Function
+ def initialize(blk_env)
+ @block_environment = blk_env
+ @executable = blk_env.compiled_code
+ end
+
+ def call(*args)
+ @executable.invoke(:anonymous, @executable.scope.module, Object.new, args, nil)
+ end
+
+ def to_proc
+ Proc.__from_block__(@block_environment)
+ end
+end
+
+class PrimitiveScope
+ def initialize(bindings, parent=nil)
+ @bindings = bindings
+ @parent = parent
+ end
+
+ def get(name)
+ @bindings[name] or (@parent && @parent.get(name)) or raise "Undefined primitive #{name} in scope #{self}"
+ end
+
+ def set(name, value)
+ @bindings[name] = value
+ end
+
+ def to_s
+ "#<PrimitiveScope @parent=#{@parent || 'nil'} #{@bindings}>"
+ end
+end
+
module GlobalScope
- Bootstrap = {
- ##
- # Symbol Function body
- ##
- :println => Function.new { |*args| puts *args },
- :+ => Function.new { |*args| args.inject(:+) },
- :- => Function.new { |*args| args.inject(:-) },
- :/ => Function.new { |a, b| a / b },
- :* => Function.new { |a, b| a * b },
- }
end
-Scope = GlobalScope::Bootstrap
+Primitives = PrimitiveScope.new({
+ ##
+ # Symbol Function body
+ ##
+ :println => PrimitiveFunction.new { |*args| puts *args },
+ :+ => PrimitiveFunction.new { |*args| args.inject(:+) },
+ :- => PrimitiveFunction.new { |*args| args.inject(:-) },
+ :/ => PrimitiveFunction.new { |a, b| a / b },
+ :* => PrimitiveFunction.new { |a, b| a * b },
+})
class Keyword
def initialize(name)
@@ -1,12 +1,65 @@
module Lambra
class BytecodeCompiler
- attr_reader :generator
+ 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
- SPECIAL_FORMS = %w(def)
+ SPECIAL_FORMS = %w(def fn)
+ PRIMITIVE_FORMS = %w(println + - / *)
- def initialize
+ def initialize(parent=nil)
@generator = Rubinius::Generator.new
+ parent_scope = parent ? parent.scope : nil
+ @scope = Scope.new(@generator, parent_scope)
end
def compile(ast, debugging=false)
@@ -37,13 +90,13 @@ def visit_List(o)
car = o.elements[0]
cdr = o.elements[1..-1]
- return visit_SpecialForm(car.name, cdr) if special_form?(car.name)
+ return visit_SpecialForm(car.name, cdr) if car.respond_to?(:name) && special_form?(car.name)
+ return visit_PrimitiveForm(car.name, cdr) if car.respond_to?(:name) && primitive_form?(car.name)
args = cdr.count
- visit_Symbol(car)
+ car.accept(self)
- # # TODO: lazy evaluation
cdr.each do |arg|
arg.accept(self)
end
@@ -56,20 +109,61 @@ def visit_SpecialForm(car, cdr)
when 'def'
name = cdr.shift.name
- g.push_cpath_top
- g.find_const :Scope
- g.push_literal name
cdr.first.accept(self)
- g.send :[]=, 2
+ s.set_local name
+ 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
+ end
+ end
+
+ def visit_PrimitiveForm(car, cdr)
+ g.push_cpath_top
+ g.find_const :Primitives
+ g.push_literal car
+ g.send :get, 1
+
+ cdr.each do |arg|
+ arg.accept(self)
+ end
+
+ 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)
- g.push_cpath_top
- g.find_const :Scope
- g.push_literal o.name
- g.send :fetch, 1
+ s.push_variable o.name
end
def visit_Number(o)
@@ -171,8 +265,8 @@ def visit_Map(o)
end
def finalize
- # g.local_names = s.variables
- # g.local_count = s.variables.size
+ g.local_names = s.variables
+ g.local_count = s.variables.size
g.close
g
end
@@ -197,5 +291,9 @@ def debug(gen = self.g)
def special_form?(name)
SPECIAL_FORMS.include?(name.to_s)
end
+
+ def primitive_form?(name)
+ PRIMITIVE_FORMS.include?(name.to_s)
+ end
end
end
@@ -13,7 +13,7 @@ def self.execute(ast)
require_relative '../bootstrap'
- env = Scope
+ env = GlobalScope
file = if ast.respond_to?(:filename) && ast.filename
ast.filename
View
@@ -15,4 +15,8 @@
it 'defines def' do
'(def x 42) x'.should eval_to 42
end
+
+ it 'defines fn' do
+ '((fn [x] (* x x)) 3)'.should eval_to 9
+ end
end

0 comments on commit b853be1

Please sign in to comment.