///|
priv struct Parser {
  program : Program
  lexer : Lexer
  pending_edges : Array[Int]
  mut scope : Int
  mut allow_in : Bool
  mut in_generator : Bool
}

///|
fn Parser::new(source : String) -> Parser {
  {
    program: Program::new(source),
    lexer: Lexer::new(source),
    pending_edges: [],
    scope: 0,
    allow_in: true,
    in_generator: false,
  }
}

///|
fn Parser::error_position(self : Parser) -> Position {
  position_at(self.lexer.source, self.lexer.start)
}

///|
/// The current token was not the expected one.
fn Parser::unexpected(self : Parser, expected : String) -> ParseError {
  UnexpectedToken(
    self.error_position(),
    expected,
    self.lexer.token.to_repr().to_string(),
  )
}

///|
/// Any syntax error that is not simply an unexpected token.
fn Parser::syntax_error(self : Parser, message : String) -> ParseError {
  Syntax(self.error_position(), message)
}

///|
fn Parser::advance(self : Parser) -> Unit raise ParseError {
  count_characters(
    self.program.source,
    self.lexer.start,
    self.lexer.end,
    self.program.character_counts,
    1,
  )
  self.lexer.next()
}

///|
fn Parser::expect(self : Parser, expected : Token) -> Unit raise ParseError {
  guard self.lexer.token == expected else {
    raise self.unexpected(expected.to_repr().to_string())
  }
  self.advance()
}

///|
fn Parser::eat(self : Parser, expected : Token) -> Bool raise ParseError {
  guard self.lexer.token == expected else { return false }
  self.advance()
  true
}

///|
fn Parser::peek_token(self : Parser) -> Token raise ParseError {
  let snapshot = self.lexer.snapshot()
  self.lexer.next()
  let token = self.lexer.token
  self.lexer.restore(snapshot)
  token
}

///|
fn Parser::peek_is_keyword(
  self : Parser,
  keyword : Keyword,
) -> Bool raise ParseError {
  let snapshot = self.lexer.snapshot()
  self.lexer.next()
  let matches_keyword = !self.lexer.newline_before &&
    self.lexer.keyword == keyword
  self.lexer.restore(snapshot)
  matches_keyword
}

///|
fn Parser::nearest_var_scope(self : Parser) -> Int {
  // Parsing scopes form a tree rooted at scope_module.
  for scope = self.scope {
    let kind = self.program.scopes[scope].kind
    guard kind != scope_module && kind != scope_function else { break scope }
    continue self.program.scopes[scope].parent
  }
}

///|
fn Parser::add_reference(
  self : Parser,
  scope_id : Int,
  name_start : Int,
  name_end : Int,
) -> Int raise ParseError {
  self.program.add_reference({
    scope: scope_id,
    name_start,
    name_end,
    name: self.program.identifier(name_start, name_end),
    symbol: -1,
  })
}

///|
fn Parser::declare_symbol(
  self : Parser,
  scope_id : Int,
  name_start : Int,
  name_end : Int,
) -> Int raise ParseError {
  let symbol = self.program.add_symbol(name_start, name_end, scope_id)
  let name = self.program.symbols[symbol].name
  // A var initializer writes through a same-name catch binding.
  let mut scope = self.scope
  while scope != scope_id && scope >= 0 {
    if self.program.scopes[scope].kind == scope_catch &&
      self.program.scope_members[scope].get(name) is Some(caught) {
      self.program.link_symbols(symbol, caught)
    }
    scope = self.program.scopes[scope].parent
  }
  self.program.add_reference({
    scope: self.scope,
    name_start,
    name_end,
    name,
    symbol,
  })
}

///|
fn Parser::declare_pattern(
  self : Parser,
  scope_id : Int,
  name_start : Int,
  name_end : Int,
) -> Int raise ParseError {
  self.program.node(
    PatternIdentifier,
    self.declare_symbol(scope_id, name_start, name_end),
    0,
    0,
    0,
  )
}

// ---------------------------------------------------------------------------
// Patterns
// ---------------------------------------------------------------------------

///|
fn Parser::pattern(
  self : Parser,
  scope_id : Int,
  allow_default : Bool,
) -> Int raise ParseError {
  let inner = self.pattern_atom(scope_id)
  guard allow_default && self.lexer.token == Assignment else { return inner }
  self.advance()
  let default_value = self.assignment_with_in()
  self.program.node(PatternDefault, inner, default_value, 0, 0)
}

///|
fn Parser::pattern_atom(self : Parser, scope_id : Int) -> Int raise ParseError {
  match self.lexer.token {
    Identifier => {
      let name_start = self.lexer.start
      let name_end = self.lexer.end
      self.advance()
      self.declare_pattern(scope_id, name_start, name_end)
    }
    LeftBracket => self.array_pattern(scope_id)
    LeftBrace => self.object_pattern(scope_id)
    _ => raise self.unexpected("pattern")
  }
}

///|
fn Parser::array_pattern(self : Parser, scope_id : Int) -> Int raise ParseError {
  self.expect(LeftBracket)
  let items = self.begin_edges()
  while self.lexer.token != RightBracket && self.lexer.token != EndOfFile {
    guard self.lexer.token != Comma else {
      self.advance()
      items.push(-1)
      continue
    }
    if self.lexer.token == Ellipsis {
      self.advance()
      let inner = self.pattern(scope_id, false)
      items.push(self.program.node(PatternRest, inner, 0, 0, 0))
    } else {
      items.push(self.pattern(scope_id, true))
    }
    guard self.eat(Comma) else { break }
  }
  self.expect(RightBracket)
  let { offset, count, } = self.program.edge_list(items)
  self.program.node(PatternArray, offset, count, 0, 0)
}

///|
fn Parser::object_pattern(
  self : Parser,
  scope_id : Int,
) -> Int raise ParseError {
  self.expect(LeftBrace)
  let items = self.begin_edges()
  while self.lexer.token != RightBrace && self.lexer.token != EndOfFile {
    if self.lexer.token == Ellipsis {
      self.advance()
      let inner = self.pattern(scope_id, false)
      items.push(self.program.node(PatternRest, inner, 0, 0, 0))
    } else {
      items.push(self.pattern_property(scope_id))
    }
    guard self.eat(Comma) else { break }
  }
  self.expect(RightBrace)
  let { offset, count, } = self.program.edge_list(items)
  self.program.node(PatternObject, offset, count, 0, 0)
}

