apricot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
 - data/.rspec +1 -0
 - data/.ruby-version +1 -0
 - data/.travis.yml +7 -0
 - data/Gemfile +6 -0
 - data/Gemfile.lock +26 -0
 - data/README.md +90 -0
 - data/Rakefile +9 -0
 - data/apricot.gemspec +22 -0
 - data/bin/apricot +58 -0
 - data/examples/bot.apr +23 -0
 - data/examples/cinch-bot.apr +12 -0
 - data/examples/hanoi.apr +10 -0
 - data/examples/hello.apr +1 -0
 - data/examples/plot.apr +28 -0
 - data/examples/quine.apr +1 -0
 - data/kernel/core.apr +928 -0
 - data/lib/apricot/ast/identifier.rb +111 -0
 - data/lib/apricot/ast/list.rb +99 -0
 - data/lib/apricot/ast/literals.rb +240 -0
 - data/lib/apricot/ast/node.rb +45 -0
 - data/lib/apricot/ast/scopes.rb +147 -0
 - data/lib/apricot/ast/toplevel.rb +66 -0
 - data/lib/apricot/ast/variables.rb +64 -0
 - data/lib/apricot/ast.rb +3 -0
 - data/lib/apricot/compiler.rb +55 -0
 - data/lib/apricot/cons.rb +27 -0
 - data/lib/apricot/errors.rb +38 -0
 - data/lib/apricot/generator.rb +15 -0
 - data/lib/apricot/identifier.rb +91 -0
 - data/lib/apricot/list.rb +96 -0
 - data/lib/apricot/macroexpand.rb +47 -0
 - data/lib/apricot/misc.rb +11 -0
 - data/lib/apricot/namespace.rb +59 -0
 - data/lib/apricot/parser.rb +541 -0
 - data/lib/apricot/printers.rb +12 -0
 - data/lib/apricot/repl.rb +254 -0
 - data/lib/apricot/ruby_ext.rb +254 -0
 - data/lib/apricot/seq.rb +44 -0
 - data/lib/apricot/special_forms.rb +735 -0
 - data/lib/apricot/stages.rb +60 -0
 - data/lib/apricot/version.rb +3 -0
 - data/lib/apricot.rb +30 -0
 - data/spec/compiler_spec.rb +499 -0
 - data/spec/identifier_spec.rb +58 -0
 - data/spec/list_spec.rb +96 -0
 - data/spec/parser_spec.rb +312 -0
 - data/spec/spec_helper.rb +10 -0
 - metadata +188 -0
 
| 
         @@ -0,0 +1,111 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Apricot
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Identifier < Node
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def initialize(line, name)
         
     | 
| 
      
 5 
     | 
    
         
            +
                    super(line)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    @id = Apricot::Identifier.intern(name)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def reference(g)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @reference ||= if name == :self
         
     | 
| 
      
 11 
     | 
    
         
            +
                                     SelfReference.new
         
     | 
| 
      
 12 
     | 
    
         
            +
                                   elsif qualified?
         
     | 
| 
      
 13 
     | 
    
         
            +
                                     NamespaceReference.new(unqualified_name, ns)
         
     | 
| 
      
 14 
     | 
    
         
            +
                                   else
         
     | 
| 
      
 15 
     | 
    
         
            +
                                     g.scope.find_var(name)
         
     | 
| 
      
 16 
     | 
    
         
            +
                                   end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def name
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @id.name
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def constant?
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @id.constant?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def qualified?
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @id.qualified?
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def ns
         
     | 
| 
      
 32 
     | 
    
         
            +
                    @id.ns
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def unqualified_name
         
     | 
| 
      
 36 
     | 
    
         
            +
                    @id.unqualified_name
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def const_names
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @id.const_names
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def bytecode(g)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    pos(g)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    if constant?
         
     | 
| 
      
 47 
     | 
    
         
            +
                      g.push_const const_names.first
         
     | 
