diff options
Diffstat (limited to 'crates/syntax/src/ast')
-rw-r--r-- | crates/syntax/src/ast/expr_ext.rs | 55 | ||||
-rw-r--r-- | crates/syntax/src/ast/generated/tokens.rs | 50 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 64 | ||||
-rw-r--r-- | crates/syntax/src/ast/node_ext.rs | 10 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 173 |
5 files changed, 246 insertions, 106 deletions
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index f5ba87223..9253c97d0 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use crate::{ | 3 | use crate::{ |
4 | ast::{self, support, AstChildren, AstNode}, | 4 | ast::{self, support, AstChildren, AstNode}, |
5 | SmolStr, | 5 | AstToken, |
6 | SyntaxKind::*, | 6 | SyntaxKind::*, |
7 | SyntaxToken, T, | 7 | SyntaxToken, T, |
8 | }; | 8 | }; |
@@ -298,12 +298,12 @@ impl ast::ArrayExpr { | |||
298 | 298 | ||
299 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | 299 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] |
300 | pub enum LiteralKind { | 300 | pub enum LiteralKind { |
301 | String, | 301 | String(ast::String), |
302 | ByteString, | 302 | ByteString(ast::ByteString), |
303 | IntNumber(ast::IntNumber), | ||
304 | FloatNumber(ast::FloatNumber), | ||
303 | Char, | 305 | Char, |
304 | Byte, | 306 | Byte, |
305 | IntNumber { suffix: Option<SmolStr> }, | ||
306 | FloatNumber { suffix: Option<SmolStr> }, | ||
307 | Bool(bool), | 307 | Bool(bool), |
308 | } | 308 | } |
309 | 309 | ||
@@ -315,44 +315,25 @@ impl ast::Literal { | |||
315 | .and_then(|e| e.into_token()) | 315 | .and_then(|e| e.into_token()) |
316 | .unwrap() | 316 | .unwrap() |
317 | } | 317 | } |
318 | |||
319 | fn find_suffix(text: &str, possible_suffixes: &[&str]) -> Option<SmolStr> { | ||
320 | possible_suffixes | ||
321 | .iter() | ||
322 | .find(|&suffix| text.ends_with(suffix)) | ||
323 | .map(|&suffix| SmolStr::new(suffix)) | ||
324 | } | ||
325 | |||
326 | pub fn kind(&self) -> LiteralKind { | 318 | pub fn kind(&self) -> LiteralKind { |
327 | const INT_SUFFIXES: [&str; 12] = [ | ||
328 | "u64", "u32", "u16", "u8", "usize", "isize", "i64", "i32", "i16", "i8", "u128", "i128", | ||
329 | ]; | ||
330 | const FLOAT_SUFFIXES: [&str; 2] = ["f32", "f64"]; | ||
331 | |||
332 | let token = self.token(); | 319 | let token = self.token(); |
333 | 320 | ||
321 | if let Some(t) = ast::IntNumber::cast(token.clone()) { | ||
322 | return LiteralKind::IntNumber(t); | ||
323 | } | ||
324 | if let Some(t) = ast::FloatNumber::cast(token.clone()) { | ||
325 | return LiteralKind::FloatNumber(t); | ||
326 | } | ||
327 | if let Some(t) = ast::String::cast(token.clone()) { | ||
328 | return LiteralKind::String(t); | ||
329 | } | ||
330 | if let Some(t) = ast::ByteString::cast(token.clone()) { | ||
331 | return LiteralKind::ByteString(t); | ||
332 | } | ||
333 | |||
334 | match token.kind() { | 334 | match token.kind() { |
335 | INT_NUMBER => { | ||
336 | // FYI: there was a bug here previously, thus the if statement below is necessary. | ||
337 | // The lexer treats e.g. `1f64` as an integer literal. See | ||
338 | // https://github.com/rust-analyzer/rust-analyzer/issues/1592 | ||
339 | // and the comments on the linked PR. | ||
340 | |||
341 | let text = token.text(); | ||
342 | if let suffix @ Some(_) = Self::find_suffix(&text, &FLOAT_SUFFIXES) { | ||
343 | LiteralKind::FloatNumber { suffix } | ||
344 | } else { | ||
345 | LiteralKind::IntNumber { suffix: Self::find_suffix(&text, &INT_SUFFIXES) } | ||
346 | } | ||
347 | } | ||
348 | FLOAT_NUMBER => { | ||
349 | let text = token.text(); | ||
350 | LiteralKind::FloatNumber { suffix: Self::find_suffix(&text, &FLOAT_SUFFIXES) } | ||
351 | } | ||
352 | STRING | RAW_STRING => LiteralKind::String, | ||
353 | T![true] => LiteralKind::Bool(true), | 335 | T![true] => LiteralKind::Bool(true), |
354 | T![false] => LiteralKind::Bool(false), | 336 | T![false] => LiteralKind::Bool(false), |
355 | BYTE_STRING | RAW_BYTE_STRING => LiteralKind::ByteString, | ||
356 | CHAR => LiteralKind::Char, | 337 | CHAR => LiteralKind::Char, |
357 | BYTE => LiteralKind::Byte, | 338 | BYTE => LiteralKind::Byte, |
358 | _ => unreachable!(), | 339 | _ => unreachable!(), |
diff --git a/crates/syntax/src/ast/generated/tokens.rs b/crates/syntax/src/ast/generated/tokens.rs index abadd0b61..728b72cd7 100644 --- a/crates/syntax/src/ast/generated/tokens.rs +++ b/crates/syntax/src/ast/generated/tokens.rs | |||
@@ -70,16 +70,58 @@ impl AstToken for String { | |||
70 | } | 70 | } |
71 | 71 | ||
72 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 72 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
73 | pub struct RawString { | 73 | pub struct ByteString { |
74 | pub(crate) syntax: SyntaxToken, | 74 | pub(crate) syntax: SyntaxToken, |
75 | } | 75 | } |
76 | impl std::fmt::Display for RawString { | 76 | impl std::fmt::Display for ByteString { |
77 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 77 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
78 | std::fmt::Display::fmt(&self.syntax, f) | 78 | std::fmt::Display::fmt(&self.syntax, f) |
79 | } | 79 | } |
80 | } | 80 | } |
81 | impl AstToken for RawString { | 81 | impl AstToken for ByteString { |
82 | fn can_cast(kind: SyntaxKind) -> bool { kind == RAW_STRING } | 82 | fn can_cast(kind: SyntaxKind) -> bool { kind == BYTE_STRING } |
83 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
84 | if Self::can_cast(syntax.kind()) { | ||
85 | Some(Self { syntax }) | ||
86 | } else { | ||
87 | None | ||
88 | } | ||
89 | } | ||
90 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | ||
91 | } | ||
92 | |||
93 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
94 | pub struct IntNumber { | ||
95 | pub(crate) syntax: SyntaxToken, | ||
96 | } | ||
97 | impl std::fmt::Display for IntNumber { | ||
98 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
99 | std::fmt::Display::fmt(&self.syntax, f) | ||
100 | } | ||
101 | } | ||
102 | impl AstToken for IntNumber { | ||
103 | fn can_cast(kind: SyntaxKind) -> bool { kind == INT_NUMBER } | ||
104 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
105 | if Self::can_cast(syntax.kind()) { | ||
106 | Some(Self { syntax }) | ||
107 | } else { | ||
108 | None | ||
109 | } | ||
110 | } | ||
111 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | ||
112 | } | ||
113 | |||
114 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
115 | pub struct FloatNumber { | ||
116 | pub(crate) syntax: SyntaxToken, | ||
117 | } | ||
118 | impl std::fmt::Display for FloatNumber { | ||
119 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
120 | std::fmt::Display::fmt(&self.syntax, f) | ||
121 | } | ||
122 | } | ||
123 | impl AstToken for FloatNumber { | ||
124 | fn can_cast(kind: SyntaxKind) -> bool { kind == FLOAT_NUMBER } | ||
83 | fn cast(syntax: SyntaxToken) -> Option<Self> { | 125 | fn cast(syntax: SyntaxToken) -> Option<Self> { |
84 | if Self::can_cast(syntax.kind()) { | 126 | if Self::can_cast(syntax.kind()) { |
85 | Some(Self { syntax }) | 127 | Some(Self { syntax }) |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 5b06cb767..b1578820f 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -110,8 +110,16 @@ pub fn record_expr_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::Re | |||
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | pub fn record_field(name: ast::NameRef, ty: ast::Type) -> ast::RecordField { | 113 | pub fn record_field( |
114 | ast_from_text(&format!("struct S {{ {}: {}, }}", name, ty)) | 114 | visibility: Option<ast::Visibility>, |
115 | name: ast::Name, | ||
116 | ty: ast::Type, | ||
117 | ) -> ast::RecordField { | ||
118 | let visibility = match visibility { | ||
119 | None => String::new(), | ||
120 | Some(it) => format!("{} ", it), | ||
121 | }; | ||
122 | ast_from_text(&format!("struct S {{ {}{}: {}, }}", visibility, name, ty)) | ||
115 | } | 123 | } |
116 | 124 | ||
117 | pub fn block_expr( | 125 | pub fn block_expr( |
@@ -351,6 +359,38 @@ pub fn visibility_pub_crate() -> ast::Visibility { | |||
351 | ast_from_text("pub(crate) struct S") | 359 | ast_from_text("pub(crate) struct S") |
352 | } | 360 | } |
353 | 361 | ||
362 | pub fn visibility_pub() -> ast::Visibility { | ||
363 | ast_from_text("pub struct S") | ||
364 | } | ||
365 | |||
366 | pub fn tuple_field_list(fields: impl IntoIterator<Item = ast::TupleField>) -> ast::TupleFieldList { | ||
367 | let fields = fields.into_iter().join(", "); | ||
368 | ast_from_text(&format!("struct f({});", fields)) | ||
369 | } | ||
370 | |||
371 | pub fn record_field_list( | ||
372 | fields: impl IntoIterator<Item = ast::RecordField>, | ||
373 | ) -> ast::RecordFieldList { | ||
374 | let fields = fields.into_iter().join(", "); | ||
375 | ast_from_text(&format!("struct f {{ {} }}", fields)) | ||
376 | } | ||
377 | |||
378 | pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::TupleField { | ||
379 | let visibility = match visibility { | ||
380 | None => String::new(), | ||
381 | Some(it) => format!("{} ", it), | ||
382 | }; | ||
383 | ast_from_text(&format!("struct f({}{});", visibility, ty)) | ||
384 | } | ||
385 | |||
386 | pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant { | ||
387 | let field_list = match field_list { | ||
388 | None => String::new(), | ||
389 | Some(it) => format!("{}", it), | ||
390 | }; | ||
391 | ast_from_text(&format!("enum f {{ {}{} }}", name, field_list)) | ||
392 | } | ||
393 | |||
354 | pub fn fn_( | 394 | pub fn fn_( |
355 | visibility: Option<ast::Visibility>, | 395 | visibility: Option<ast::Visibility>, |
356 | fn_name: ast::Name, | 396 | fn_name: ast::Name, |
@@ -373,6 +413,26 @@ pub fn fn_( | |||
373 | )) | 413 | )) |
374 | } | 414 | } |
375 | 415 | ||
416 | pub fn struct_( | ||
417 | visibility: Option<ast::Visibility>, | ||
418 | strukt_name: ast::Name, | ||
419 | type_params: Option<ast::GenericParamList>, | ||
420 | field_list: ast::FieldList, | ||
421 | ) -> ast::Struct { | ||
422 | let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; | ||
423 | let type_params = | ||
424 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | ||
425 | let visibility = match visibility { | ||
426 | None => String::new(), | ||
427 | Some(it) => format!("{} ", it), | ||
428 | }; | ||
429 | |||
430 | ast_from_text(&format!( | ||
431 | "{}struct {}{}{}{}", | ||
432 | visibility, strukt_name, type_params, field_list, semicolon | ||
433 | )) | ||
434 | } | ||
435 | |||
376 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 436 | fn ast_from_text<N: AstNode>(text: &str) -> N { |
377 | let parse = SourceFile::parse(text); | 437 | let parse = SourceFile::parse(text); |
378 | let node = match parse.tree().syntax().descendants().find_map(N::cast) { | 438 | let node = match parse.tree().syntax().descendants().find_map(N::cast) { |
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index c5cd1c504..ce35ac01a 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs | |||
@@ -7,7 +7,7 @@ use itertools::Itertools; | |||
7 | use parser::SyntaxKind; | 7 | use parser::SyntaxKind; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | ast::{self, support, token_ext::HasStringValue, AstNode, AstToken, NameOwner, SyntaxNode}, | 10 | ast::{self, support, AstNode, AstToken, NameOwner, SyntaxNode}, |
11 | SmolStr, SyntaxElement, SyntaxToken, T, | 11 | SmolStr, SyntaxElement, SyntaxToken, T, |
12 | }; | 12 | }; |
13 | 13 | ||
@@ -55,13 +55,7 @@ impl ast::Attr { | |||
55 | let key = self.simple_name()?; | 55 | let key = self.simple_name()?; |
56 | let value_token = lit.syntax().first_token()?; | 56 | let value_token = lit.syntax().first_token()?; |
57 | 57 | ||
58 | let value: SmolStr = if let Some(s) = ast::String::cast(value_token.clone()) { | 58 | let value: SmolStr = ast::String::cast(value_token.clone())?.value()?.into(); |
59 | s.value()?.into() | ||
60 | } else if let Some(s) = ast::RawString::cast(value_token) { | ||
61 | s.value()?.into() | ||
62 | } else { | ||
63 | return None; | ||
64 | }; | ||
65 | 59 | ||
66 | Some((key, value)) | 60 | Some((key, value)) |
67 | } | 61 | } |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index c5ef92733..e4e512f2e 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -8,11 +8,11 @@ use std::{ | |||
8 | use rustc_lexer::unescape::{unescape_literal, Mode}; | 8 | use rustc_lexer::unescape::{unescape_literal, Mode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast::{AstToken, Comment, RawString, String, Whitespace}, | 11 | ast::{self, AstToken}, |
12 | TextRange, TextSize, | 12 | TextRange, TextSize, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | impl Comment { | 15 | impl ast::Comment { |
16 | pub fn kind(&self) -> CommentKind { | 16 | pub fn kind(&self) -> CommentKind { |
17 | kind_by_prefix(self.text()) | 17 | kind_by_prefix(self.text()) |
18 | } | 18 | } |
@@ -80,7 +80,7 @@ fn kind_by_prefix(text: &str) -> CommentKind { | |||
80 | panic!("bad comment text: {:?}", text) | 80 | panic!("bad comment text: {:?}", text) |
81 | } | 81 | } |
82 | 82 | ||
83 | impl Whitespace { | 83 | impl ast::Whitespace { |
84 | pub fn spans_multiple_lines(&self) -> bool { | 84 | pub fn spans_multiple_lines(&self) -> bool { |
85 | let text = self.text(); | 85 | let text = self.text(); |
86 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) | 86 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) |
@@ -114,43 +114,28 @@ impl QuoteOffsets { | |||
114 | } | 114 | } |
115 | } | 115 | } |
116 | 116 | ||
117 | pub trait HasQuotes: AstToken { | 117 | impl ast::String { |
118 | fn quote_offsets(&self) -> Option<QuoteOffsets> { | 118 | pub fn is_raw(&self) -> bool { |
119 | let text = self.text().as_str(); | 119 | self.text().starts_with('r') |
120 | let offsets = QuoteOffsets::new(text)?; | ||
121 | let o = self.syntax().text_range().start(); | ||
122 | let offsets = QuoteOffsets { | ||
123 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
124 | contents: offsets.contents + o, | ||
125 | }; | ||
126 | Some(offsets) | ||
127 | } | ||
128 | fn open_quote_text_range(&self) -> Option<TextRange> { | ||
129 | self.quote_offsets().map(|it| it.quotes.0) | ||
130 | } | 120 | } |
131 | 121 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { | |
132 | fn close_quote_text_range(&self) -> Option<TextRange> { | 122 | let contents_range = self.text_range_between_quotes()?; |
133 | self.quote_offsets().map(|it| it.quotes.1) | 123 | assert!(TextRange::up_to(contents_range.len()).contains_range(range)); |
124 | Some(range + contents_range.start()) | ||
134 | } | 125 | } |
135 | 126 | ||
136 | fn text_range_between_quotes(&self) -> Option<TextRange> { | 127 | pub fn value(&self) -> Option<Cow<'_, str>> { |
137 | self.quote_offsets().map(|it| it.contents) | 128 | if self.is_raw() { |
138 | } | 129 | let text = self.text().as_str(); |
139 | } | 130 | let text = |
140 | 131 | &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | |
141 | impl HasQuotes for String {} | 132 | return Some(Cow::Borrowed(text)); |
142 | impl HasQuotes for RawString {} | 133 | } |
143 | |||
144 | pub trait HasStringValue: HasQuotes { | ||
145 | fn value(&self) -> Option<Cow<'_, str>>; | ||
146 | } | ||
147 | 134 | ||
148 | impl HasStringValue for String { | ||
149 | fn value(&self) -> Option<Cow<'_, str>> { | ||
150 | let text = self.text().as_str(); | 135 | let text = self.text().as_str(); |
151 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 136 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
152 | 137 | ||
153 | let mut buf = std::string::String::with_capacity(text.len()); | 138 | let mut buf = String::with_capacity(text.len()); |
154 | let mut has_error = false; | 139 | let mut has_error = false; |
155 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { | 140 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { |
156 | Ok(c) => buf.push(c), | 141 | Ok(c) => buf.push(c), |
@@ -164,21 +149,31 @@ impl HasStringValue for String { | |||
164 | let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) }; | 149 | let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) }; |
165 | Some(res) | 150 | Some(res) |
166 | } | 151 | } |
167 | } | ||
168 | 152 | ||
169 | impl HasStringValue for RawString { | 153 | pub fn quote_offsets(&self) -> Option<QuoteOffsets> { |
170 | fn value(&self) -> Option<Cow<'_, str>> { | ||
171 | let text = self.text().as_str(); | 154 | let text = self.text().as_str(); |
172 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 155 | let offsets = QuoteOffsets::new(text)?; |
173 | Some(Cow::Borrowed(text)) | 156 | let o = self.syntax().text_range().start(); |
157 | let offsets = QuoteOffsets { | ||
158 | quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), | ||
159 | contents: offsets.contents + o, | ||
160 | }; | ||
161 | Some(offsets) | ||
162 | } | ||
163 | pub fn text_range_between_quotes(&self) -> Option<TextRange> { | ||
164 | self.quote_offsets().map(|it| it.contents) | ||
165 | } | ||
166 | pub fn open_quote_text_range(&self) -> Option<TextRange> { | ||
167 | self.quote_offsets().map(|it| it.quotes.0) | ||
168 | } | ||
169 | pub fn close_quote_text_range(&self) -> Option<TextRange> { | ||
170 | self.quote_offsets().map(|it| it.quotes.1) | ||
174 | } | 171 | } |
175 | } | 172 | } |
176 | 173 | ||
177 | impl RawString { | 174 | impl ast::ByteString { |
178 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { | 175 | pub fn is_raw(&self) -> bool { |
179 | let contents_range = self.text_range_between_quotes()?; | 176 | self.text().starts_with("br") |
180 | assert!(TextRange::up_to(contents_range.len()).contains_range(range)); | ||
181 | Some(range + contents_range.start()) | ||
182 | } | 177 | } |
183 | } | 178 | } |
184 | 179 | ||
@@ -500,7 +495,7 @@ pub trait HasFormatSpecifier: AstToken { | |||
500 | } | 495 | } |
501 | } | 496 | } |
502 | 497 | ||
503 | impl HasFormatSpecifier for String { | 498 | impl HasFormatSpecifier for ast::String { |
504 | fn char_ranges( | 499 | fn char_ranges( |
505 | &self, | 500 | &self, |
506 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { | 501 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { |
@@ -521,18 +516,86 @@ impl HasFormatSpecifier for String { | |||
521 | } | 516 | } |
522 | } | 517 | } |
523 | 518 | ||
524 | impl HasFormatSpecifier for RawString { | 519 | impl ast::IntNumber { |
525 | fn char_ranges( | 520 | const SUFFIXES: &'static [&'static str] = &[ |
526 | &self, | 521 | "u8", "u16", "u32", "u64", "u128", "usize", // Unsigned. |
527 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { | 522 | "i8", "i16", "i32", "i64", "i128", "isize", // Signed. |
528 | let text = self.text().as_str(); | 523 | ]; |
529 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 524 | |
530 | let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); | 525 | pub fn radix(&self) -> Radix { |
526 | match self.text().get(..2).unwrap_or_default() { | ||
527 | "0b" => Radix::Binary, | ||
528 | "0o" => Radix::Octal, | ||
529 | "0x" => Radix::Hexadecimal, | ||
530 | _ => Radix::Decimal, | ||
531 | } | ||
532 | } | ||
531 | 533 | ||
532 | let mut res = Vec::with_capacity(text.len()); | 534 | pub fn value(&self) -> Option<u128> { |
533 | for (idx, c) in text.char_indices() { | 535 | let token = self.syntax(); |
534 | res.push((TextRange::at(idx.try_into().unwrap(), TextSize::of(c)) + offset, Ok(c))); | 536 | |
537 | let mut text = token.text().as_str(); | ||
538 | if let Some(suffix) = self.suffix() { | ||
539 | text = &text[..text.len() - suffix.len()] | ||
540 | } | ||
541 | |||
542 | let radix = self.radix(); | ||
543 | text = &text[radix.prefix_len()..]; | ||
544 | |||
545 | let buf; | ||
546 | if text.contains("_") { | ||
547 | buf = text.replace('_', ""); | ||
548 | text = buf.as_str(); | ||
549 | }; | ||
550 | |||
551 | let value = u128::from_str_radix(text, radix as u32).ok()?; | ||
552 | Some(value) | ||
553 | } | ||
554 | |||
555 | pub fn suffix(&self) -> Option<&str> { | ||
556 | let text = self.text(); | ||
557 | // FIXME: don't check a fixed set of suffixes, `1_0_1_l_o_l` is valid | ||
558 | // syntax, suffix is `l_o_l`. | ||
559 | ast::IntNumber::SUFFIXES.iter().chain(ast::FloatNumber::SUFFIXES.iter()).find_map( | ||
560 | |suffix| { | ||
561 | if text.ends_with(suffix) { | ||
562 | return Some(&text[text.len() - suffix.len()..]); | ||
563 | } | ||
564 | None | ||
565 | }, | ||
566 | ) | ||
567 | } | ||
568 | } | ||
569 | |||
570 | impl ast::FloatNumber { | ||
571 | const SUFFIXES: &'static [&'static str] = &["f32", "f64"]; | ||
572 | pub fn suffix(&self) -> Option<&str> { | ||
573 | let text = self.text(); | ||
574 | ast::FloatNumber::SUFFIXES.iter().find_map(|suffix| { | ||
575 | if text.ends_with(suffix) { | ||
576 | return Some(&text[text.len() - suffix.len()..]); | ||
577 | } | ||
578 | None | ||
579 | }) | ||
580 | } | ||
581 | } | ||
582 | |||
583 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
584 | pub enum Radix { | ||
585 | Binary = 2, | ||
586 | Octal = 8, | ||
587 | Decimal = 10, | ||
588 | Hexadecimal = 16, | ||
589 | } | ||
590 | |||
591 | impl Radix { | ||
592 | pub const ALL: &'static [Radix] = | ||
593 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; | ||
594 | |||
595 | const fn prefix_len(&self) -> usize { | ||
596 | match self { | ||
597 | Self::Decimal => 0, | ||
598 | _ => 2, | ||
535 | } | 599 | } |
536 | Some(res) | ||
537 | } | 600 | } |
538 | } | 601 | } |