///|
fn Parser::pattern_property(
  self : Parser,
  scope_id : Int,
) -> Int raise ParseError {
  let (key, is_computed) = self.property_key_with_computed()
  guard !self.eat(Colon) else {
    let binding = self.pattern(scope_id, true)
    let flags = if is_computed { PROPERTY_COMPUTED } else { 0 }
    return self.program.node(PatternProperty, key, binding, 0, flags)
  }
  let key_start = self.program.nodes[key].a
  let key_end = self.program.nodes[key].b
  let binding = self.declare_pattern(scope_id, key_start, key_end)
  let value = if self.lexer.token == Assignment {
    self.advance()
    let default_value = self.assignment_with_in()
    self.program.node(PatternDefault, binding, default_value, 0, 0)
  } else {
    binding
  }
  self.program.node(PatternProperty, key, value, PROPERTY_SHORTHAND, 0)
}

///|
fn Parser::property_key_with_computed(
  self : Parser,
) -> (Int, Bool) raise ParseError {
  guard self.lexer.token == LeftBracket else {
    return (self.property_key(), false)
  }
  self.advance()
  let expression = self.assignment_with_in()
  self.expect(RightBracket)
  (expression, true)
}

///|
fn Program::parse(source : String) -> Program raise ParseError {
  let parser = Parser::new(source)
  parser.advance()
  let statements = parser.statements(true)
  let { offset, count, } = parser.program.edge_list(statements)
  parser.program.root = parser.program.node(Block, offset, count, 0, 0)
  guard parser.program.cover_defaults == 0 else {
    raise parser.syntax_error(
      "shorthand initializer requires a binding pattern",
    )
  }
  parser.program
}

///|
fn Parser::statements(
  self : Parser,
  is_top_level : Bool,
) -> PendingEdges raise ParseError {
  let output = self.begin_edges()
  while self.lexer.token != EndOfFile && self.lexer.token != RightBrace {
    output.push(self.statement())
  }
  guard !is_top_level || self.lexer.token == EndOfFile else {
    raise self.syntax_error("unexpected top-level }")
  }
  output
}

///|
fn Parser::statement(self : Parser) -> Int raise ParseError {
  match self.lexer.token {
    LeftBrace => self.block()
    Semicolon => {
      self.advance()
      self.program.node(Empty, 0, 0, 0, 0)
    }
    Identifier => self.identifier_statement()
    _ => self.expression_statement()
  }
}

///|
fn Parser::identifier_statement(self : Parser) -> Int raise ParseError {
  match self.lexer.keyword {
    Import if self.is_import_declaration() => self.parse_import()
    Export => self.parse_export()
    Var => {
      self.advance()
      self.declaration(DECLARATION_VAR)
    }
    Let if self.peek_token() != Colon => {
      self.advance()
      self.declaration(DECLARATION_LET)
    }
    Const => {
      self.advance()
      self.declaration(DECLARATION_CONST)
    }
    Async if self.peek_is_keyword(Keyword::Function) => {
      self.advance()
      self.advance()
      self.function(true, is_async=true)
    }
    Function => {
      self.advance()
      self.function(true)
    }
    Class => {
      self.advance()
      self.class(true)
    }
    If => {
      self.advance()
      self.parse_if()
    }
    For => {
      self.advance()
      self.parse_for()
    }
    While => {
      self.advance()
      self.parse_while()
    }
    Do => {
      self.advance()
      self.parse_do_while()
    }
    Return => {
      self.advance()
      self.parse_return()
    }
    Throw => {
      self.advance()
      self.parse_throw()
    }
    Break => {
      self.advance()
      self.jump(true)
    }
    Continue => {
      self.advance()
      self.jump(false)
    }
    Switch => {
      self.advance()
      self.parse_switch()
    }
    Try => {
      self.advance()
      self.parse_try()
    }
    Debugger => {
      self.advance()
      self.eat_semicolon()
      self.program.node(Debugger, 0, 0, 0, 0)
    }
    With => {
      self.advance()
      self.parse_with()
    }
    _ if self.peek_token() == Colon => self.label()
    _ => self.expression_statement()
  }
}

///|
fn Parser::is_import_declaration(self : Parser) -> Bool raise ParseError {
  let next = self.peek_token()
  next != LeftParenthesis && next != Dot
}

///|
fn Parser::expression_statement(self : Parser) -> Int raise ParseError {
  let expression = self.expression()
  self.eat_semicolon()
  self.program.node(ExpressionStatement, expression, 0, 0, 0)
}

///|
fn Parser::eat_semicolon(self : Parser) -> Unit raise ParseError {
  guard self.lexer.token == Semicolon else { return }
  self.advance()
}

///|
fn Parser::block(self : Parser) -> Int raise ParseError {
  self.expect(LeftBrace)
  let outer_scope = self.scope
  self.scope = self.program.push_scope(outer_scope, scope_block)
  let statements = self.statements(false)
  self.scope = outer_scope
  self.expect(RightBrace)
  let { offset, count, } = self.program.edge_list(statements)
  self.program.node(Block, offset, count, 0, 0)
}

///|
fn Parser::declaration(self : Parser, kind : Int) -> Int raise ParseError {
  let declarations = self.declarators(kind)
  self.eat_semicolon()
  let { offset, count, } = self.program.edge_list(declarations)
  self.program.node(VariableDeclaration, kind, offset, count, 0)
}

///|
fn Parser::parse_if(self : Parser) -> Int raise ParseError {
  self.expect(LeftParenthesis)
  let condition = self.expression()
  self.expect(RightParenthesis)
  let consequent = self.statement()
  let alternate = if self.lexer.token == Identifier &&
    self.lexer.keyword == Keyword::Else {
    self.advance()
    self.statement()
  } else {
    -1
  }
  self.program.node(If, condition, consequent, alternate, 0)
}

///|
fn Parser::parse_while(self : Parser) -> Int raise ParseError {
  self.expect(LeftParenthesis)
  let condition = self.expression()
  self.expect(RightParenthesis)
  let body = self.statement()
  self.program.node(While, condition, body, 0, 0)
}

///|
fn Parser::parse_do_while(self : Parser) -> Int raise ParseError {
  let body = self.statement()
  guard self.lexer.token == Identifier && self.lexer.keyword == Keyword::While else {
    raise self.unexpected("while")
  }
  self.advance()
  self.expect(LeftParenthesis)
  let condition = self.expression()
  self.expect(RightParenthesis)
  self.eat_semicolon()
  self.program.node(DoWhile, body, condition, 0, 0)
}

