CodeTools::AST << {

Block < CodeTools::Compiler::LocalVariables, Node {
  node_type block
  field parameters, field body

  var parent # TODO: investigate removing
  var new_cscope

  CompilerClass: CodeTools::Compiler # TODO: remove this

  setter body: |given| given || NullLiteral.new(line:self.line)

  locals: self.body.?locals.?body.map(&:value) ?? null

  block_local?: |name|
    locals.?include?(name) ?? false

  module?: false

  nest_scope: |scope| scope.parent = self

  # Look up a local variable in this block's scope.
  search_local: |name| {
    (variable = self.variables[name]) &? (
      variable.nested_reference
    ) ?? (self.block_local?(name) &? (
      self.new_local(name)
    ) ?? ((reference = self.parent.search_local(name)) &? (
      reference.depth = reference.depth + 1
      reference
    ) ?? (
      null
    )))
  }

  # Assign a slot number to a local variable access node.
  # TODO: remove this method in favor of using search_local in all cases instead.
  assign_local_reference: |var| {
    var.variable = (variable = self.variables[var.name]) &? (
      variable.reference
    ) ?? (self.block_local?(var.name) &? (
      variable = self.new_local(var.name)
      variable.reference
    ) ?? ((reference = self.parent.search_local(var.name)) &? (
      reference.depth = reference.depth + 1
      reference
    ) ?? (
      variable = self.new_local(var.name)
      variable.reference
    )))
  }

  new_local: |name| {
    self.variables[name] = \
      self.variables[name] || CompilerClass::LocalVariable.new(allocate_slot)
  }

  new_nested_local: |name| new_local(name).nested_reference

  effective_parameters: self.parameters || ParameterAssembly.new(
    line: self.line
    rest: RestParameter.new(line: self.line)
  )

  bytecode: |g| {
    pos(g)

    state = g.state
    state.scope.nest_scope(self)

    parameters = self.effective_parameters
    blk = new_block_generator(g, parameters)

    blk.push_state(self)
    blk.definition_line(self.line)
    blk.state.push_super(state.super)
    blk.state.push_eval(state.eval)

    blk.state.push_name(blk.name)

    # Push line info down.
    pos(blk)

    parameters.bytecode(blk)

    blk.state.push_block
    blk.push_modifiers
    blk.break = null
    blk.next = null
    blk.redo = blk.new_label
    blk.redo.set!

    self.body.bytecode(blk)

    blk.pop_modifiers
    blk.state.pop_block
    blk.ret
    blk.close
    blk.pop_state

    blk.local_count = local_count
    blk.local_names = local_names

    self.new_cscope &? (
      g.push_scope
      g.send(:for_method_definition, 0)
      g.add_scope
    )
    g.create_block(blk)
  }
}

}