class Atomy::Code::Block

Public Class Methods

new(body, args = [], proc_argument = nil, lambda_style = true) click to toggle source
# File lib/atomy/code/block.rb, line 4
def initialize(body, args = [], proc_argument = nil, lambda_style = true)
  @body = body

  @arguments = args.dup
  if args.last.is_a?(Atomy::Grammar::AST::Prefix) && args.last.operator == :*
    @splat_argument = @arguments.pop
  end

  @proc_argument = proc_argument

  @lambda_style = lambda_style
end

Public Instance Methods

bytecode(gen, mod) click to toggle source
# File lib/atomy/code/block.rb, line 17
def bytecode(gen, mod)
  blk = build_block(gen.state.scope, mod)

  if @lambda_style
    gen.push_cpath_top
    gen.find_const :Proc
  end

  gen.create_block(blk)

  if @lambda_style
    gen.send(:__from_block__, 1)
    gen.dup
    gen.send(:lambda_style!, 0)
    gen.pop
  end
end

Private Instance Methods

build_block(scope, mod) click to toggle source
# File lib/atomy/code/block.rb, line 37
def build_block(scope, mod)
  Atomy::Compiler.generate(mod.file) do |blk|
    # close over the outer scope
    blk.state.scope.parent = scope

    # for now, only allow a fixed set of arguments
    blk.required_args = blk.total_args = @arguments.size

    if @lambda_style
      # discard extra arguments
      blk.splat_index = @arguments.size
    end

    # this bubbles up to Proc#arity and BlockEnvironment, though it
    # doesn't appear to change actual behavior of the block
    if @splat_argument
      # this is + 1 so that if there are no args the arity is -1, not -0
      # (which is not a thing)
      blk.arity = -(@arguments.size + 1)
    else
      blk.arity = @arguments.size
    end

    # create a local for each argument name
    @arguments.each.with_index do |a, i|
      blk.state.scope.new_local(:"arg:#{i}")
    end

    # local for discarded splat args
    blk.state.scope.new_local(:"arg:extra")

    # pattern-match all args
    @arguments.each.with_index do |a, i|
      Assign.new(a, Variable.new(:"arg:#{i}")).bytecode(blk, mod)
    end

    # pattern-match the splat arg
    if @splat_argument
      Assign.new(@splat_argument, Variable.new(:"arg:extra")).bytecode(blk, mod)
    end

    # pattern-match the proc arg
    if @proc_argument
      Assign.new(@proc_argument, PushProc.new).bytecode(blk, mod)
    end

    # build the block's body
    mod.compile(blk, @body)
  end
end