///|
fn Parser::parse_for(self : Parser) -> Int raise ParseError {
  let is_await = self.lexer.keyword == Keyword::Await
  if is_await {
    self.advance()
  }
  self.expect(LeftParenthesis)
  let outer_scope = self.scope
  self.scope = self.program.push_scope(outer_scope, scope_block)
  let (initializer, is_declaration) = self.for_initializer()
  match self.lexer.token {
    Identifier if self.lexer.keyword == Keyword::Of => {
      if !is_declaration {
        self.assignment_pattern(initializer)
      }
      self.for_of_tail(initializer, is_await, outer_scope)
    }
    Identifier if self.lexer.keyword == Keyword::In => {
      guard !is_await else { raise self.syntax_error("for await requires of") }
      if !is_declaration {
        self.assignment_pattern(initializer)
      }
      self.for_in_tail(initializer, outer_scope)
    }
    _ => {
      guard !is_await else { raise self.syntax_error("for await requires of") }
      self.classic_for_tail(initializer, outer_scope)
    }
  }
}

///|
fn Parser::for_initializer(self : Parser) -> (Int, Bool) raise ParseError {
  guard self.lexer.token != Semicolon else { return (-1, false) }
  let is_declaration = self.lexer.token == Identifier &&
    (
      self.lexer.keyword == Keyword::Var ||
      self.lexer.keyword == Keyword::Let ||
      self.lexer.keyword == Keyword::Const
    )
  let initializer = if is_declaration {
    let kind = match self.lexer.keyword {
      Var => DECLARATION_VAR
      Let => DECLARATION_LET
      _ => DECLARATION_CONST
    }
    self.advance()
    self.allow_in = false
    self.declaration_head(kind)
  } else {
    self.allow_in = false
    self.expression()
  }
  self.allow_in = true
  (initializer, is_declaration)
}

///|
fn Parser::for_of_tail(
  self : Parser,
  initializer : Int,
  is_await : Bool,
  outer_scope : Int,
) -> Int raise ParseError {
  self.advance()
  let iterable = self.assignment()
  self.expect(RightParenthesis)
  let body = self.statement()
  self.scope = outer_scope
  self.program.node(
    ForOf,
    initializer,
    iterable,
    body,
    if is_await {
      1
    } else {
      0
    },
  )
}

///|
fn Parser::for_in_tail(
  self : Parser,
  initializer : Int,
  outer_scope : Int,
) -> Int raise ParseError {
  self.advance()
  let object = self.expression()
  self.expect(RightParenthesis)
  let body = self.statement()
  self.scope = outer_scope
  self.program.node(ForIn, initializer, object, body, 0)
}

///|
fn Parser::classic_for_tail(
  self : Parser,
  initializer : Int,
  outer_scope : Int,
) -> Int raise ParseError {
  self.expect(Semicolon)
  let condition = if self.lexer.token == Semicolon {
    -1
  } else {
    self.expression()
  }
  self.expect(Semicolon)
  let update = if self.lexer.token == RightParenthesis {
    -1
  } else {
    self.expression()
  }
  self.expect(RightParenthesis)
  let body = self.statement()
  self.scope = outer_scope
  self.program.node(For, initializer, condition, update, body)
}

///|
fn Parser::declaration_head(self : Parser, kind : Int) -> Int raise ParseError {
  let declarations = self.declarators(kind)
  let { offset, count, } = self.program.edge_list(declarations)
  self.program.node(VariableDeclaration, kind, offset, count, 0)
}

///|
fn Parser::declarators(
  self : Parser,
  kind : Int,
) -> PendingEdges raise ParseError {
  let scope_id = if kind == DECLARATION_VAR {
    self.nearest_var_scope()
  } else {
    self.scope
  }
  let declarations = self.begin_edges()
  while true {
    let pattern = self.pattern(scope_id, false)
    let initializer = if self.lexer.token == Assignment {
      self.advance()
      self.assignment()
    } else {
      -1
    }
    declarations.push(self.program.node(Declarator, pattern, initializer, 0, 0))
    guard self.eat(Comma) else { break }
  }
  declarations
}

///|
fn Parser::parse_return(self : Parser) -> Int raise ParseError {
  let argument = if self.lexer.newline_before ||
    self.lexer.token == Semicolon ||
    self.lexer.token == RightBrace ||
    self.lexer.token == EndOfFile {
    -1
  } else {
    self.expression()
  }
  self.eat_semicolon()
  self.program.node(Return, argument, 0, 0, 0)
}

///|
fn Parser::parse_throw(self : Parser) -> Int raise ParseError {
  guard !self.lexer.newline_before else {
    raise self.syntax_error("newline after throw")
  }
  let argument = self.expression()
  self.eat_semicolon()
  self.program.node(Throw, argument, 0, 0, 0)
}

///|
fn Parser::jump(self : Parser, is_break : Bool) -> Int raise ParseError {
  let (label_start, label_end, label_name) = if !self.lexer.newline_before &&
    self.lexer.token == Identifier {
    let start = self.lexer.start
    let end = self.lexer.end
    let name = self.program.identifier(start, end)
    self.advance()
    (start, end, name)
  } else {
    (-1, -1, -1)
  }
  self.eat_semicolon()
  let kind : NodeKind = if is_break { Break } else { Continue }
  self.program.node(kind, label_start, label_end, label_name, 0)
}

///|
fn Parser::label(self : Parser) -> Int raise ParseError {
  let label_start = self.lexer.start
  let label_end = self.lexer.end
  let label_name = self.program.identifier(label_start, label_end)
  self.advance()
  self.expect(Colon)
  let body = self.statement()
  self.program.node(Labeled, label_start, label_end, body, label_name)
}

///|
fn Parser::parse_with(self : Parser) -> Int raise ParseError {
  self.program.has_dynamic_scope = true
  self.expect(LeftParenthesis)
  let object = self.expression()
  self.expect(RightParenthesis)
  let body = self.statement()
  self.program.node(With, object, body, 0, 0)
}

///|
fn Parser::parse_switch(self : Parser) -> Int raise ParseError {
  self.expect(LeftParenthesis)
  let discriminant = self.expression()
  self.expect(RightParenthesis)
  self.expect(LeftBrace)
  let outer_scope = self.scope
  self.scope = self.program.push_scope(outer_scope, scope_block)
  let cases = self.begin_edges()
  while self.lexer.token != RightBrace && self.lexer.token != EndOfFile {
    cases.push(self.switch_case())
  }
  self.scope = outer_scope
  self.expect(RightBrace)
  let { offset: cases_offset, count: cases_count, } = self.program.edge_list(
    cases,
  )
  self.program.node(Switch, discriminant, cases_offset, cases_count, 0)
}