| 
      
 48 
     | 
    
         
            +
                      const_names.drop(1).each {|n| g.find_const n }
         
     | 
| 
      
 49 
     | 
    
         
            +
                    else
         
     | 
| 
      
 50 
     | 
    
         
            +
                      reference(g).bytecode(g)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # called by (def <identifier> <value>)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def assign_bytecode(g, value)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    if constant?
         
     | 
| 
      
 57 
     | 
    
         
            +
                      if const_names.length == 1
         
     | 
| 
      
 58 
     | 
    
         
            +
                        g.push_scope
         
     | 
| 
      
 59 
     | 
    
         
            +
                      else
         
     | 
| 
      
 60 
     | 
    
         
            +
                        g.push_const const_names[0]
         
     | 
| 
      
 61 
     | 
    
         
            +
                        const_names[1..-2].each {|n| g.find_const n }
         
     | 
| 
      
 62 
     | 
    
         
            +
                      end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                      g.push_literal const_names.last
         
     | 
| 
      
 65 
     | 
    
         
            +
                      value.bytecode(g)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      g.send :const_set, 2
         
     | 
| 
      
 67 
     | 
    
         
            +
                    else
         
     | 
| 
      
 68 
     | 
    
         
            +
                      g.compile_error "Can't change the value of self" if name == :self
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      g.push_const :Apricot
         
     | 
| 
      
 71 
     | 
    
         
            +
                      g.send :current_namespace, 0
         
     | 
| 
      
 72 
     | 
    
         
            +
                      g.push_literal name
         
     | 
| 
      
 73 
     | 
    
         
            +
                      value.bytecode(g)
         
     | 
| 
      
 74 
     | 
    
         
            +
                      g.send :set_var, 2
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  def quote_bytecode(g)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    pos(g)
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    g.push_const :Apricot
         
     | 
| 
      
 82 
     | 
    
         
            +
                    g.find_const :Identifier
         
     | 
| 
      
 83 
     | 
    
         
            +
                    g.push_literal name
         
     | 
| 
      
 84 
     | 
    
         
            +
                    g.send :intern, 1
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  def meta(g)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    ref = reference(g)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    ref.is_a?(NamespaceReference) && ref.meta
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  def namespace_fn?(g)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    ref = reference(g)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    ref.is_a?(NamespaceReference) && ref.fn?
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def module_method?(g)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    ref = reference(g)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    ref.is_a?(NamespaceReference) && ref.method?
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                  def to_value
         
     | 
| 
      
 103 
     | 
    
         
            +
                    @id
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  def node_equal?(other)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    self.name == other.name
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,99 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Apricot::AST
         
     | 
| 
      
 2 
     | 
    
         
            +
              class List < Node
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :elements
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(line, elements)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  super(line)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @elements = elements
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def bytecode(g, macroexpand = true)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  if @elements.empty?
         
     | 
| 
      
 14 
     | 
    
         
            +
                    quote_bytecode(g)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    return
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  callee = @elements.first
         
     | 
| 
      
 19 
     | 
    
         
            +
                  args = @elements.drop(1)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  # Handle special forms such as def, let, fn, quote, etc
         
     | 
| 
      
 22 
     | 
    
         
            +
                  if callee.is_a?(Identifier) && special = Apricot::SpecialForm[callee.name]
         
     | 
| 
      
 23 
     | 
    
         
            +
                    special.bytecode(g, args)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    return
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  if macroexpand
         
     | 
| 
      
 28 
     | 
    
         
            +
                    form = Node.from_value(Apricot.macroexpand(self.to_value), line)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    # Avoid recursing and macroexpanding again if expansion returns a list
         
     | 
| 
      
 31 
     | 
    
         
            +
                    if form.is_a?(List)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      form.bytecode(g, false)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    else
         
     | 
| 
      
 34 
     | 
    
         
            +
                      form.bytecode(g)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                    return
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  # Handle (foo ...) and (Foo/bar ...) calls
         
     | 
