///|
/// `if ` after a clause's patterns; `None` without `if`. An empty
/// guard (`if ->`) is the deprecated empty guard.
fn Parser::parse_case_clause_guard(
  self : Parser,
) -> @ast.ClauseGuard? raise ParseError {
  guard self.maybe_one(Keyword(If)) is Some((start, end)) else { return None }
  let result = self.parse_clause_guard_inner()
  if result is None {
    self.warn(DeprecatedEmptyClauseGuard(location=sp(start, end)))
  }
  result
}

///|
fn Parser::parse_clause_guard_inner(
  self : Parser,
) -> @ast.ClauseGuard? raise ParseError {
  let opstack : Array[OpEntry] = []
  let estack : Array[@ast.ClauseGuard] = []
  let mut last_op_start = 0
  let mut last_op_end = 0
  while true {
    match self.parse_case_clause_guard_unit() {
      Some(unit) => estack.push(unit)
      None =>
        if estack.length() == 0 {
          return None
        } else {
          parse_error(OpNakedRight, sp(last_op_start, last_op_end))
        }
    }
    guard self.token0 is Some(t) else { break }
    guard guard_precedence(t.kind) is Some(prec) else { break }
    self.advance()
    last_op_start = t.span.start
    last_op_end = t.span.end
    ignore(
      handle_operator(
        Some({ token: t, precedence: prec, }),
        opstack,
        estack,
        reduce_clause_guard,
      ),
    )
  }
  handle_operator(None, opstack, estack, reduce_clause_guard)
}

///|
/// After a name in a guard: a `(` means a function call, which guards do
/// not allow (`CallInClauseGuard` over the call when it parses, otherwise
/// `UnexpectedToken` at the `(` with the multi-line hint).
fn Parser::parse_function_call_in_clause_guard(
  self : Parser,
  start : Int32,
) -> Unit raise ParseError {
  if self.maybe_one(LeftParen) is Some((l_paren_start, l_paren_end)) {
    let closed = try {
      let _ = self.parse_fn_arguments()
      Some(self.expect_one(RightParen))
    } catch {
      _ => None
    } noraise {
      v => v
    }
    if closed is Some((_, end)) {
      parse_error(CallInClauseGuard, sp(start, end))
    }
    raise add_multi_line_clause_hint(
      ParseError(
        error=UnexpectedToken(
          token=LeftParen,
          expected=[token_text(RArrow)][:],
          hint=None,
        ),
        location=sp(l_paren_start, l_paren_end),
      ),
    )
  }
}

///|
/// One guard unit: `!unit`, a name with field accesses and tuple indexes,
/// a `{ }` block, or a constant.
fn Parser::parse_case_clause_guard_unit(
  self : Parser,
) -> @ast.ClauseGuard? raise ParseError {
  self.enter()
  let result = self.parse_case_clause_guard_unit_inner()
  self.leave()
  result
}

///|
fn Parser::parse_case_clause_guard_unit_inner(
  self : Parser,
) -> @ast.ClauseGuard? raise ParseError {
  match self.token0 {
    Some({ kind: Bang, span, .. }) => {
      self.advance()
      match self.parse_case_clause_guard_unit() {
        Some(unit) =>
          Some(
            Not(location=sp(span.start, unit.location().end), expression=unit),
          )
        None => parse_error(ExpectedValue, sp(span.start, span.start))
      }
    }
    Some({ kind: Name(name), span, .. }) => {
      self.advance()
      self.parse_function_call_in_clause_guard(span.start)
      let mut unit : @ast.ClauseGuard = match
        self.parse_record_in_clause_guard(name, span) {
        Some(record) => record
        None => Var(location=span, name~)
      }
      while true {
        guard self.maybe_one(Dot) is Some((dot_s, _)) else { return Some(unit) }
        match self.next_token() {
          Some({ kind: Int(value~, ..), span: int_span, .. }) =>
            match parse_u64(value.replace(old="_", new="")) {
              Some(index) =>
                unit = TupleIndex(
                  location=sp(dot_s, int_span.end),
                  index~,
                  tuple=unit,
                )
              None => parse_error(InvalidTupleAccess, span)
            }
          Some({ kind: Name(label), span: name_span, .. }) => {
            self.parse_function_call_in_clause_guard(span.start)
            unit = FieldAccess(
              label_location=name_span,
              index=None,
              label~,
              container=unit,
            )
          }
          Some({ span: other, .. }) =>
            parse_error(IncorrectName(Variable), other)
          None => self.next_token_unexpected(["A positive integer"])
        }
      }
      Some(unit)
    }
    Some({ kind: LeftBrace, span, .. }) => {
      self.advance()
      Some(self.parse_case_clause_guard_block(span.start))
    }
    _ =>
      match self.parse_const_value_unit() {
        Some(constant) => Some(Constant(constant))
        None => None
      }
  }
}

///|
/// `{ guard }` with `{` consumed; an empty block is `EmptyGuardBlock`.
fn Parser::parse_case_clause_guard_block(
  self : Parser,
  start : Int32,
) -> @ast.ClauseGuard raise ParseError {
  match self.parse_clause_guard_inner() {
    Some(body) => {
      let (_, end) = self.expect_one(RightBrace)
      Block(location=sp(start, end), value=body)
    }
    None => {
      let location = match self.next_token() {
        Some({ kind: RightBrace, span, .. }) => sp(start, span.end)
        _ => sp(start, start + 1)
      }
      parse_error(EmptyGuardBlock, location)
    }
  }
}

///|
/// `module.Constructor(...)` in a guard: a constant record.
fn Parser::parse_record_in_clause_guard(
  self : Parser,
  module_ : String,
  module_location : @span.SrcSpan,
) -> @ast.ClauseGuard? raise ParseError {
  guard self.token0 is Some({ kind: Dot, .. }) &&
    self.token1 is Some({ kind: UpName(_), .. }) else {
    return None
  }
  self.advance()
  guard self.next_token() is Some({ kind: UpName(name), span, .. }) else {
    return None
  }
  match
    self.parse_const_record_finish(
      module_location.start,
      Some({ location: module_location, name: module_, }),
      name,
      span.end,
    ) {
    Some(record) => Some(Constant(record))
    None => None
  }
}