///|
fn Parser::switch_case(self : Parser) -> Int raise ParseError {
  let condition = match self.lexer.token {
    Identifier if self.lexer.keyword == Keyword::Case => {
      self.advance()
      self.expression()
    }
    Identifier if self.lexer.keyword == Keyword::Default => {
      self.advance()
      -1
    }
    _ => raise self.unexpected("case or default")
  }
  self.expect(Colon)
  let statements = self.begin_edges()
  while !self.at_switch_case_end() {
    statements.push(self.statement())
  }
  let { offset, count, } = self.program.edge_list(statements)
  self.program.node(SwitchCase, condition, offset, count, 0)
}

///|
fn Parser::at_switch_case_end(self : Parser) -> Bool {
  self.lexer.token == RightBrace ||
  self.lexer.token == EndOfFile ||
  (
    self.lexer.token == Identifier &&
    (
      self.lexer.keyword == Keyword::Case ||
      self.lexer.keyword == Keyword::Default
    )
  )
}

///|
fn Parser::parse_try(self : Parser) -> Int raise ParseError {
  let block = self.block()
  let handler = if self.lexer.token == Identifier &&
    self.lexer.keyword == Keyword::Catch {
    self.catch_clause()
  } else {
    -1
  }
  let finalizer = if self.lexer.token == Identifier &&
    self.lexer.keyword == Keyword::Finally {
    self.advance()
    self.block()
  } else {
    -1
  }
  self.program.node(Try, block, handler, finalizer, 0)
}

///|
fn Parser::catch_clause(self : Parser) -> Int raise ParseError {
  self.advance()
  let outer_scope = self.scope
  self.scope = self.program.push_scope(outer_scope, scope_catch)
  let parameter = if self.eat(LeftParenthesis) {
    let pattern = self.pattern(self.scope, false)
    self.expect(RightParenthesis)
    pattern
  } else {
    -1
  }
  let body = self.block()
  self.scope = outer_scope
  self.program.node(Catch, parameter, body, 0, 0)
}

///|
fn Parser::function(
  self : Parser,
  is_declaration : Bool,
  is_async? : Bool = false,
) -> Int raise ParseError {
  let is_generator = self.eat(Asterisk)
  let mut name_reference = -1
  if self.lexer.token == Identifier {
    let name_start = self.lexer.start
    let name_end = self.lexer.end
    self.advance()
    if is_declaration {
      name_reference = self.declare_symbol(self.scope, name_start, name_end)
      if self.program.scopes[self.scope].kind == scope_block {
        // Sloppy-mode block functions can also expose an Annex B var binding.
        self.program.preserved_names[self.program.identifier(
          name_start, name_end,
        )] = true
      }
    } else {
      name_reference = self.add_reference(self.scope, name_start, name_end)
    }
  }
  let function_id = self.function_tail(
    name_reference,
    is_async,
    is_generator,
    !is_declaration,
  )
  self.program.node(
    if is_declaration {
      FunctionDeclaration
    } else {
      FunctionExpression
    },
    function_id,
    0,
    0,
    0,
  )
}

///|
fn Parser::function_tail(
  self : Parser,
  name_reference : Int,
  is_async : Bool,
  is_generator : Bool,
  bind_name_inside : Bool,
) -> Int raise ParseError {
  let outer_scope = self.scope
  let saved_generator = self.in_generator
  self.in_generator = is_generator
  let parent = if bind_name_inside && name_reference >= 0 {
    self.function_name_scope(name_reference, outer_scope)
  } else {
    outer_scope
  }
  let function_scope = self.program.push_scope(parent, scope_parameters)
  self.scope = function_scope
  let parameters = self.function_parameters(function_scope)
  let body = self.function_body()
  self.scope = outer_scope
  self.in_generator = saved_generator
  let { offset, count, } = self.program.edge_list(parameters)
  self.program.functions.push({
    name: name_reference,
    parameters_offset: offset,
    parameters_count: count,
    body,
    flags: function_modifier_flags(is_async, is_generator),
  })
  self.program.functions.length() - 1
}

///|
fn Parser::function_name_scope(
  self : Parser,
  name_reference : Int,
  outer_scope : Int,
) -> Int raise ParseError {
  let name_scope = self.program.push_scope(outer_scope, scope_block)
  let reference = self.program.references[name_reference]
  let symbol = self.program.add_symbol(
    reference.name_start,
    reference.name_end,
    name_scope,
  )
  self.program.references[name_reference] = {
    ..reference,
    symbol,
    scope: name_scope,
  }
  name_scope
}

///|
fn Parser::function_parameters(
  self : Parser,
  function_scope : Int,
) -> PendingEdges raise ParseError {
  self.expect(LeftParenthesis)
  let parameters = self.begin_edges()
  while self.lexer.token != RightParenthesis && self.lexer.token != EndOfFile {
    if self.lexer.token == Ellipsis {
      self.advance()
      let inner = self.pattern(function_scope, false)
      parameters.push(self.program.node(PatternRest, inner, 0, 0, 0))
    } else {
      parameters.push(self.pattern(function_scope, true))
    }
    guard self.eat(Comma) else { break }
  }
  self.expect(RightParenthesis)
  parameters
}

///|
fn Parser::class(self : Parser, is_declaration : Bool) -> Int raise ParseError {
  let name_reference = self.class_name_reference(is_declaration)
  let outer_scope = self.scope
  self.enter_class_scope(name_reference, is_declaration)
  let superclass = self.class_superclass()
  let members = self.class_body_members()
  self.scope = outer_scope
  self.expect(RightBrace)
  self.finish_class(name_reference, superclass, members, is_declaration)
}

///|
fn Parser::class_name_reference(
  self : Parser,
  is_declaration : Bool,
) -> Int raise ParseError {
  let has_name = self.lexer.token == Identifier &&
    self.lexer.keyword != Keyword::Extends
  guard has_name else { return -1 }
  let name_start = self.lexer.start
  let name_end = self.lexer.end
  self.advance()
  guard is_declaration else {
    return self.add_reference(self.scope, name_start, name_end)
  }
  self.declare_symbol(self.scope, name_start, name_end)
}

