arity_parser/ast/
tokens.rs1use rowan::TextRange;
11use smol_str::SmolStr;
12
13use crate::syntax::{SyntaxKind, SyntaxToken};
14
15pub trait AstToken {
18 fn can_cast(kind: SyntaxKind) -> bool
19 where
20 Self: Sized;
21
22 fn cast(syntax: SyntaxToken) -> Option<Self>
23 where
24 Self: Sized;
25
26 fn syntax(&self) -> &SyntaxToken;
27
28 fn text(&self) -> &str {
29 self.syntax().text()
30 }
31
32 fn text_range(&self) -> TextRange {
33 self.syntax().text_range()
34 }
35}
36
37macro_rules! ast_token {
38 ($name:ident, $kind:expr) => {
39 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
40 pub struct $name(SyntaxToken);
41
42 impl AstToken for $name {
43 fn can_cast(kind: SyntaxKind) -> bool {
44 kind == $kind
45 }
46
47 fn cast(syntax: SyntaxToken) -> Option<Self> {
48 Self::can_cast(syntax.kind()).then(|| Self(syntax))
49 }
50
51 fn syntax(&self) -> &SyntaxToken {
52 &self.0
53 }
54 }
55 };
56}
57
58ast_token!(Ident, SyntaxKind::IDENT);
59ast_token!(IntLit, SyntaxKind::INT);
60ast_token!(FloatLit, SyntaxKind::FLOAT);
61ast_token!(ComplexLit, SyntaxKind::COMPLEX);
62ast_token!(StringLit, SyntaxKind::STRING);
63ast_token!(Comment, SyntaxKind::COMMENT);
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub enum RConstant {
69 True,
70 False,
71 Na,
72 NaInteger,
73 NaReal,
74 NaComplex,
75 NaCharacter,
76 Null,
77 NaN,
78 Inf,
79 TSymbol,
81 FSymbol,
83}
84
85impl Ident {
86 pub fn name(&self) -> &str {
88 self.text()
89 }
90
91 pub fn constant(&self) -> Option<RConstant> {
96 Some(match self.text() {
97 "TRUE" => RConstant::True,
98 "FALSE" => RConstant::False,
99 "NA" => RConstant::Na,
100 "NA_integer_" => RConstant::NaInteger,
101 "NA_real_" => RConstant::NaReal,
102 "NA_complex_" => RConstant::NaComplex,
103 "NA_character_" => RConstant::NaCharacter,
104 "NULL" => RConstant::Null,
105 "NaN" => RConstant::NaN,
106 "Inf" => RConstant::Inf,
107 "T" => RConstant::TSymbol,
108 "F" => RConstant::FSymbol,
109 _ => return None,
110 })
111 }
112
113 pub fn is_true(&self) -> bool {
115 self.text() == "TRUE"
116 }
117
118 pub fn is_false(&self) -> bool {
120 self.text() == "FALSE"
121 }
122
123 pub fn is_na(&self) -> bool {
125 matches!(
126 self.text(),
127 "NA" | "NA_integer_" | "NA_real_" | "NA_complex_" | "NA_character_"
128 )
129 }
130
131 pub fn is_null(&self) -> bool {
133 self.text() == "NULL"
134 }
135
136 pub fn is_nan(&self) -> bool {
138 self.text() == "NaN"
139 }
140
141 pub fn is_bool_symbol(&self) -> bool {
143 matches!(self.text(), "T" | "F")
144 }
145
146 pub fn is_implicit_method_var(&self) -> bool {
151 matches!(self.text(), ".Generic" | ".Method" | ".Class")
152 }
153
154 pub fn is_dots(&self) -> bool {
157 let name = self.text();
158 name.starts_with('.') && name.chars().all(|c| c == '.' || c.is_ascii_digit())
159 }
160
161 pub fn is_reserved_constant(&self) -> bool {
166 matches!(
167 self.constant(),
168 Some(
169 RConstant::Na
170 | RConstant::NaInteger
171 | RConstant::NaReal
172 | RConstant::NaComplex
173 | RConstant::NaCharacter
174 | RConstant::Null
175 | RConstant::True
176 | RConstant::False
177 | RConstant::Inf
178 | RConstant::NaN
179 )
180 )
181 }
182}
183
184impl StringLit {
185 pub fn quote(&self) -> Option<char> {
188 let bytes = self.text().as_bytes();
189 if bytes.len() < 2 {
190 return None;
191 }
192 let q = bytes[0];
193 (matches!(q, b'"' | b'\'') && bytes[bytes.len() - 1] == q).then_some(q as char)
194 }
195
196 pub fn inner(&self) -> Option<&str> {
200 self.quote()?;
201 let text = self.text();
202 Some(&text[1..text.len() - 1])
203 }
204
205 pub fn is_backtick(&self) -> bool {
207 let bytes = self.text().as_bytes();
208 bytes.len() >= 2 && bytes[0] == b'`' && bytes[bytes.len() - 1] == b'`'
209 }
210
211 pub fn unquote(&self) -> Option<&str> {
214 let text = self.text();
215 let bytes = text.as_bytes();
216 if bytes.len() >= 2 {
217 let (first, last) = (bytes[0], bytes[bytes.len() - 1]);
218 if matches!(first, b'"' | b'\'' | b'`') && first == last {
219 return Some(&text[1..text.len() - 1]);
220 }
221 }
222 None
223 }
224}
225
226pub fn token_name(token: &SyntaxToken) -> SmolStr {
229 if token.kind() == SyntaxKind::STRING {
230 let text = token.text();
231 let bytes = text.as_bytes();
232 if bytes.len() >= 2 {
233 let (first, last) = (bytes[0], bytes[bytes.len() - 1]);
234 if matches!(first, b'"' | b'\'' | b'`') && first == last {
235 return SmolStr::new(&text[1..text.len() - 1]);
236 }
237 }
238 }
239 SmolStr::new(token.text())
240}