| 
      
 40 
     | 
    
         
            +
                  if callee.is_a?(Identifier)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    meta = callee.meta(g)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    # Handle inlinable function calls
         
     | 
| 
      
 44 
     | 
    
         
            +
                    if meta && meta[:inline] && (!meta[:'inline-arities'] ||
         
     | 
| 
      
 45 
     | 
    
         
            +
                                                 meta[:'inline-arities'].apricot_call(args.length))
         
     | 
| 
      
 46 
     | 
    
         
            +
                      # Apply the inliner macro to the arguments and compile the result.
         
     | 
| 
      
 47 
     | 
    
         
            +
                      inliner = meta[:inline]
         
     | 
| 
      
 48 
     | 
    
         
            +
                      args.map!(&:to_value)
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 51 
     | 
    
         
            +
                        inlined_form = inliner.apricot_call(*args)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      rescue => e
         
     | 
| 
      
 53 
     | 
    
         
            +
                        g.compile_error "Inliner macro for '#{callee.name}' raised an exception:\n  #{e}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                      end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      Node.from_value(inlined_form, line).bytecode(g)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      return
         
     | 
| 
      
 58 
     | 
    
         
            +
                    elsif callee.namespace_fn?(g) || callee.module_method?(g)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      ns_id = Apricot::Identifier.intern(callee.ns.name)
         
     | 
| 
      
 60 
     | 
    
         
            +
                      g.push_const ns_id.const_names.first
         
     | 
| 
      
 61 
     | 
    
         
            +
                      ns_id.const_names.drop(1).each {|n| g.find_const(n) }
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                      args.each {|arg| arg.bytecode(g) }
         
     | 
| 
      
 64 
     | 
    
         
            +
                      g.send callee.unqualified_name, args.length
         
     | 
| 
      
 65 
     | 
    
         
            +
                      return
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # Handle everything else
         
     | 
| 
      
 70 
     | 
    
         
            +
                  callee.bytecode(g)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  args.each {|arg| arg.bytecode(g) }
         
     | 
| 
      
 72 
     | 
    
         
            +
                  g.send :apricot_call, args.length
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def quote_bytecode(g)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  g.push_const :Apricot
         
     | 
| 
      
 77 
     | 
    
         
            +
                  g.find_const :List
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  if @elements.empty?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    g.find_const :EmptyList
         
     | 
| 
      
 81 
     | 
    
         
            +
                  else
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @elements.each {|e| e.quote_bytecode(g) }
         
     | 
| 
      
 83 
     | 
    
         
            +
                    g.send :[], @elements.length
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 88 
     | 
    
         
            +
                  Apricot::List[*@elements.map(&:to_value)]
         
     | 
| 
      
 89 
     | 
    
         
            +
                end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def node_equal?(other)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  self.elements == other.elements
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                def [](*i)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @elements[*i]
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,240 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Apricot::AST
         
     | 
| 
      
 2 
     | 
    
         
            +
              class BasicLiteral < Node
         
     | 
| 
      
 3 
     | 
    
         
            +
                def quote_bytecode(g)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  bytecode(g)
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              class Literal < BasicLiteral
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :value
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(line, value)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  super(line)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @value = value
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  g.push @value
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 22 
     | 
    
         
            +
                  case @value
         
     | 
| 
      
 23 
     | 
    
         
            +
                  when :true then true
         
     | 
| 
      
 24 
     | 
    
         
            +
                  when :false then false
         
     | 
| 
      
 25 
     | 
    
         
            +
                  when :nil then nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                  else @value
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def node_equal?(other)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  self.value == other.value
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def self.new_integer(line, value)
         
     | 
| 
      
 36 
     | 
    
         
            +
                case value
         
     | 
| 
      
 37 
     | 
    
         
            +
                when Bignum
         
     | 
| 
      
 38 
     | 
    
         
            +
                  BignumLiteral.new(line, value)
         
     | 
