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