///|
// Called only for grammar-validated JSON number lexemes. Normalize decimal
// position exactly, without converting to Double (which can round fractions).
fn exact_positive_integer(source : String) -> Int32? {
  if source.has_prefix("-") {
    return None
  }
  let chars = source.to_array()
  let mut digits = 0
  let mut fractional = 0
  let mut after_dot = false
  let mut first_nonzero = -1
  let mut last_nonzero = -1
  let mut exponent_start = chars.length()
  for i, char in chars {
    if char is ('e' | 'E') {
      exponent_start = i + 1
      break
    }
    if char == '.' {
      after_dot = true
    } else {
      if char != '0' {
        if first_nonzero < 0 {
          first_nonzero = digits
        }
        last_nonzero = digits
      }
      digits += 1
      if after_dot {
        fractional += 1
      }
    }
  }
  if first_nonzero < 0 {
    return None
  }
  let mut negative_exponent = false
  let mut exponent = 0
  // Saturation is safe: a larger exponent cannot cancel the bounded mantissa
  // into a nonzero 32-bit integer. It also avoids exponent arithmetic overflow.
  let ceiling = source.length() + 10
  for char in chars[exponent_start:] {
    match char {
      '-' => negative_exponent = true
      '+' => ()
      _ => {
        let digit = char.to_int() - '0'.to_int()
        exponent = if exponent > (ceiling - digit) / 10 {
          ceiling
        } else {
          exponent * 10 + digit
        }
      }
    }
  }
  let exponent = if negative_exponent { -exponent } else { exponent }
  let cutoff = digits + exponent - fractional
  if last_nonzero >= cutoff || cutoff - first_nonzero > 10 {
    return None
  }
  let mut ordinal = 0
  let mut result = 0
  for char in chars {
    if char is ('e' | 'E') {
      break
    }
    if char != '.' {
      if ordinal >= first_nonzero && ordinal < cutoff {
        let digit = char.to_int() - '0'.to_int()
        if result > (2147483647 - digit) / 10 {
          return None
        }
        result = result * 10 + digit
      }
      ordinal += 1
    }
  }
  for _ in digits.. 2147483647 / 10 {
      return None
    }
    result *= 10
  }
  Some(result)
}