///|
fn parse_hex(text : String) -> Int64 raise ConfigError {
  if text.is_empty() || text.has_prefix("-") || text.has_prefix("+") {
    raise Invalid("hex requires unsigned digits")
  }
  parse_number(
    if text.has_prefix("0x") || text.has_prefix("0X") {
      text
    } else {
      "0x" + text
    },
  )
}

///|
fn Expr::typed_value(
  self : Expr,
  kind : Kind,
  values : Map[String, Value],
  linux612~ : Bool = false,
) -> Value raise ConfigError {
  if linux612 {
    if kind == Boolean || kind == Tristate {
      return Logic(self.evaluate(values, linux612=true).logic())
    }
    let raw = match self {
      NumericLiteral(raw, _) => raw
      SymbolRef(name) => values.get(name).unwrap_or(Text(name)).text()
      Constant(v) => v.text()
      _ => return linux_initial(kind)
    }
    return match kind {
      Integer | Hex => RawNumber(raw, kind)
      StringType => Text(raw)
      _ => Logic(self.evaluate(values, linux612=true).logic())
    }
  }
  match self {
    NumericLiteral(raw, n) =>
      match kind {
        Hex => Number(parse_hex(raw))
        StringType => Text(raw)
        Integer => {
          if raw.contains("x") || raw.contains("X") {
            raise Invalid("integer defaults require decimal literals")
          }
          Number(n)
        }
        _ => Logic(N)
      }
    // Unquoted A-F-only hex literals are lexically indistinguishable from
    // undefined references. Defined symbols always take precedence.
    SymbolRef(raw) =>
      if kind == Hex && !values.contains(raw) {
        Number(parse_hex(raw))
      } else {
        typed_default(self.evaluate(values), kind)
      }
    _ => typed_default(self.evaluate(values), kind)
  }
}