| 
      
 39 
     | 
    
         
            +
                when Fixnum
         
     | 
| 
      
 40 
     | 
    
         
            +
                  Literal.new(line, value)
         
     | 
| 
      
 41 
     | 
    
         
            +
                else
         
     | 
| 
      
 42 
     | 
    
         
            +
                  raise TypeError, "#{value} is not an integer"
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              class BignumLiteral < Literal
         
     | 
| 
      
 47 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  g.push_unique_literal @value
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              class FloatLiteral < Literal
         
     | 
| 
      
 54 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  g.push_unique_literal @value
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              class SymbolLiteral < Literal
         
     | 
| 
      
 61 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  g.push_literal @value
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              class StringLiteral < Literal
         
     | 
| 
      
 68 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  g.push_literal @value
         
     | 
| 
      
 71 
     | 
    
         
            +
                  g.string_dup # Duplicate string to avoid mutating the literal
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              class RationalLiteral < BasicLiteral
         
     | 
| 
      
 76 
     | 
    
         
            +
                attr_reader :numerator, :denominator
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                def initialize(line, numerator, denominator)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  super(line)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  @numerator = numerator
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @denominator = denominator
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  # A rational literal should only be converted to a Rational the first
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # time it is encountered. We push a literal nil here, and then overwrite
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # the literal value with the created Rational if it is nil, i.e. the
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # first time only. Subsequent encounters will use the previously created
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # Rational. This idea was copied from Rubinius::AST::RegexLiteral.
         
     | 
| 
      
 92 
     | 
    
         
            +
                  idx = g.add_literal(nil)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  g.push_literal_at idx
         
     | 
| 
      
 94 
     | 
    
         
            +
                  g.dup
         
     | 
| 
      
 95 
     | 
    
         
            +
                  g.is_nil
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  lbl = g.new_label
         
     | 
| 
      
 98 
     | 
    
         
            +
                  g.gif lbl
         
     | 
| 
      
 99 
     | 
    
         
            +
                  g.pop
         
     | 
| 
      
 100 
     | 
    
         
            +
                  g.push_self
         
     | 
| 
      
 101 
     | 
    
         
            +
                  g.push @numerator
         
     | 
| 
      
 102 
     | 
    
         
            +
                  g.push @denominator
         
     | 
| 
      
 103 
     | 
    
         
            +
                  g.send :Rational, 2, true
         
     | 
| 
      
 104 
     | 
    
         
            +
                  g.set_literal idx
         
     | 
| 
      
 105 
     | 
    
         
            +
                  lbl.set!
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 109 
     | 
    
         
            +
                  Rational(@numerator, @denominator)
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                def node_equal?(other)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  self.numerator == other.numerator && self.denominator == other.denominator
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
              end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
              class RegexLiteral < BasicLiteral
         
     | 
| 
      
 118 
     | 
    
         
            +
                attr_accessor :pattern, :options
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def initialize(line, pattern, options)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  super(line)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  @pattern = pattern
         
     | 
| 
      
 123 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                def bytecode(g)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                  idx = g.add_literal(nil)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  g.push_literal_at idx
         
     | 
| 
      
 131 
     | 
    
         
            +
                  g.dup
         
     | 
| 
      
 132 
     | 
    
         
            +
                  g.is_nil
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  lbl = g.new_label
         
     | 
| 
      
 135 
     | 
    
         
            +
                  g.gif lbl
         
     | 
| 
      
 136 
     | 
    
         
            +
                  g.pop
         
     | 
| 
      
 137 
     | 
    
         
            +
                  g.push_const :Regexp
         
     | 
| 
      
 138 
     | 
    
         
            +
                  g.push_literal @pattern
         
     | 
| 
      
 139 
     | 
    
         
            +
                  g.push @options
         
     | 
| 
      
 140 
     | 
    
         
            +
                  g.send :new, 2
         
     | 
| 
      
 141 
     | 
    
         
            +
                  g.set_literal idx
         
     | 