///|
fn Parser::enter_class_scope(
  self : Parser,
  name_reference : Int,
  is_declaration : Bool,
) -> Unit raise ParseError {
  let class_scope = self.program.push_scope(self.scope, scope_class)
  self.scope = class_scope
  let has_inner_name = !is_declaration && name_reference >= 0
  guard has_inner_name else { return }
  let reference = self.program.references[name_reference]
  let symbol = self.program.add_symbol(
    reference.name_start,
    reference.name_end,
    class_scope,
  )
  self.program.references[name_reference] = {
    ..reference,
    symbol,
    scope: class_scope,
  }
}

///|
fn Parser::class_superclass(self : Parser) -> Int raise ParseError {
  let has_extends = self.lexer.token == Identifier &&
    self.lexer.keyword == Keyword::Extends
  guard has_extends else { return -1 }
  self.advance()
  self.postfix()
}

///|
/// Leave the closing brace unread so the caller restores the outer scope first.
fn Parser::class_body_members(self : Parser) -> PendingEdges raise ParseError {
  self.expect(LeftBrace)
  let members = self.begin_edges()
  while self.lexer.token != RightBrace && self.lexer.token != EndOfFile {
    guard !self.eat(Semicolon) else { continue }
    self.class_member(members)
  }
  members
}

///|
fn Parser::finish_class(
  self : Parser,
  name_reference : Int,
  superclass : Int,
  members : PendingEdges,
  is_declaration : Bool,
) -> Int {
  let { offset, count, } = self.program.edge_list(members)
  self.program.classes.push({
    name: name_reference,
    superclass,
    members_offset: offset,
    members_count: count,
  })
  let class_id = self.program.classes.length() - 1
  let kind = if is_declaration { ClassDeclaration } else { ClassExpression }
  self.program.node(kind, class_id, 0, 0, 0)
}

///|
fn Parser::property_key(self : Parser) -> Int raise ParseError {
  match self.lexer.token {
    Number => self.source_literal(NumberLiteral)
    StringLiteral | Identifier => self.source_literal(StringLiteral)
    LeftBracket => {
      self.advance()
      let expression = self.assignment_with_in()
      self.expect(RightBracket)
      expression
    }
    _ => raise self.unexpected("property key")
  }
}

///|
fn Parser::expression(self : Parser) -> Int raise ParseError {
  let first_expression = self.assignment()
  guard self.lexer.token == Comma else { return first_expression }
  let items = self.begin_edges()
  items.push(first_expression)
  while self.eat(Comma) {
    items.push(self.assignment())
  }
  let { offset, count, } = self.program.edge_list(items)
  self.program.node(Sequence, offset, count, 0, 0)
}

///|
fn Parser::assignment(self : Parser) -> Int raise ParseError {
  guard !(self.in_generator && self.lexer.keyword == Keyword::Yield) else {
    return self.yield_expression()
  }
  let left = self.conditional()
  match self.lexer.token {
    Assignment
    | AdditionAssignment
    | SubtractionAssignment
    | MultiplicationAssignment
    | DivisionAssignment
    | ModuloAssignment
    | ExponentiationAssignment
    | LeftShiftAssignment
    | RightShiftAssignment
    | UnsignedRightShiftAssignment
    | BitwiseAndAssignment
    | BitwiseOrAssignment
    | BitwiseXorAssignment
    | LogicalAndAssignment
    | LogicalOrAssignment
    | NullishCoalescingAssignment => {
      if self.lexer.token == Assignment {
        self.assignment_pattern(left)
      }
      let operator = self.assignment_operator()
      self.advance()
      let right = self.assignment()
      self.program.node(Assignment, operator, left, right, 0)
    }
    _ => left
  }
}

///|
fn Parser::yield_expression(self : Parser) -> Int raise ParseError {
  self.advance()
  let delegated = !self.lexer.newline_before && self.eat(Asterisk)
  let operation = if delegated { Operator::YieldStar } else { Operator::Yield }
  let operator = self.program.add_operator(operation)
  let has_no_argument = (!delegated && self.lexer.newline_before) ||
    self.lexer.token == Semicolon ||
    self.lexer.token == RightBrace ||
    self.lexer.token == EndOfFile ||
    self.lexer.token == RightParenthesis ||
    self.lexer.token == RightBracket ||
    self.lexer.token == Comma
  let argument = if has_no_argument {
    guard !delegated else {
      raise self.syntax_error("yield* requires an expression")
    }
    -1
  } else {
    self.assignment()
  }
  self.program.node(Unary, operator, argument, 0, 0)
}

///|
fn Parser::assignment_operator(self : Parser) -> Int {
  let operation = match self.lexer.token {
    Assignment => Operator::Assignment
    AdditionAssignment => Operator::Add
    SubtractionAssignment => Operator::Subtract
    MultiplicationAssignment => Operator::Multiply
    DivisionAssignment => Operator::Divide
    ModuloAssignment => Operator::Modulo
    ExponentiationAssignment => Operator::Exponent
    LeftShiftAssignment => Operator::ShiftLeft
    RightShiftAssignment => Operator::ShiftRight
    UnsignedRightShiftAssignment => Operator::UnsignedShiftRight
    BitwiseAndAssignment => Operator::BitwiseAnd
    BitwiseOrAssignment => Operator::BitwiseOr
    BitwiseXorAssignment => Operator::BitwiseXor
    LogicalAndAssignment => Operator::LogicalAnd
    LogicalOrAssignment => Operator::LogicalOr
    NullishCoalescingAssignment => Operator::NullishCoalescing
    _ => Operator::Add
  }
  self.program.add_operator(operation)
}

///|
fn Parser::conditional(self : Parser) -> Int raise ParseError {
  let condition = self.binary(1)
  guard self.lexer.token == Question else { return condition }
  self.advance()
  let consequent = self.assignment_with_in()
  self.expect(Colon)
  let alternate = self.assignment()
  self.program.node(Conditional, condition, consequent, alternate, 0)
}

