///|
/// Parse only the pinned signed Int32 literal forms, without a machine Int
/// intermediate. The parser has already classified the token as an Int literal.
fn library_int32_literal(
raw : String,
loc : @location.Location,
) -> String raise FrontendError {
let chars = raw.to_array()
let negative = chars.length() > 0 && chars[0] == '-'
let mut start = if negative { 1 } else { 0 }
let mut radix = 10
if chars.length() > start + 1 && chars[start] == '0' {
match chars[start + 1] {
'x' | 'X' => radix = 16
'b' | 'B' => radix = 2
'o' | 'O' => radix = 8
_ => ()
}
if radix != 10 {
start += 2
}
}
let mut value = 0L
let mut digits = 0
for i = start; i < chars.length(); i = i + 1 {
if chars[i] == '_' {
continue
}
let code = chars[i].to_int()
let digit = if code >= 48 && code <= 57 {
code - 48
} else if code >= 65 && code <= 70 {
code - 55
} else if code >= 97 && code <= 102 {
code - 87
} else {
-1
}
if digit < 0 || digit >= radix {
reject("invalid_literal", "Invalid signed Int32 literal", loc)
}
value = value * radix.to_int64() + digit.to_int64()
if value > 4294967295L {
reject("invalid_literal", "Signed Int32 literal exceeds 32 bits", loc)
}
digits += 1
}
let decimal_limit = if negative { 2147483648L } else { 2147483647L }
if digits == 0 ||
(radix == 10 && value > decimal_limit) ||
(negative && value > 2147483648L) {
reject("invalid_literal", "Signed Int32 literal is out of range", loc)
}
(if negative {
-value
} else if radix != 10 && value > 2147483647L {
value - 4294967296L
} else {
value
}).to_string()
}
///|
/// The lexer chooses the suffix family. Reconstruct the exact integer from
/// digits so neither the host Int64 nor a floating value can truncate it.
fn library_integer_literal(
raw : String,
scalar : LibraryScalar,
loc : @location.Location,
) -> String raise FrontendError {
if scalar == SignedInt32 {
return library_int32_literal(raw, loc)
}
let suffix = match scalar {
SignedInt64 => "L"
UnsignedInt32 => "U"
UnsignedInt64 => "UL"
UnboundedInteger => "N"
_ => ""
}
let digits_text = if suffix != "" && raw.has_suffix(suffix) {
raw[:raw.length() - suffix.length()].to_owned()
} else {
raw
}
let chars = digits_text.to_array()
let negative = chars.length() > 0 && chars[0] == '-'
let mut start = if negative { 1 } else { 0 }
let mut radix = 10
if chars.length() > start + 1 && chars[start] == '0' {
match chars[start + 1] {
'x' | 'X' => radix = 16
'b' | 'B' => radix = 2
'o' | 'O' => radix = 8
_ => ()
}
if radix != 10 {
start += 2
}
}
let mut value = @bigint.BigInt::from_int(0)
let mut digits = 0
for i = start; i < chars.length(); i = i + 1 {
if chars[i] == '_' {
continue
}
let code = chars[i].to_int()
let digit = if code >= 48 && code <= 57 {
code - 48
} else if code >= 65 && code <= 70 {
code - 55
} else if code >= 97 && code <= 102 {
code - 87
} else {
-1
}
if digit < 0 || digit >= radix {
reject("invalid_literal", "Invalid integer digit", loc)
}
value = value * @bigint.BigInt::from_int(radix) +
@bigint.BigInt::from_int(digit)
digits += 1
if digits > 4096 {
reject("literal_limit", "Integer literal exceeds the digit budget", loc)
}
}
if digits == 0 || (negative && !scalar.is_signed()) {
reject("invalid_literal", "Invalid integer literal", loc)
}
let bits = scalar.bits()
if bits != 0 {
let modulus = @bigint.BigInt::from_int(1) << bits
let max_unsigned = modulus - @bigint.BigInt::from_int(1)
if value > max_unsigned {
reject("invalid_literal", "Integer literal exceeds its source width", loc)
}
if scalar.is_signed() {
let signed_limit = @bigint.BigInt::from_int(1) << (bits - 1)
if negative {
if value > signed_limit {
reject("invalid_literal", "Signed literal is out of range", loc)
}
return (-value).to_string()
}
if radix == 10 {
if value >= signed_limit {
reject("invalid_literal", "Signed literal is out of range", loc)
}
} else if value >= signed_limit {
value = value - modulus
}
}
} else if negative {
value = -value
}
value.to_string()
}