mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Integers with different bases
This commit is contained in:
parent
f738d89ff2
commit
570c528b3e
@ -43,11 +43,18 @@ The number can be negative, zero, or positive. As Typst uses 64 bits to store
|
|||||||
integers, integers cannot be smaller than `{-9223372036854775808}` or larger than
|
integers, integers cannot be smaller than `{-9223372036854775808}` or larger than
|
||||||
`{9223372036854775807}`.
|
`{9223372036854775807}`.
|
||||||
|
|
||||||
|
The number can also be specified as hexadecimal, octal, or binary by starting it
|
||||||
|
with a zero followed by either `x`, `o`, or `b`.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
```example
|
```example
|
||||||
#(1 + 2) \
|
#(1 + 2) \
|
||||||
#(2 - 5) \
|
#(2 - 5) \
|
||||||
#(3 + 4 < 8)
|
#(3 + 4 < 8)
|
||||||
|
|
||||||
|
#0xff \
|
||||||
|
#0o10 \
|
||||||
|
#0b1001
|
||||||
```
|
```
|
||||||
|
|
||||||
# Float
|
# Float
|
||||||
|
@ -912,7 +912,17 @@ node! {
|
|||||||
impl Int {
|
impl Int {
|
||||||
/// Get the integer value.
|
/// Get the integer value.
|
||||||
pub fn get(&self) -> i64 {
|
pub fn get(&self) -> i64 {
|
||||||
self.0.text().parse().unwrap_or_default()
|
let text = self.0.text();
|
||||||
|
if let Some(rest) = text.strip_prefix("0x") {
|
||||||
|
i64::from_str_radix(rest, 16)
|
||||||
|
} else if let Some(rest) = text.strip_prefix("0o") {
|
||||||
|
i64::from_str_radix(rest, 8)
|
||||||
|
} else if let Some(rest) = text.strip_prefix("0b") {
|
||||||
|
i64::from_str_radix(rest, 2)
|
||||||
|
} else {
|
||||||
|
text.parse()
|
||||||
|
}
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,9 +524,28 @@ impl Lexer<'_> {
|
|||||||
SyntaxKind::Ident
|
SyntaxKind::Ident
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number(&mut self, start: usize, c: char) -> SyntaxKind {
|
fn number(&mut self, mut start: usize, c: char) -> SyntaxKind {
|
||||||
|
// Handle alternative integer bases.
|
||||||
|
let mut base = 10;
|
||||||
|
if c == '0' {
|
||||||
|
if self.s.eat_if('b') {
|
||||||
|
base = 2;
|
||||||
|
} else if self.s.eat_if('o') {
|
||||||
|
base = 8;
|
||||||
|
} else if self.s.eat_if('x') {
|
||||||
|
base = 16;
|
||||||
|
}
|
||||||
|
if base != 10 {
|
||||||
|
start = self.s.cursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read the first part (integer or fractional depending on `first`).
|
// Read the first part (integer or fractional depending on `first`).
|
||||||
self.s.eat_while(char::is_ascii_digit);
|
self.s.eat_while(if base == 16 {
|
||||||
|
char::is_ascii_alphanumeric
|
||||||
|
} else {
|
||||||
|
char::is_ascii_digit
|
||||||
|
});
|
||||||
|
|
||||||
// Read the fractional part if not already done.
|
// Read the fractional part if not already done.
|
||||||
// Make sure not to confuse a range for the decimal separator.
|
// Make sure not to confuse a range for the decimal separator.
|
||||||
@ -534,12 +553,13 @@ impl Lexer<'_> {
|
|||||||
&& !self.s.at("..")
|
&& !self.s.at("..")
|
||||||
&& !self.s.scout(1).map_or(false, is_id_start)
|
&& !self.s.scout(1).map_or(false, is_id_start)
|
||||||
&& self.s.eat_if('.')
|
&& self.s.eat_if('.')
|
||||||
|
&& base == 10
|
||||||
{
|
{
|
||||||
self.s.eat_while(char::is_ascii_digit);
|
self.s.eat_while(char::is_ascii_digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the exponent.
|
// Read the exponent.
|
||||||
if !self.s.at("em") && self.s.eat_if(['e', 'E']) {
|
if !self.s.at("em") && self.s.eat_if(['e', 'E']) && base == 10 {
|
||||||
self.s.eat_if(['+', '-']);
|
self.s.eat_if(['+', '-']);
|
||||||
self.s.eat_while(char::is_ascii_digit);
|
self.s.eat_while(char::is_ascii_digit);
|
||||||
}
|
}
|
||||||
@ -553,14 +573,21 @@ impl Lexer<'_> {
|
|||||||
let number = self.s.get(start..suffix_start);
|
let number = self.s.get(start..suffix_start);
|
||||||
let suffix = self.s.from(suffix_start);
|
let suffix = self.s.from(suffix_start);
|
||||||
|
|
||||||
|
let kind = if i64::from_str_radix(number, base).is_ok() {
|
||||||
|
SyntaxKind::Int
|
||||||
|
} else if base == 10 && number.parse::<f64>().is_ok() {
|
||||||
|
SyntaxKind::Float
|
||||||
|
} else {
|
||||||
|
return self.error(match base {
|
||||||
|
2 => "invalid binary number",
|
||||||
|
8 => "invalid octal number",
|
||||||
|
16 => "invalid hexadecimal number",
|
||||||
|
_ => "invalid number",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if suffix.is_empty() {
|
if suffix.is_empty() {
|
||||||
return if number.parse::<i64>().is_ok() {
|
return kind;
|
||||||
SyntaxKind::Int
|
|
||||||
} else if number.parse::<f64>().is_ok() {
|
|
||||||
SyntaxKind::Float
|
|
||||||
} else {
|
|
||||||
self.error("invalid number")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches!(
|
if !matches!(
|
||||||
|
@ -109,6 +109,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test numbers with alternative bases.
|
||||||
|
#test(0x10, 16)
|
||||||
|
#test(0b1101, 13)
|
||||||
|
#test(0xA + 0xa, 0x14)
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 2-7 invalid binary number
|
||||||
|
#0b123
|
||||||
|
|
||||||
|
---
|
||||||
|
// Error: 2-8 invalid hexadecimal number
|
||||||
|
#0x123z
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test boolean operators.
|
// Test boolean operators.
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "constant.numeric.integer.typst",
|
"name": "constant.numeric.integer.typst",
|
||||||
"match": "\\b\\d+\\b"
|
"match": "\\b(0x[0-9a-zA-Z]+|(0b|0o)?\\d+)\\b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "constant.numeric.float.typst",
|
"name": "constant.numeric.float.typst",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user