///|
fn Parser::binary_operator(self : Parser) -> (Int, Operator) {
  match self.lexer.token {
    ExponentiationOperator => (11, Operator::Exponent)
    Asterisk => (10, Operator::Multiply)
    Slash => (10, Operator::Divide)
    Percent => (10, Operator::Modulo)
    Plus => (9, Operator::Add)
    Minus => (9, Operator::Subtract)
    ShiftLeft => (8, Operator::ShiftLeft)
    ShiftRight => (8, Operator::ShiftRight)
    UnsignedShiftRight => (8, Operator::UnsignedShiftRight)
    LessThan => (7, Operator::LessThan)
    GreaterThan => (7, Operator::GreaterThan)
    LessThanOrEqual => (7, Operator::LessThanOrEqual)
    GreaterThanOrEqual => (7, Operator::GreaterThanOrEqual)
    Equal => (6, Operator::Equal)
    NotEqual => (6, Operator::NotEqual)
    StrictlyEqual => (6, Operator::StrictlyEqual)
    StrictlyNotEqual => (6, Operator::StrictlyNotEqual)
    BitwiseAnd => (5, Operator::BitwiseAnd)
    BitwiseXor => (4, Operator::BitwiseXor)
    BitwiseOr => (3, Operator::BitwiseOr)
    LogicalAnd => (2, Operator::LogicalAnd)
    NullishCoalescing => (1, Operator::NullishCoalescing)
    LogicalOr => (1, Operator::LogicalOr)
    Identifier if self.allow_in && self.lexer.keyword == Keyword::In =>
      (7, Operator::In)
    Identifier if self.lexer.keyword == Keyword::InstanceOf =>
      (7, Operator::InstanceOf)
    _ => (-1, Operator::Add)
  }
}

///|
fn Parser::binary(self : Parser, min_precedence : Int) -> Int raise ParseError {
  for left = self.unary() {
    let (precedence, operation) = self.binary_operator()
    guard precedence >= min_precedence else { break left }
    let operator = self.program.add_operator(operation)
    self.advance()
    let next_precedence = if operation == Operator::Exponent {
      precedence
    } else {
      precedence + 1
    }
    let right = self.binary(next_precedence)
    let logical = operation == Operator::LogicalAnd ||
      operation == Operator::LogicalOr ||
      operation == Operator::NullishCoalescing
    continue self.program.node(
        if logical {
          Logical
        } else {
          Binary
        },
        operator,
        left,
        right,
        0,
      )
  }
}

///|
fn Parser::unary(self : Parser) -> Int raise ParseError {
  match self.lexer.token {
    LogicalNot => self.prefix_unary(Operator::LogicalNot)
    Tilde => self.prefix_unary(Operator::BitwiseNot)
    Plus => self.prefix_unary(Operator::Plus)
    Minus => self.prefix_unary(Operator::Minus)
    Increment => self.prefix_update(Operator::Increment)
    Decrement => self.prefix_update(Operator::Decrement)
    Identifier if self.lexer.keyword == Keyword::TypeOf =>
      self.prefix_unary(Operator::TypeOf)
    Identifier if self.lexer.keyword == Keyword::Void =>
      self.prefix_unary(Operator::Void)
    Identifier if self.lexer.keyword == Keyword::Delete =>
      self.prefix_unary(Operator::Delete)
    Identifier if self.lexer.keyword == Keyword::Await =>
      self.prefix_unary(Operator::Await)
    _ => self.postfix()
  }
}

///|
fn Parser::prefix_unary(
  self : Parser,
  operation : Operator,
) -> Int raise ParseError {
  let operator = self.program.add_operator(operation)
  self.advance()
  let operand = self.unary()
  self.program.node(Unary, operator, operand, 0, 0)
}

///|
fn Parser::prefix_update(
  self : Parser,
  operation : Operator,
) -> Int raise ParseError {
  let operator = self.program.add_operator(operation)
  self.advance()
  let operand = self.unary()
  self.program.node(Update, operator, UPDATE_PREFIX, operand, 0)
}

///|
fn Parser::postfix(self : Parser) -> Int raise ParseError {
  let primary = self.primary()
  let expression = self.call_member_tail(primary)
  let has_postfix_update = !self.lexer.newline_before &&
    (self.lexer.token == Increment || self.lexer.token == Decrement)
  guard has_postfix_update else { return expression }
  let operation = if self.lexer.token == Increment {
    Operator::Increment
  } else {
    Operator::Decrement
  }
  let operator = self.program.add_operator(operation)
  self.advance()
  self.program.node(Update, operator, UPDATE_POSTFIX, expression, 0)
}

///|
fn Parser::call_member_tail(self : Parser, start : Int) -> Int raise ParseError {
  for expression = start {
    match self.lexer.token {
      Dot => {
        self.advance()
        continue self.named_member(expression, false)
      }
      OptionalChaining => {
        self.advance()
        continue self.optional_chain_tail(expression)
      }
      LeftBracket => continue self.index_member(expression, false)
      LeftParenthesis => {
        self.mark_direct_eval(expression)
        continue self.call_tail(expression, false)
      }
      TemplateNoSubstitution | TemplateHead => {
        let template = self.template_literal()
        continue self.program.node(TaggedTemplate, expression, template, 0, 0)
      }
      _ => break expression
    }
  }
}

///|
fn Parser::named_member(
  self : Parser,
  object : Int,
  is_optional : Bool,
) -> Int raise ParseError {
  let name_start = self.lexer.start
  let name_end = self.lexer.end
  self.advance()
  let flags = if is_optional { CHAIN_OPTIONAL } else { 0 }
  self.program.node(Member, object, name_start, name_end, flags)
}

///|
fn Parser::index_member(
  self : Parser,
  object : Int,
  is_optional : Bool,
) -> Int raise ParseError {
  self.advance()
  let index = self.expression_with_in()
  self.expect(RightBracket)
  let flags = if is_optional { CHAIN_OPTIONAL } else { 0 }
  self.program.node(Index, object, index, 0, flags)
}

///|
fn Parser::call_tail(
  self : Parser,
  callee : Int,
  is_optional : Bool,
) -> Int raise ParseError {
  let { offset, count, } = self.arguments()
  let flags = if is_optional { CHAIN_OPTIONAL } else { 0 }
  self.program.node(Call, callee, offset, count, flags)
}

///|
fn Parser::optional_chain_tail(
  self : Parser,
  object : Int,
) -> Int raise ParseError {
  match self.lexer.token {
    LeftParenthesis => self.call_tail(object, true)
    LeftBracket => self.index_member(object, true)
    _ => self.named_member(object, true)
  }
}

///|
fn Parser::mark_direct_eval(self : Parser, expression : Int) -> Unit {
  let callee = for current = expression {
    let node = self.program.nodes[current]
    guard node.kind == Parenthesized else { break node }
    continue node.a
  }
  guard callee.kind == Identifier &&
    self.program.references[callee.a].name == name_eval else {
    return
  }
  self.program.has_dynamic_scope = true
}