| 
      
 142 
     | 
    
         
            +
                  lbl.set!
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 146 
     | 
    
         
            +
                  Regexp.new(@pattern, @options)
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                def node_equal?(other)
         
     | 
| 
      
 150 
     | 
    
         
            +
                  self.pattern == other.pattern && self.options == other.options
         
     | 
| 
      
 151 
     | 
    
         
            +
                end
         
     | 
| 
      
 152 
     | 
    
         
            +
              end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
              class CollectionLiteral < Node
         
     | 
| 
      
 155 
     | 
    
         
            +
                attr_reader :elements
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                def initialize(line, elements)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  super(line)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  @elements = elements
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                def quote_bytecode(g)
         
     | 
| 
      
 163 
     | 
    
         
            +
                  bytecode(g, true)
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                def node_equal?(other)
         
     | 
| 
      
 167 
     | 
    
         
            +
                  self.elements == other.elements
         
     | 
| 
      
 168 
     | 
    
         
            +
                end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                def [](*i)
         
     | 
| 
      
 171 
     | 
    
         
            +
                  @elements[*i]
         
     | 
| 
      
 172 
     | 
    
         
            +
                end
         
     | 
| 
      
 173 
     | 
    
         
            +
              end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
              class ArrayLiteral < CollectionLiteral
         
     | 
| 
      
 176 
     | 
    
         
            +
                def bytecode(g, quoted = false)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                  @elements.each {|e| quoted ? e.quote_bytecode(g) : e.bytecode(g) }
         
     | 
| 
      
 180 
     | 
    
         
            +
                  g.make_array @elements.length
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 184 
     | 
    
         
            +
                  @elements.map(&:to_value)
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
              end
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
              class HashLiteral < CollectionLiteral
         
     | 
| 
      
 189 
     | 
    
         
            +
                def bytecode(g, quoted = false)
         
     | 
| 
      
 190 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                  # Create a new Hash
         
     | 
| 
      
 193 
     | 
    
         
            +
                  g.push_const :Hash
         
     | 
| 
      
 194 
     | 
    
         
            +
                  g.push(@elements.length / 2)
         
     | 
| 
      
 195 
     | 
    
         
            +
                  g.send :new_from_literal, 1
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  # Add keys and values
         
     | 
| 
      
 198 
     | 
    
         
            +
                  @elements.each_slice(2) do |k, v|
         
     | 
| 
      
 199 
     | 
    
         
            +
                    g.dup # The Hash
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                    if quoted
         
     | 
| 
      
 202 
     | 
    
         
            +
                      k.quote_bytecode(g)
         
     | 
| 
      
 203 
     | 
    
         
            +
                      v.quote_bytecode(g)
         
     | 
| 
      
 204 
     | 
    
         
            +
                    else
         
     | 
| 
      
 205 
     | 
    
         
            +
                      k.bytecode(g)
         
     | 
| 
      
 206 
     | 
    
         
            +
                      v.bytecode(g)
         
     | 
| 
      
 207 
     | 
    
         
            +
                    end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                    g.send :[]=, 2
         
     | 
| 
      
 210 
     | 
    
         
            +
                    g.pop # []= leaves v on the stack
         
     | 
| 
      
 211 
     | 
    
         
            +
                  end
         
     | 
| 
      
 212 
     | 
    
         
            +
                end
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 215 
     | 
    
         
            +
                  h = {}
         
     | 
| 
      
 216 
     | 
    
         
            +
                  @elements.each_slice(2) {|k,v| h[k.to_value] = v.to_value }
         
     | 
| 
      
 217 
     | 
    
         
            +
                  h
         
     | 
| 
      
 218 
     | 
    
         
            +
                end
         
     | 
| 
      
 219 
     | 
    
         
            +
              end
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
              class SetLiteral < CollectionLiteral
         
     | 
| 
      
 222 
     | 
    
         
            +
                def bytecode(g, quoted = false)
         
     | 
