1 use proc_macro2::TokenStream;
2 use quote::quote;
3 
4 use crate::generator::Generator;
5 use crate::graph::NodeId;
6 
7 /// This struct keeps track of bytes available to be read without
8 /// bounds checking across the tree.
9 ///
10 /// For example, a branch that matches 4 bytes followed by a fork
11 /// with smallest branch containing of 2 bytes can do a bounds check
12 /// for 6 bytes ahead, and leave the remaining 2 byte array (fixed size)
13 /// to be handled by the fork, avoiding bound checks there.
14 #[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Debug)]
15 pub struct Context {
16     /// Amount of bytes that haven't been bumped yet but should
17     /// before a new read is performed
18     at: usize,
19     /// Number of bytes available without bound checks
20     available: usize,
21     /// Whether or not the Lexer has been bumped at least by 1 byte
22     bumped: bool,
23     /// Node to backtrack to to in case an explicit match has failed.
24     /// If `None` will instead produce an error token.
25     backtrack: Option<NodeId>,
26 }
27 
28 impl Context {
can_backtrack(&self) -> bool29     pub fn can_backtrack(&self) -> bool {
30         self.backtrack.is_some()
31     }
32 
switch(&mut self, miss: Option<NodeId>) -> Option<TokenStream>33     pub fn switch(&mut self, miss: Option<NodeId>) -> Option<TokenStream> {
34         self.backtrack = Some(miss?);
35         self.bump()
36     }
37 
advance(self, n: usize) -> Self38     pub const fn advance(self, n: usize) -> Self {
39         Context {
40             at: self.at + n,
41             ..self
42         }
43     }
44 
bump(&mut self) -> Option<TokenStream>45     pub fn bump(&mut self) -> Option<TokenStream> {
46         match self.at {
47             0 => None,
48             n => {
49                 let tokens = quote!(lex.bump_unchecked(#n););
50                 self.at = 0;
51                 self.available = 0;
52                 self.bumped = true;
53                 Some(tokens)
54             }
55         }
56     }
57 
remainder(&self) -> usize58     pub fn remainder(&self) -> usize {
59         self.available.saturating_sub(self.at)
60     }
61 
read_unchecked(&mut self, len: usize) -> TokenStream62     pub fn read_unchecked(&mut self, len: usize) -> TokenStream {
63         let at = self.at;
64 
65         match len {
66             0 => {
67                 self.advance(1);
68 
69                 quote!(lex.read_unchecked::<u8>(#at))
70             }
71             l => {
72                 self.advance(l);
73 
74                 quote!(lex.read_unchecked::<&[u8; #l]>(#at))
75             }
76         }
77     }
78 
read(&mut self, len: usize) -> TokenStream79     pub fn read(&mut self, len: usize) -> TokenStream {
80         self.available = len;
81 
82         match (self.at, len) {
83             (0, 0) => quote!(lex.read::<u8>()),
84             (a, 0) => quote!(lex.read_at::<u8>(#a)),
85             (0, l) => quote!(lex.read::<&[u8; #l]>()),
86             (a, l) => quote!(lex.read_at::<&[u8; #l]>(#a)),
87         }
88     }
89 
wipe(&mut self)90     pub fn wipe(&mut self) {
91         self.available = 0;
92     }
93 
backtrack(self) -> Self94     const fn backtrack(self) -> Self {
95         Context {
96             at: 0,
97             available: 0,
98             bumped: self.bumped,
99             backtrack: None,
100         }
101     }
102 
miss(mut self, miss: Option<NodeId>, gen: &mut Generator) -> TokenStream103     pub fn miss(mut self, miss: Option<NodeId>, gen: &mut Generator) -> TokenStream {
104         self.wipe();
105         match (miss, self.backtrack) {
106             (Some(id), _) => gen.goto(id, self).clone(),
107             (_, Some(id)) => gen.goto(id, self.backtrack()).clone(),
108             _ if self.bumped => quote!(lex.error()),
109             _ => quote!(_error(lex)),
110         }
111     }
112 
write_suffix(&self, buf: &mut String)113     pub fn write_suffix(&self, buf: &mut String) {
114         use std::fmt::Write;
115 
116         if self.at > 0 {
117             let _ = write!(buf, "_at{}", self.at);
118         }
119         if self.available > 0 {
120             let _ = write!(buf, "_with{}", self.available);
121         }
122         if let Some(id) = self.backtrack {
123             let _ = write!(buf, "_ctx{}", id);
124         }
125         if self.bumped {
126             buf.push_str("_x");
127         }
128     }
129 }
130