///|
fn Parser::arguments(self : Parser) -> EdgeRange raise ParseError {
  self.expect(LeftParenthesis)
  let arguments = self.begin_edges()
  while self.lexer.token != RightParenthesis && self.lexer.token != EndOfFile {
    if self.lexer.token == Ellipsis {
      self.advance()
      let inner = self.assignment_with_in()
      arguments.push(self.program.node(Spread, inner, 0, 0, 0))
    } else {
      arguments.push(self.assignment_with_in())
    }
    guard self.eat(Comma) else { break }
  }
  self.expect(RightParenthesis)
  self.program.edge_list(arguments)
}

///|
fn Parser::primary(self : Parser) -> Int raise ParseError {
  match self.lexer.token {
    Number => self.source_literal(NumberLiteral)
    StringLiteral => self.source_literal(StringLiteral)
    Slash | DivisionAssignment | Regex => {
      if self.lexer.token != Regex {
        self.lexer.position = self.lexer.start
        self.lexer.regex()
      }
      self.source_literal(RegexLiteral)
    }
    TemplateNoSubstitution | TemplateHead => self.template_literal()
    LeftParenthesis => self.parentheses()
    LeftBracket => self.array_literal()
    LeftBrace => self.object_literal()
    Identifier => self.identifier()
    _ => raise self.unexpected("expression")
  }
}

///|
fn Parser::source_literal(
  self : Parser,
  kind : NodeKind,
) -> Int raise ParseError {
  let literal = self.program.node(kind, self.lexer.start, self.lexer.end, 0, 0)
  self.advance()
  literal
}

///|
fn Parser::identifier(self : Parser) -> Int raise ParseError {
  match self.lexer.keyword {
    True => {
      self.advance()
      self.program.node(BooleanLiteral, 1, 0, 0, 0)
    }
    False => {
      self.advance()
      self.program.node(BooleanLiteral, 0, 0, 0, 0)
    }
    Null => {
      self.advance()
      self.program.node(NullLiteral, 0, 0, 0, 0)
    }
    This => {
      self.advance()
      self.program.node(This, 0, 0, 0, 0)
    }
    Super => {
      self.advance()
      self.program.node(Super, 0, 0, 0, 0)
    }
    Function => {
      self.advance()
      self.function(false)
    }
    Class => {
      self.advance()
      self.class(false)
    }
    New => {
      self.advance()
      self.parse_new()
    }
    Async => {
      guard self.async_expression() is Some(expression) else {
        return self.identifier_reference()
      }
      expression
    }
    _ => self.identifier_reference()
  }
}

///|
fn Parser::async_expression(self : Parser) -> Int? raise ParseError {
  let next_token = self.peek_token()
  guard next_token == Identifier || next_token == LeftParenthesis else {
    return None
  }
  let snapshot = self.lexer.snapshot()
  self.advance()
  let same_line = !self.lexer.newline_before
  match self.lexer.token {
    Identifier if same_line && self.lexer.keyword == Keyword::Function => {
      self.advance()
      Some(self.function(false, is_async=true))
    }
    Identifier if same_line && self.peek_token() == Arrow => {
      let start = self.lexer.start
      let end = self.lexer.end
      self.advance()
      Some(self.single_arrow(start, end, true))
    }
    LeftParenthesis if same_line =>
      Some(self.async_parentheses(snapshot.start, snapshot.end))
    _ => {
      self.lexer.restore(snapshot)
      None
    }
  }
}

///|
fn Parser::identifier_reference(self : Parser) -> Int raise ParseError {
  let start = self.lexer.start
  let end = self.lexer.end
  self.advance()
  guard self.lexer.token != Arrow else {
    return self.single_arrow(start, end, false)
  }
  self.program.node(
    Identifier,
    self.add_reference(self.scope, start, end),
    0,
    0,
    0,
  )
}

///|
fn Parser::parse_new(self : Parser) -> Int raise ParseError {
  guard self.lexer.token != Dot else {
    self.advance()
    let name_start = self.lexer.start
    let name_end = self.lexer.end
    self.advance()
    return self.program.node(
      Member,
      -1,
      name_start,
      name_end,
      MEMBER_NEW_TARGET,
    )
  }
  let callee = self.member_chain()
  let { offset, count, } = if self.lexer.token == LeftParenthesis {
    self.arguments()
  } else {
    { offset: -1, count: 0, }
  }
  self.program.node(New, callee, offset, count, 0)
}

///|
fn Parser::member_chain(self : Parser) -> Int raise ParseError {
  for expression = self.primary() {
    match self.lexer.token {
      Dot => {
        self.advance()
        continue self.named_member(expression, false)
      }
      LeftBracket => continue self.index_member(expression, false)
      _ => break expression
    }
  }
}

///|
fn Parser::single_arrow(
  self : Parser,
  name_start : Int,
  name_end : Int,
  is_async : Bool,
) -> Int raise ParseError {
  let outer_scope = self.scope
  let saved_generator = self.in_generator
  self.in_generator = false
  let function_scope = self.program.push_scope(
    outer_scope, scope_arrow_parameters,
  )
  self.scope = function_scope
  guard !self.lexer.newline_before else {
    raise self.syntax_error("newline before arrow")
  }
  let parameter = self.declare_pattern(function_scope, name_start, name_end)
  let name_reference = -1
  self.expect(Arrow)
  let (body, body_flag) = self.arrow_body()
  self.scope = outer_scope
  self.in_generator = saved_generator
  let parameters = self.begin_edges()
  parameters.push(parameter)
  let { offset, count, } = self.program.edge_list(parameters)
  self.program.functions.push({
    name: name_reference,
    parameters_offset: offset,
    parameters_count: count,
    body,
    flags: FUNCTION_ARROW | function_modifier_flags(is_async, false) | body_flag,
  })
  let function_id = self.program.functions.length() - 1
  self.program.node(ArrowExpression, function_id, 0, 0, 0)
}

///|
fn Parser::arrow_body(self : Parser) -> (Int, Int) raise ParseError {
  guard self.lexer.token == LeftBrace else {
    return (self.assignment(), FUNCTION_EXPRESSION_BODY)
  }
  (self.function_body(), 0)
}

///|
fn Parser::parentheses(self : Parser) -> Int raise ParseError {
  let expression = self.parenthesized()
  guard self.lexer.token == Arrow else {
    return self.finish_parenthesized(expression)
  }
  self.finish_arrow(expression, false)
}

///|
fn Parser::template_literal(self : Parser) -> Int raise ParseError {
  guard self.lexer.token == TemplateNoSubstitution else {
    return self.template()
  }
  self.source_literal(TemplateNoSubstitution)
}

