1#![allow(clippy::indexing_slicing)]
19use logos::{Logos, Span};
20use smol_str::SmolStr;
21use std::fmt::{self, Display};
22
23#[allow(clippy::unwrap_used)]
25pub(crate) mod regex_constants {
26 use regex::Regex;
27 lazy_static::lazy_static! {
28 pub static ref COMMENT : Regex = Regex::new(r"//[^\n\r]*").unwrap();
29 pub static ref STRING : Regex = Regex::new(r#""(\\.|[^"\\])*""#).unwrap();
30 }
31}
32
33pub fn get_comment(text: &str) -> impl Iterator<Item = &str> + std::fmt::Debug {
34 regex_constants::COMMENT
35 .find_iter(text)
36 .map(|c| c.as_str().trim())
37}
38
39#[derive(Clone, Debug, Default, PartialEq, Eq)]
41pub struct Comment<'src> {
42 leading_comment: Vec<&'src str>,
43 trailing_comment: &'src str,
44}
45
46impl<'src> Comment<'src> {
47 pub fn new(leading_comment: &'src str, trailing_comment: &'src str) -> Self {
48 Self {
49 leading_comment: get_comment(leading_comment).collect(),
50 trailing_comment: trailing_comment.trim(),
54 }
55 }
56
57 pub fn leading_comment(&self) -> &[&'src str] {
58 &self.leading_comment
59 }
60
61 pub fn trailing_comment(&self) -> &'src str {
62 self.trailing_comment
63 }
64
65 fn format_leading_comment(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 for comment_line in itertools::Itertools::intersperse(self.leading_comment.iter(), &"\n") {
67 write!(f, "{comment_line}")?;
68 }
69 if !self.leading_comment.is_empty() {
70 writeln!(f)?;
71 }
72 Ok(())
73 }
74
75 fn format_trailing_comment(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "{}", self.trailing_comment)
77 }
78}
79
80impl Display for Comment<'_> {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 self.format_leading_comment(f)?;
83 self.format_trailing_comment(f)?;
84 Ok(())
85 }
86}
87
88#[derive(Logos, Clone, Debug, PartialEq, Eq)]
90pub enum Token {
91 #[regex(r"\s*", logos::skip)]
92 Whitespace,
93
94 #[regex(r"//[^\n\r]*[\n\r]*", logos::skip)]
95 Comment,
96
97 #[token("true")]
98 True,
99
100 #[token("false")]
101 False,
102
103 #[token("if")]
104 If,
105
106 #[token("permit")]
107 Permit,
108
109 #[token("forbid")]
110 Forbid,
111
112 #[token("when")]
113 When,
114
115 #[token("unless")]
116 Unless,
117
118 #[token("in")]
119 In,
120
121 #[token("has")]
122 Has,
123
124 #[token("like")]
125 Like,
126
127 #[token("is")]
128 Is,
129
130 #[token("then")]
131 Then,
132
133 #[token("else")]
134 Else,
135
136 #[token("principal")]
137 Principal,
138
139 #[token("action")]
140 Action,
141
142 #[token("resource")]
143 Resource,
144
145 #[token("context")]
146 Context,
147
148 #[token("?principal")]
149 PrincipalSlot,
150
151 #[token("?resource")]
152 ResourceSlot,
153
154 #[regex(r"[_a-zA-Z][_a-zA-Z0-9]*", |lex| SmolStr::new(lex.slice()))]
155 Identifier(SmolStr),
156
157 #[regex("[0-9]+", |lex| SmolStr::new(lex.slice()))]
158 Number(SmolStr),
159
160 #[regex(r#""(\\.|[^"\\])*""#, |lex| SmolStr::new(lex.slice()))]
161 Str(SmolStr),
162
163 #[token("@")]
164 At,
165
166 #[token(".")]
167 Dot,
168
169 #[token(",")]
170 Comma,
171
172 #[token(";")]
173 SemiColon,
174
175 #[token(":")]
176 Colon,
177
178 #[token("::")]
179 DoubleColon,
180
181 #[token("(")]
182 LParen,
183
184 #[token(")")]
185 RParen,
186
187 #[token("{")]
188 LBrace,
189
190 #[token("}")]
191 RBrace,
192
193 #[token("[")]
194 LBracket,
195
196 #[token("]")]
197 RBracket,
198
199 #[token("==")]
200 Equal,
201
202 #[token("!=")]
203 NotEqual,
204
205 #[token("<")]
206 Lt,
207
208 #[token("<=")]
209 Le,
210
211 #[token(">")]
212 Gt,
213
214 #[token(">=")]
215 Ge,
216
217 #[token("||")]
218 Or,
219
220 #[token("&&")]
221 And,
222
223 #[token("+")]
224 Add,
225
226 #[token("-")]
227 Dash,
228
229 #[token("*")]
230 Mul,
231
232 #[token("/")]
233 Div,
234
235 #[token("%")]
236 Modulo,
237
238 #[token("!")]
239 Neg,
240}
241
242impl fmt::Display for Token {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 match self {
245 Self::Action => write!(f, "action"),
246 Self::Add => write!(f, "+"),
247 Self::And => write!(f, "&&"),
248 Self::At => write!(f, "@"),
249 Self::Colon => write!(f, ":"),
250 Self::Comma => write!(f, ","),
251 #[allow(clippy::unreachable)]
253 Self::Comment => unreachable!("comment should be skipped!"),
254 Self::Context => write!(f, "context"),
255 Self::Dash => write!(f, "-"),
256 Self::Div => write!(f, "/"),
257 Self::Dot => write!(f, "."),
258 Self::DoubleColon => write!(f, "::"),
259 Self::Else => write!(f, "else"),
260 Self::Equal => write!(f, "=="),
261 Self::False => write!(f, "false"),
262 Self::Forbid => write!(f, "forbid"),
263 Self::Ge => write!(f, ">="),
264 Self::Gt => write!(f, ">"),
265 Self::Has => write!(f, "has"),
266 Self::Identifier(i) => write!(f, "{}", i),
267 Self::If => write!(f, "if"),
268 Self::In => write!(f, "in"),
269 Self::LBrace => write!(f, "{{"),
270 Self::LBracket => write!(f, "["),
271 Self::LParen => write!(f, "("),
272 Self::Le => write!(f, "<="),
273 Self::Like => write!(f, "like"),
274 Self::Is => write!(f, "is"),
275 Self::Lt => write!(f, "<"),
276 Self::Modulo => write!(f, "%"),
277 Self::Mul => write!(f, "*"),
278 Self::Neg => write!(f, "!"),
279 Self::NotEqual => write!(f, "!="),
280 Self::Number(n) => write!(f, "{}", n),
281 Self::Or => write!(f, "||"),
282 Self::Permit => write!(f, "permit"),
283 Self::Principal => write!(f, "principal"),
284 Self::PrincipalSlot => write!(f, "principal?"),
285 Self::RBrace => write!(f, "}}"),
286 Self::RBracket => write!(f, "]"),
287 Self::RParen => write!(f, ")"),
288 Self::Resource => write!(f, "resource"),
289 Self::ResourceSlot => write!(f, "resource?"),
290 Self::SemiColon => write!(f, ";"),
291 Self::Str(s) => write!(f, "{}", s),
292 Self::Then => write!(f, "then"),
293 Self::True => write!(f, "true"),
294 Self::Unless => write!(f, "unless"),
295 Self::When => write!(f, "when"),
296 #[allow(clippy::unreachable)]
298 Self::Whitespace => unreachable!("whitespace should be skipped!"),
299 }
300 }
301}
302
303#[derive(Debug, Clone, PartialEq, Eq)]
306pub struct WrappedToken<'src> {
307 pub token: Token,
308 pub comment: Comment<'src>,
309 pub span: Span,
310}
311
312impl<'src> WrappedToken<'src> {
313 pub fn new(token: Token, span: Span, comment: Comment<'src>) -> Self {
314 Self {
315 token,
316 comment,
317 span,
318 }
319 }
320
321 fn clear_leading_comment(&mut self) {
322 self.comment.leading_comment.clear();
323 }
324
325 fn clear_trailing_comment(&mut self) {
326 self.comment.trailing_comment = "";
327 }
328
329 pub fn consume_leading_comment(&mut self) -> Vec<&'src str> {
330 let comment = self.comment.leading_comment.clone();
331 self.clear_leading_comment();
332 comment
333 }
334
335 pub fn consume_comment(&mut self) -> Comment<'src> {
336 let comment = self.comment.clone();
337 self.clear_leading_comment();
338 self.clear_trailing_comment();
339 comment
340 }
341}
342
343impl Display for WrappedToken<'_> {
344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
345 self.comment.format_leading_comment(f)?;
346 write!(f, "{} ", self.token)?;
347 self.comment.format_trailing_comment(f)?;
348 Ok(())
349 }
350}