| 
      
 223 
     | 
    
         
            +
                  pos(g)
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                  g.push_const :Set
         
     | 
| 
      
 226 
     | 
    
         
            +
                  g.send :new, 0 # TODO: Inline this new?
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                  @elements.each do |e|
         
     | 
| 
      
 229 
     | 
    
         
            +
                    quoted ? e.quote_bytecode(g) : e.bytecode(g)
         
     | 
| 
      
 230 
     | 
    
         
            +
                    g.send :add, 1
         
     | 
| 
      
 231 
     | 
    
         
            +
                  end
         
     | 
| 
      
 232 
     | 
    
         
            +
                end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                def to_value
         
     | 
| 
      
 235 
     | 
    
         
            +
                  s = Set.new
         
     | 
| 
      
 236 
     | 
    
         
            +
                  @elements.each {|e| s << e.to_value }
         
     | 
| 
      
 237 
     | 
    
         
            +
                  s
         
     | 
| 
      
 238 
     | 
    
         
            +
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
              end
         
     | 
| 
      
 240 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Apricot
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Node
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :line
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(line)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @line = line
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def pos(g)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    g.set_line(@line)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def ==(other)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    return true if self.equal?(other)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    return false unless self.class == other.class
         
     | 
| 
      
 17 
     | 
    
         
            +
                    self.node_equal?(other)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def self.from_value(val, line = 0)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    case val
         
     | 
| 
      
 22 
     | 
    
         
            +
                    when true     then Literal.new(line, :true)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    when false    then Literal.new(line, :false)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    when nil      then Literal.new(line, :nil)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    when Integer  then AST.new_integer(line, val)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    when Symbol   then SymbolLiteral.new(line, val)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    when Float    then FloatLiteral.new(line, val)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    when String   then StringLiteral.new(line, val)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    when Rational then RationalLiteral.new(line, val.numerator, val.denominator)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    when Regexp   then RegexLiteral.new(line, val.source, val.options)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    when Array    then ArrayLiteral.new(line, val.map {|x| from_value x, line})
         
     | 
| 
      
 32 
     | 
    
         
            +
                    when Set      then SetLiteral.new(line, val.map {|x| from_value x, line})
         
     | 
| 
      
 33 
     | 
    
         
            +
                    when Apricot::Identifier then Identifier.new(line, val.name)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    when Apricot::Seq        then List.new(line, val.map {|x| from_value x, line})
         
     | 
| 
      
 35 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 36 
     | 
    
         
            +
                      elems = []
         
     | 
