///|
/// Parse RFC 4506 decimal/octal/hex integer literals exactly, without a machine-word cap.
/// The 4096-character ceiling is an operation budget, not an integer type restriction.
pub fn parse_integer_constant(
  text : String,
  offset? : Int = 0,
) -> @bigint.BigInt raise XdrError {
  let cs = text.to_array()
  if cs.length() > 4096 {
    raise Invalid(offset, "constant literal resource limit")
  }
  let negative = cs.length() > 0 && cs[0] == '-'
  let mut i = if negative { 1 } else { 0 }
  if i == cs.length() {
    raise Invalid(offset, "missing integer digits")
  }
  let mut radix = 10
  if i + 1 < cs.length() && cs[i] == '0' {
    if cs[i + 1] == 'x' || cs[i + 1] == 'X' {
      radix = 16
      i += 2
    } else {
      radix = 8
    }
  }
  if i == cs.length() {
    raise Invalid(offset, "missing hexadecimal digits")
  }
  let digits = StringBuilder()
  while i < cs.length() {
    let c = cs[i]
    let d = if c >= '0' && c <= '9' {
      c.to_int() - 48
    } else if c >= 'a' && c <= 'f' {
      c.to_int() - 87
    } else if c >= 'A' && c <= 'F' {
      c.to_int() - 55
    } else {
      raise Invalid(offset + i, "invalid integer digit")
    }
    if d >= radix {
      raise Invalid(offset + i, "invalid integer digit")
    }
    digits.write_char(c)
    i += 1
  }
  let n = @bigint.BigInt::from_string(digits.to_string(), radix~)
  if negative {
    -n
  } else {
    n
  }
}

///|
fn constant_i32(n : @bigint.BigInt, offset : Int) -> Int raise XdrError {
  if n.compare_int64(-2147483648L) < 0 || n.compare_int64(2147483647L) > 0 {
    raise Invalid(offset, "signed 32-bit value out of range")
  }
  n.to_int()
}

///|
fn constant_u32(n : @bigint.BigInt, offset : Int) -> UInt raise XdrError {
  if n.compare_int(0) < 0 || n.compare_uint(4294967295U) > 0 {
    raise Invalid(offset, "unsigned 32-bit value out of range")
  }
  n.to_uint()
}

///|
fn constant_tag(n : @bigint.BigInt, offset : Int) -> Int64 raise XdrError {
  if n.compare_int64(-2147483648L) < 0 || n.compare_int64(4294967295L) > 0 {
    raise Invalid(offset, "union case outside signed/unsigned 32-bit range")
  }
  n.to_int64()
}