///|
fn Parser::template(self : Parser) -> Int raise ParseError {
  let parts = self.begin_edges()
  let mut quasi_count = 0
  while true {
    quasi_count = quasi_count + 1
    parts.push(self.lexer.quasi_start)
    parts.push(self.lexer.quasi_end)
    self.advance()
    let expression = self.expression_with_in()
    parts.push(expression)
    guard self.lexer.token == RightBrace else {
      raise self.syntax_error("template substitution must end with }")
    }
    self.lexer.resume_template()
    guard self.lexer.token != TemplateTail else {
      quasi_count = quasi_count + 1
      parts.push(self.lexer.quasi_start)
      parts.push(self.lexer.quasi_end)
      self.advance()
      break
    }
  }
  let { offset, .. } = self.program.edge_list(parts)
  self.program.node(Template, offset, quasi_count, 0, 0)
}

///|
fn Parser::array_literal(self : Parser) -> Int raise ParseError {
  self.expect(LeftBracket)
  let items = self.begin_edges()
  let mut trailing_comma = false
  while self.lexer.token != RightBracket && self.lexer.token != EndOfFile {
    guard self.lexer.token != Comma else {
      self.advance()
      items.push(-1)
      continue
    }
    if self.lexer.token == Ellipsis {
      self.advance()
      let inner = self.assignment_with_in()
      items.push(self.program.node(Spread, inner, 0, 0, 0))
    } else {
      items.push(self.assignment_with_in())
    }
    guard self.eat(Comma) else { break }
    trailing_comma = self.lexer.token == RightBracket
  }
  self.expect(RightBracket)
  let { offset, count, } = self.program.edge_list(items)
  self.program.node(
    ArrayLiteral,
    offset,
    count,
    0,
    if trailing_comma {
      1
    } else {
      0
    },
  )
}

///|
fn Parser::object_literal(self : Parser) -> Int raise ParseError {
  self.expect(LeftBrace)
  let properties = self.begin_edges()
  let mut trailing_comma = false
  while self.lexer.token != RightBrace && self.lexer.token != EndOfFile {
    properties.push(self.object_property())
    guard self.eat(Comma) else { break }
    trailing_comma = self.lexer.token == RightBrace
  }
  self.expect(RightBrace)
  let { offset, count, } = self.program.edge_list(properties)
  self.program.node(
    ObjectLiteral,
    offset,
    count,
    0,
    if trailing_comma {
      1
    } else {
      0
    },
  )
}

///|
fn Parser::object_property(self : Parser) -> Int raise ParseError {
  guard self.lexer.token != Ellipsis else {
    self.advance()
    let inner = self.assignment_with_in()
    return self.program.node(PatternRest, inner, 0, 0, 0)
  }
  let kind = self.object_method_kind()
  let is_async = self.lexer.token == Identifier &&
    self.lexer.keyword == Keyword::Async &&
    self.property_modifier_follows()
  if is_async {
    self.advance()
  }
  let is_generator = self.eat(Asterisk)
  let is_identifier_key = self.lexer.token == Identifier
  let (key, is_computed) = self.property_key_with_computed()
  let flags = if is_computed { PROPERTY_COMPUTED } else { 0 }
  guard self.lexer.token != LeftParenthesis else {
    let function_id = self.function_tail(-1, is_async, is_generator, false)
    return self.program.node(Method, key, function_id, kind, flags)
  }
  guard !self.eat(Colon) else {
    let value = self.assignment_with_in()
    return self.program.node(Property, key, value, 0, flags)
  }
  let is_shorthand = is_identifier_key && !is_computed
  guard !is_shorthand else { return self.shorthand_property(key) }
  let value = self.assignment_with_in()
  self.program.node(Property, key, value, 0, flags)
}

///|
fn Parser::property_modifier_follows(self : Parser) -> Bool raise ParseError {
  match self.peek_token() {
    Colon | Assignment | Comma | RightBrace | LeftParenthesis => false
    _ => true
  }
}

///|
fn Parser::object_method_kind(self : Parser) -> Int raise ParseError {
  let is_accessor = self.lexer.token == Identifier &&
    (self.lexer.keyword == Keyword::Get || self.lexer.keyword == Keyword::Set) &&
    self.property_modifier_follows()
  guard is_accessor else { return METHOD_ORDINARY }
  let kind = if self.lexer.keyword == Keyword::Get {
    METHOD_GETTER
  } else {
    METHOD_SETTER
  }
  self.advance()
  kind
}

///|
fn Parser::shorthand_property(self : Parser, key : Int) -> Int raise ParseError {
  let reference = self.add_reference(
    self.scope,
    self.program.nodes[key].a,
    self.program.nodes[key].b,
  )
  let binding = self.program.node(Identifier, reference, 0, 0, 0)
  let value = if self.eat(Assignment) {
    let default_value = self.assignment_with_in()
    self.program.cover_defaults += 1
    self.program.node(CoverDefault, binding, default_value, 0, 0)
  } else {
    binding
  }
  self.program.node(Property, key, value, PROPERTY_SHORTHAND, 0)
}

///|
/// Parameter initializers cannot see declarations from the function body.
/// Body var declarations matching a parameter keep its spelling, preserving
/// JavaScript's initialization from the parameter environment.
fn Parser::function_body(self : Parser) -> Int raise ParseError {
  let parameters = self.scope
  let saved_allow_in = self.allow_in
  self.allow_in = true
  let body_scope = self.program.push_scope(parameters, scope_function)
  for name, symbol in self.program.scope_members[parameters] {
    self.program.scope_members[body_scope][name] = symbol
  }
  self.scope = body_scope
  self.expect(LeftBrace)
  let statements = self.statements(false)
  self.expect(RightBrace)
  self.scope = parameters
  self.allow_in = saved_allow_in
  let { offset, count, } = self.program.edge_list(statements)
  self.program.node(Block, offset, count, 0, 0)
}

///|
fn Parser::assignment_with_in(self : Parser) -> Int raise ParseError {
  let saved_allow_in = self.allow_in
  self.allow_in = true
  let expression = self.assignment()
  self.allow_in = saved_allow_in
  expression
}

///|
fn Parser::expression_with_in(self : Parser) -> Int raise ParseError {
  let saved_allow_in = self.allow_in
  self.allow_in = true
  let expression = self.expression()
  self.allow_in = saved_allow_in
  expression
}

///|
fn function_modifier_flags(is_async : Bool, is_generator : Bool) -> Int {
  let async_flag = if is_async { FUNCTION_ASYNC } else { 0 }
  let generator_flag = if is_generator { FUNCTION_GENERATOR } else { 0 }
  async_flag | generator_flag
}