| 
      
 37 
     | 
    
         
            +
                      val.each_pair {|k,v| elems << from_value(k, line) << from_value(v, line) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                      HashLiteral.new(line, elems)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    else
         
     | 
| 
      
 40 
     | 
    
         
            +
                      raise TypeError, "No AST node for #{val} (#{val.class})"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,147 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Apricot
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                # This is a scope with real local variable storage, i.e. it is part of a
         
     | 
| 
      
 4 
     | 
    
         
            +
                # block of code like a fn or the top level program. Let scopes do not have
         
     | 
| 
      
 5 
     | 
    
         
            +
                # storage and must ask for storage from one of these.
         
     | 
| 
      
 6 
     | 
    
         
            +
                module StorageScope
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def variable_names
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @variable_names ||= []
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def store_new_local(name)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    slot = next_slot
         
     | 
| 
      
 13 
     | 
    
         
            +
                    variable_names << name
         
     | 
| 
      
 14 
     | 
    
         
            +
                    slot
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def next_slot
         
     | 
| 
      
 18 
     | 
    
         
            +
                    variable_names.size
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def local_count
         
     | 
| 
      
 22 
     | 
    
         
            +
                    variable_names.size
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def local_names
         
     | 
| 
      
 26 
     | 
    
         
            +
                    variable_names
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                class Scope
         
     | 
| 
      
 31 
     | 
    
         
            +
                  attr_reader :parent, :variables
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # The loop label stores the code location where a (recur) form should
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # jump to. The secondary loop label is used in the case of recur in a fn
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # overload with variadic arguments. If the array passed for the variadic
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # arguments in the recur is empty, it should instead jump to the
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # matching non-variadic overload, if applicable.
         
     | 
| 
      
 37 
     | 
    
         
            +
                  attr_accessor :loop_label, :secondary_loop_label
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def initialize(parent)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @parent = parent
         
     | 
| 
      
 41 
     | 
    
         
            +
                    @variables = {}
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @loop_label = nil
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                class FnScope < Scope
         
     | 
| 
      
 47 
     | 
    
         
            +
                  attr_reader :name, :self_reference
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def initialize(parent, name)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    super(parent)
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    if name
         
     | 
| 
      
 53 
     | 
    
         
            +
                      @name = name
         
     | 
| 
      
 54 
     | 
    
         
            +
                      name_slot = @parent.store_new_local(name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      @self_reference = LocalReference.new(name_slot, 1)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  # An identifier or a nested scope is looking up a variable. If the
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # variable is found here, return a reference to it. Otherwise look it up
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # on the parent and increment its depth because it is beyond the bounds
         
     | 
| 
      
 62 
     | 
    
         
            +
                  # of the current block of code (fn).
         
     | 
| 
      
 63 
     | 
    
         
            +
                  def find_var(name, depth = 0)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    return @self_reference if name == @name
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    @parent.find_var(name, depth + 1)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # A (recur) is looking for a recursion target (ie. a loop or a fn
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # overload scope).
         
     | 
| 
      
 71 
     | 
    
         
            +
                  def find_recur_target
         
     | 
| 
      
 72 
     | 
    
         
            +
                    @parent.find_recur_target
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                class OverloadScope < Scope
         
     | 
| 
      
 77 
     | 
    
         
            +
                  include StorageScope
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  attr_accessor :splat
         
     | 
| 
      
 80 
     | 
    
         
            +
                  alias_method :splat?, :splat
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  attr_accessor :block_arg
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  def initialize(parent_fn)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    super(parent_fn)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  # An identifier or a nested scope is looking up a variable. If the
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # variable is found here, return a reference to it. Otherwise look it up
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # on the parent (a fn). Don't increase the depth, the lookup on the fn
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # will do that, and if we do it twice then the generated
         
     | 
| 
      
 92 
     | 
    
         
            +
                  # push_local_depth instructions look up too many scopes.
         
     | 
| 
      
 93 
     | 
    
         
            +
                  def find_var(name, depth = 0)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    if slot = @variables[name]
         
     | 
| 
      
 95 
     | 
    
         
            +
                      LocalReference.new(slot, depth)
         
     | 
| 
      
 96 
     | 
    
         
            +
                    else
         
     | 
| 
      
 97 
     | 
    
         
            +
                      @parent.find_var(name, depth)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  # Create a new local on the current level.
         
     | 
| 
      
 102 
     | 
    
         
            +
                  def new_local(name)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    name = name.name if name.is_a? Identifier
         
     | 
| 
      
 104 
     | 
    
         
            +
                    @variables[name] = store_new_local(name)
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  # A (recur) is looking for a recursion target. This, being a fn
         
     | 
| 
      
 108 
     | 
    
         
            +
                  # overload, is one.
         
     | 
| 
      
 109 
     | 
    
         
            +
                  def find_recur_target
         
     | 
| 
      
 110 
     | 
    
         
            +
                    self
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                # The let scope doesn't have real storage for locals. It stores its locals
         
     | 
| 
      
 115 
     | 
    
         
            +
                # on the nearest enclosing real scope, which is any separate block of code
         
     | 
| 
      
 116 
     | 
    
         
            +
                # such as a fn, defn, defmacro or the top level of the program.
         
     | 
| 
      
 117 
     | 
    
         
            +
                class LetScope < Scope
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # An identifier or a nested scope is looking up a variable.
         
     | 
| 
      
 119 
     | 
    
         
            +
                  def find_var(name, depth = 0)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    if slot = @variables[name]
         
     | 
| 
      
 121 
     | 
    
         
            +
                      LocalReference.new(slot, depth)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    else
         
     | 
| 
      
 123 
     | 
    
         
            +
                      @parent.find_var(name, depth)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  # Create a new local on the current level, with storage on the nearest
         
     | 
| 
      
 128 
     | 
    
         
            +
                  # enclosing real scope.
         
     | 
| 
      
 129 
     | 
    
         
            +
                  def new_local(name)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    name = name.name if name.is_a? Identifier
         
     | 
| 
      
 131 
     | 
    
         
            +
                    @variables[name] = @parent.store_new_local(name)
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  # A deeper let is asking for a new local slot. Pass it along to the
         
     | 
| 
      
 135 
     | 
    
         
            +
                  # parent so it eventually reaches a real scope.
         
     | 
| 
      
 136 
     | 
    
         
            +
                  def store_new_local(name)
         
     | 
| 
      
 137 
     | 
    
         
            +
                    @parent.store_new_local(name)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  # A (recur) is looking for a recursion target. This is one only if it is
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # a (loop) form.
         
     | 
| 
      
 142 
     | 
    
         
            +
                  def find_recur_target
         
     | 
| 
      
 143 
     | 
    
         
            +
                    loop_label ? self : @parent.find_recur_target
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
                end
         
     | 
| 
      
 146 
     | 
    
         
            +
              end
         
     | 
| 
      
 147 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Apricot
         
     | 
| 
      
 2 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 3 
     | 
    
         
            +
                class TopLevel < Node
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include StorageScope
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_reader :elements, :file
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def initialize(elements, file, line = 1, evaluate = false)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @elements = elements
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @file = file
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @line = line
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @evaluate = evaluate
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def bytecode(g)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    g.name = :__top_level__
         
     | 
| 
      
 17 
     | 
    
         
            +
                    g.file = @file.to_sym
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    g.scopes << self
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    pos(g)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    if @elements.empty?
         
     | 
| 
      
 24 
     | 
    
         
            +
                      g.push_nil
         
     | 
| 
      
 25 
     | 
    
         
            +
                    else
         
     | 
| 
      
 26 
     | 
    
         
            +
                      @elements.each_with_index do |e, i|
         
     | 
| 
      
 27 
     | 
    
         
            +
                        g.pop unless i == 0
         
     | 
| 
      
 28 
     | 
    
         
            +
                        e.bytecode(g)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        # We evaluate top level forms as we generate the bytecode for them
         
     | 
| 
      
 30 
     | 
    
         
            +
                        # so macros defined in a file can be used immediately after the
         
     | 
| 
      
 31 
     | 
    
         
            +
                        # definition.
         
     | 
| 
      
 32 
     | 
    
         
            +
                        Apricot::Compiler.eval_node(e, @file) if @evaluate
         
     | 
| 
      
 33 
     | 
    
         
            +
                      end
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    g.ret
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    g.scopes.pop
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    g.local_count = local_count
         
     | 
| 
      
 41 
     | 
    
         
            +
                    g.local_names = local_names
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # A nested scope is looking up a variable. There are no local variables
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # at the top level, so look up the variable on the current namespace.
         
     | 
| 
      
 46 
     | 
    
         
            +
                  def find_var(name, depth = nil)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # Ignore depth, it has no bearing on namespace lookups.
         
     | 
| 
      
 48 
     | 
    
         
            +
                    NamespaceReference.new(name)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def node_equal?(other)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    self.file == other.file && self.elements == other.elements
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def [](*i)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @elements[*i]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  # A (recur) is looking for a recursion target. Since this is the top
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # level, which has no parent, the lookup has failed.
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def find_recur_target
         
     | 
| 
      
 62 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     |