summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/src/reference/types.md7
-rw-r--r--src/syntax/ast.rs12
-rw-r--r--src/syntax/lexer.rs47
-rw-r--r--tests/typ/compiler/ops.typ14
-rw-r--r--tools/support/typst.tmLanguage.json2
5 files changed, 70 insertions, 12 deletions
diff --git a/docs/src/reference/types.md b/docs/src/reference/types.md
index c323ab41..312ba7c0 100644
--- a/docs/src/reference/types.md
+++ b/docs/src/reference/types.md
@@ -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
`{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
#(1 + 2) \
#(2 - 5) \
#(3 + 4 < 8)
+
+#0xff \
+#0o10 \
+#0b1001
```
# Float
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 4abf51d9..d718ccf0 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -912,7 +912,17 @@ node! {
impl Int {
/// Get the integer value.
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()
}
}
diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs
index 748cb076..b67a009c 100644
--- a/src/syntax/lexer.rs
+++ b/src/syntax/lexer.rs
@@ -524,9 +524,28 @@ impl Lexer<'_> {
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`).
- 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.
// Make sure not to confuse a range for the decimal separator.
@@ -534,12 +553,13 @@ impl Lexer<'_> {
&& !self.s.at("..")
&& !self.s.scout(1).map_or(false, is_id_start)
&& self.s.eat_if('.')
+ && base == 10
{
self.s.eat_while(char::is_ascii_digit);
}
// 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_while(char::is_ascii_digit);
}
@@ -553,14 +573,21 @@ impl Lexer<'_> {
let number = self.s.get(start..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() {
- return if number.parse::<i64>().is_ok() {
- SyntaxKind::Int
- } else if number.parse::<f64>().is_ok() {
- SyntaxKind::Float
- } else {
- self.error("invalid number")
- };
+ return kind;
}
if !matches!(
diff --git a/tests/typ/compiler/ops.typ b/tests/typ/compiler/ops.typ
index a29003ed..1a9a2169 100644
--- a/tests/typ/compiler/ops.typ
+++ b/tests/typ/compiler/ops.typ
@@ -110,6 +110,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 not.
diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json
index 1e7e94e0..7e01c41a 100644
--- a/tools/support/typst.tmLanguage.json
+++ b/tools/support/typst.tmLanguage.json
@@ -344,7 +344,7 @@
},
{
"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",