///|
fn Cursor::placeable(self : Cursor) -> Node raise ParseFailure {
  if self.depth >= self.max_depth {
    raise ParseFailure("E9001", [])
  }
  self.depth += 1
  errdefer {
    self.depth -= 1
  }
  let result = {
    let start = self.index
    self.expect('{')
    self.skip_blank()
    let expr = self.expression()
    self.expect('}')
    Node::with_span(Placeable(expr), start, self.index)
  }
  self.depth -= 1
  result
}

///|
fn Cursor::inline_expression(self : Cursor) -> Node raise ParseFailure {
  let start = self.index
  if self.current() == Some('{') {
    return self.placeable()
  }
  if self.is_number_start() {
    return self.number()
  }
  if self.current() == Some('"') {
    return self.string_literal()
  }
  if self.current() == Some('$') {
    ignore(self.next())
    let id = self.identifier()
    return Node::with_span(VariableReference(id), start, self.index)
  }
  if self.current() == Some('-') {
    ignore(self.next())
    let id = self.identifier()
    let attribute = if self.current() == Some('.') {
      ignore(self.next())
      Some(self.identifier())
    } else {
      None
    }
    self.peek_blank()
    let arguments = if self.current_peek() == Some('(') {
      self.skip_to_peek()
      Some(self.call_arguments())
    } else {
      None
    }
    return Node::with_span(
      TermReference(id, attribute, arguments),
      start,
      self.index,
    )
  }
  if is_id_start(self.current_peek()) {
    let id = self.identifier()
    self.peek_blank()
    if self.current_peek() == Some('(') {
      if !uppercase_function(identifier_name(id)) {
        raise ParseFailure("E0008", [])
      }
      self.skip_to_peek()
      let arguments = self.call_arguments()
      return Node::with_span(
        FunctionReference(id, arguments),
        start,
        self.index,
      )
    }
    let attribute = if self.current() == Some('.') {
      ignore(self.next())
      Some(self.identifier())
    } else {
      None
    }
    return Node::with_span(MessageReference(id, attribute), start, self.index)
  }
  raise ParseFailure("E0028", [])
}

///|
fn Cursor::call_argument(self : Cursor) -> Node raise ParseFailure {
  let start = self.index
  let expr = self.inline_expression()
  self.skip_blank()
  if self.current() != Some(':') {
    return expr
  }
  match expr.kind {
    MessageReference(id, None) => {
      ignore(self.next())
      self.skip_blank()
      let value = self.literal()
      Node::with_span(NamedArgument(id, value), start, self.index)
    }
    _ => raise ParseFailure("E0009", [])
  }
}

///|
fn Cursor::call_arguments(self : Cursor) -> Node raise ParseFailure {
  if self.depth >= self.max_depth {
    raise ParseFailure("E9001", [])
  }
  self.depth += 1
  errdefer {
    self.depth -= 1
  }
  let start = self.index
  let positional : Array[Node] = []
  let named : Array[Node] = []
  let names : Map[String, Bool] = Map([])
  self.expect('(')
  self.skip_blank()
  while self.current() != Some(')') {
    let argument = self.call_argument()
    match argument.kind {
      NamedArgument(id, _) => {
        let name = identifier_name(id)
        if names.contains(name) {
          raise ParseFailure("E0022", [])
        }
        names[name] = true
        named.push(argument)
      }
      _ => {
        if !names.is_empty() {
          raise ParseFailure("E0021", [])
        }
        positional.push(argument)
      }
    }
    self.skip_blank()
    if self.current() == Some(',') {
      ignore(self.next())
      self.skip_blank()
      continue
    }
    break
  }
  self.expect(')')
  self.depth -= 1
  Node::with_span(CallArguments(positional, named), start, self.index)
}

///|
fn Cursor::expression(self : Cursor) -> Node raise ParseFailure {
  let start = self.index
  let selector = self.inline_expression()
  self.skip_blank()
  if self.current() == Some('-') {
    if self.peek() != Some('>') {
      self.reset_peek()
      return selector
    }
    match selector.kind {
      MessageReference(_, None) => raise ParseFailure("E0016", [])
      MessageReference(_, Some(_)) => raise ParseFailure("E0018", [])
      TermReference(_, None, _) => raise ParseFailure("E0017", [])
      TermReference(_, Some(_), _)
      | StringLiteral(_)
      | NumberLiteral(_)
      | VariableReference(_)
      | FunctionReference(_, _) => ()
      _ => raise ParseFailure("E0029", [])
    }
    ignore(self.next())
    ignore(self.next())
    ignore(self.skip_blank_inline())
    self.expect_line_end()
    let variants = self.variants()
    return Node::with_span(
      SelectExpression(selector, variants),
      start,
      self.index,
    )
  }
  match selector.kind {
    TermReference(_, Some(_), _) => raise ParseFailure("E0019", [])
    _ => selector
  }
}

///|
fn Cursor::variant(
  self : Cursor,
  has_default : Bool,
) -> Node raise ParseFailure {
  let start = self.index
  let default = self.current() == Some('*')
  if default {
    if has_default {
      raise ParseFailure("E0015", [])
    }
    ignore(self.next())
  }
  self.expect('[')
  self.skip_blank()
  if self.current() == None {
    raise ParseFailure("E0013", [])
  }
  let key = if self.current() == Some('-') || is_digit(self.current()) {
    self.number()
  } else {
    self.identifier()
  }
  self.skip_blank()
  self.expect(']')
  let value = match self.maybe_pattern() {
    Some(n) => n
    None => raise ParseFailure("E0012", [])
  }
  Node::with_span(Variant(key, value, default), start, self.index)
}

///|
fn Cursor::variants(self : Cursor) -> Array[Node] raise ParseFailure {
  let variants : Array[Node] = []
  let mut has_default = false
  self.skip_blank()
  while self.is_variant_start() {
    let v = self.variant(has_default)
    match v.kind {
      Variant(_, _, d) => has_default = has_default || d
      _ => ()
    }
    variants.push(v)
    self.skip_blank()
  }
  if variants.is_empty() {
    raise ParseFailure("E0011", [])
  }
  if !has_default {
    raise ParseFailure("E0010", [])
  }
  variants
}