Skip to main content

logos_derive/
lib.rs

1//! <img src="https://raw.githubusercontent.com/maciejhirsz/logos/master/logos.svg?sanitize=true" alt="Logos logo" width="250" align="right">
2//!
3//! # Logos
4//!
5//! This is a `#[derive]` macro crate, [for documentation go to main crate](https://docs.rs/logos).
6
7// The `quote!` macro requires deep recursion.
8#![recursion_limit = "196"]
9#![doc(html_logo_url = "https://maciej.codes/kosz/logos.png")]
10
11mod error;
12mod generator;
13mod graph;
14mod leaf;
15mod mir;
16mod parser;
17mod util;
18
19use generator::Generator;
20use graph::{DisambiguationError, Fork, Graph, Rope};
21use leaf::Leaf;
22use parser::{Mode, Parser};
23use util::MaybeVoid;
24
25use proc_macro::TokenStream;
26use proc_macro2::Span;
27use quote::quote;
28use syn::spanned::Spanned;
29use syn::{Fields, ItemEnum};
30
31#[proc_macro_derive(Logos, attributes(logos, extras, error, end, token, regex))]
32pub fn logos(input: TokenStream) -> TokenStream {
33    let mut item: ItemEnum = syn::parse(input).expect("Logos can be only be derived for enums");
34
35    let name = &item.ident;
36
37    let mut error = None;
38    let mut parser = Parser::default();
39
40    for param in item.generics.params {
41        parser.parse_generic(param);
42    }
43
44    for attr in &mut item.attrs {
45        parser.try_parse_logos(attr);
46
47        // TODO: Remove in future versions
48        if attr.path.is_ident("extras") {
49            parser.err(
50                "\
51                #[extras] attribute is deprecated. Use #[logos(extras = Type)] instead.\n\
52                \n\
53                For help with migration see release notes: \
54                https://github.com/maciejhirsz/logos/releases\
55                ",
56                attr.span(),
57            );
58        }
59    }
60
61    let mut ropes = Vec::new();
62    let mut regex_ids = Vec::new();
63    let mut graph = Graph::new();
64
65    for variant in &mut item.variants {
66        let field = match &mut variant.fields {
67            Fields::Unit => MaybeVoid::Void,
68            Fields::Unnamed(fields) => {
69                if fields.unnamed.len() != 1 {
70                    parser.err(
71                        format!(
72                            "Logos currently only supports variants with one field, found {}",
73                            fields.unnamed.len(),
74                        ),
75                        fields.span(),
76                    );
77                }
78
79                let ty = &mut fields
80                    .unnamed
81                    .first_mut()
82                    .expect("Already checked len; qed")
83                    .ty;
84                let ty = parser.get_type(ty);
85
86                MaybeVoid::Some(ty)
87            }
88            Fields::Named(fields) => {
89                parser.err("Logos doesn't support named fields yet.", fields.span());
90
91                MaybeVoid::Void
92            }
93        };
94
95        // Lazy leaf constructor to avoid cloning
96        let var_ident = &variant.ident;
97        let leaf = move |span| Leaf::new(var_ident, span).field(field.clone());
98
99        for attr in &mut variant.attrs {
100            let attr_name = match attr.path.get_ident() {
101                Some(ident) => ident.to_string(),
102                None => continue,
103            };
104
105            match attr_name.as_str() {
106                "error" => {
107                    let span = variant.ident.span();
108                    if let Some(previous) = error.replace(&variant.ident) {
109                        parser
110                            .err("Only one #[error] variant can be declared.", span)
111                            .err("Previously declared #[error]:", previous.span());
112                    }
113                }
114                "end" => {
115                    // TODO: Remove in future versions
116                    parser.err(
117                        "\
118                        Since 0.11 Logos no longer requires the #[end] variant.\n\
119                        \n\
120                        For help with migration see release notes: \
121                        https://github.com/maciejhirsz/logos/releases\
122                        ",
123                        attr.span(),
124                    );
125                }
126                "token" => {
127                    let definition = match parser.parse_definition(attr) {
128                        Some(definition) => definition,
129                        None => {
130                            parser.err("Expected #[token(...)]", attr.span());
131                            continue;
132                        }
133                    };
134
135                    if definition.ignore_flags.is_empty() {
136                        let bytes = definition.literal.to_bytes();
137                        let then = graph.push(
138                            leaf(definition.literal.span())
139                                .priority(definition.priority.unwrap_or(bytes.len() * 2))
140                                .callback(definition.callback),
141                        );
142
143                        ropes.push(Rope::new(bytes, then));
144                    } else {
145                        let mir = definition
146                            .literal
147                            .escape_regex()
148                            .to_mir(
149                                &Default::default(),
150                                definition.ignore_flags,
151                                &mut parser.errors,
152                            )
153                            .expect("The literal should be perfectly valid regex");
154
155                        let then = graph.push(
156                            leaf(definition.literal.span())
157                                .priority(definition.priority.unwrap_or_else(|| mir.priority()))
158                                .callback(definition.callback),
159                        );
160                        let id = graph.regex(mir, then);
161
162                        regex_ids.push(id);
163                    }
164                }
165                "regex" => {
166                    let definition = match parser.parse_definition(attr) {
167                        Some(definition) => definition,
168                        None => {
169                            parser.err("Expected #[regex(...)]", attr.span());
170                            continue;
171                        }
172                    };
173                    let mir = match definition.literal.to_mir(
174                        &parser.subpatterns,
175                        definition.ignore_flags,
176                        &mut parser.errors,
177                    ) {
178                        Ok(mir) => mir,
179                        Err(err) => {
180                            parser.err(err, definition.literal.span());
181                            continue;
182                        }
183                    };
184
185                    let then = graph.push(
186                        leaf(definition.literal.span())
187                            .priority(definition.priority.unwrap_or_else(|| mir.priority()))
188                            .callback(definition.callback),
189                    );
190                    let id = graph.regex(mir, then);
191
192                    regex_ids.push(id);
193                }
194                _ => (),
195            }
196        }
197    }
198
199    let mut root = Fork::new();
200
201    let extras = parser.extras.take();
202    let source = match parser.mode {
203        Mode::Utf8 => quote!(str),
204        Mode::Binary => quote!([u8]),
205    };
206
207    let error_def = match error {
208        Some(error) => Some(quote!(const ERROR: Self = #name::#error;)),
209        None => {
210            parser.err("missing #[error] token variant.", Span::call_site());
211            None
212        }
213    };
214
215    let generics = parser.generics();
216    let this = quote!(#name #generics);
217
218    let impl_logos = |body| {
219        quote! {
220            impl<'s> ::logos::Logos<'s> for #this {
221                type Extras = #extras;
222
223                type Source = #source;
224
225                #error_def
226
227                fn lex(lex: &mut ::logos::Lexer<'s, Self>) {
228                    #body
229                }
230            }
231        }
232    };
233
234    for id in regex_ids {
235        let fork = graph.fork_off(id);
236
237        root.merge(fork, &mut graph);
238    }
239    for rope in ropes {
240        root.merge(rope.into_fork(&mut graph), &mut graph);
241    }
242    while let Some(id) = root.miss.take() {
243        let fork = graph.fork_off(id);
244
245        if fork.branches().next().is_some() {
246            root.merge(fork, &mut graph);
247        } else {
248            break;
249        }
250    }
251
252    for &DisambiguationError(a, b) in graph.errors() {
253        let a = graph[a].unwrap_leaf();
254        let b = graph[b].unwrap_leaf();
255        let disambiguate = a.priority + 1;
256
257        let mut err = |a: &Leaf, b: &Leaf| {
258            parser.err(
259                format!(
260                    "\
261                    A definition of variant `{0}` can match the same input as another definition of variant `{1}`.\n\
262                    \n\
263                    hint: Consider giving one definition a higher priority: \
264                    #[regex(..., priority = {2})]\
265                    ",
266                    a.ident,
267                    b.ident,
268                    disambiguate,
269                ),
270                a.span
271            );
272        };
273
274        err(a, b);
275        err(b, a);
276    }
277
278    if let Some(errors) = parser.errors.render() {
279        return impl_logos(errors).into();
280    }
281
282    let root = graph.push(root);
283
284    graph.shake(root);
285
286    // panic!("{:#?}\n\n{} nodes", graph, graph.nodes().iter().filter_map(|n| n.as_ref()).count());
287
288    let generator = Generator::new(name, &this, root, &graph);
289
290    let body = generator.generate();
291    let tokens = impl_logos(quote! {
292        use ::logos::internal::{LexerInternal, CallbackResult};
293
294        type Lexer<'s> = ::logos::Lexer<'s, #this>;
295
296        fn _end<'s>(lex: &mut Lexer<'s>) {
297            lex.end()
298        }
299
300        fn _error<'s>(lex: &mut Lexer<'s>) {
301            lex.bump_unchecked(1);
302
303            lex.error();
304        }
305
306        #body
307    });
308
309    // panic!("{}", tokens);
310
311    TokenStream::from(tokens)
312}