1 use super::errors::{ErrorKind, ParserError}; 2 use super::{core::Parser, core::Result, slice::Slice}; 3 use crate::ast; 4 5 #[derive(Debug, PartialEq)] 6 enum TextElementTermination { 7 LineFeed, 8 CRLF, 9 PlaceableStart, 10 EOF, 11 } 12 13 // This enum tracks the placement of the text element in the pattern, which is needed for 14 // dedentation logic. 15 #[derive(Debug, PartialEq)] 16 enum TextElementPosition { 17 InitialLineStart, 18 LineStart, 19 Continuation, 20 } 21 22 // This enum allows us to mark pointers in the source which will later become text elements 23 // but without slicing them out of the source string. This makes the indentation adjustments 24 // cheaper since they'll happen on the pointers, rather than extracted slices. 25 #[derive(Debug)] 26 enum PatternElementPlaceholders<S> { 27 Placeable(ast::Expression<S>), 28 // (start, end, indent, position) 29 TextElement(usize, usize, usize, TextElementPosition), 30 } 31 32 // This enum tracks whether the text element is blank or not. 33 // This is important to identify text elements which should not be taken into account 34 // when calculating common indent. 35 #[derive(Debug, PartialEq)] 36 enum TextElementType { 37 Blank, 38 NonBlank, 39 } 40 41 impl<'s, S> Parser<S> 42 where 43 S: Slice<'s>, 44 { get_pattern(&mut self) -> Result<Option<ast::Pattern<S>>>45 pub(super) fn get_pattern(&mut self) -> Result<Option<ast::Pattern<S>>> { 46 let mut elements = vec![]; 47 let mut last_non_blank = None; 48 let mut common_indent = None; 49 50 self.skip_blank_inline(); 51 52 let mut text_element_role = if self.skip_eol() { 53 self.skip_blank_block(); 54 TextElementPosition::LineStart 55 } else { 56 TextElementPosition::InitialLineStart 57 }; 58 59 while self.ptr < self.length { 60 if self.take_byte_if(b'{') { 61 if text_element_role == TextElementPosition::LineStart { 62 common_indent = Some(0); 63 } 64 let exp = self.get_placeable()?; 65 last_non_blank = Some(elements.len()); 66 elements.push(PatternElementPlaceholders::Placeable(exp)); 67 text_element_role = TextElementPosition::Continuation; 68 } else { 69 let slice_start = self.ptr; 70 let mut indent = 0; 71 if text_element_role == TextElementPosition::LineStart { 72 indent = self.skip_blank_inline(); 73 if let Some(b) = get_current_byte!(self) { 74 if indent == 0 { 75 if b != &b'\r' && b != &b'\n' { 76 break; 77 } 78 } else if !Self::is_byte_pattern_continuation(*b) { 79 self.ptr = slice_start; 80 break; 81 } 82 } else { 83 break; 84 } 85 } 86 let (start, end, text_element_type, termination_reason) = self.get_text_slice()?; 87 if start != end { 88 if text_element_role == TextElementPosition::LineStart 89 && text_element_type == TextElementType::NonBlank 90 { 91 if let Some(common) = common_indent { 92 if indent < common { 93 common_indent = Some(indent); 94 } 95 } else { 96 common_indent = Some(indent); 97 } 98 } 99 if text_element_role != TextElementPosition::LineStart 100 || text_element_type == TextElementType::NonBlank 101 || termination_reason == TextElementTermination::LineFeed 102 { 103 if text_element_type == TextElementType::NonBlank { 104 last_non_blank = Some(elements.len()); 105 } 106 elements.push(PatternElementPlaceholders::TextElement( 107 slice_start, 108 end, 109 indent, 110 text_element_role, 111 )); 112 } 113 } 114 115 text_element_role = match termination_reason { 116 TextElementTermination::LineFeed => TextElementPosition::LineStart, 117 TextElementTermination::CRLF => TextElementPosition::LineStart, 118 TextElementTermination::PlaceableStart => TextElementPosition::Continuation, 119 TextElementTermination::EOF => TextElementPosition::Continuation, 120 }; 121 } 122 } 123 124 if let Some(last_non_blank) = last_non_blank { 125 let elements = elements 126 .into_iter() 127 .take(last_non_blank + 1) 128 .enumerate() 129 .map(|(i, elem)| match elem { 130 PatternElementPlaceholders::Placeable(expression) => { 131 ast::PatternElement::Placeable { expression } 132 } 133 PatternElementPlaceholders::TextElement(start, end, indent, role) => { 134 let start = if role == TextElementPosition::LineStart { 135 common_indent.map_or_else( 136 || start + indent, 137 |common_indent| start + std::cmp::min(indent, common_indent), 138 ) 139 } else { 140 start 141 }; 142 let mut value = self.source.slice(start..end); 143 if last_non_blank == i { 144 value.trim(); 145 } 146 ast::PatternElement::TextElement { value } 147 } 148 }) 149 .collect(); 150 return Ok(Some(ast::Pattern { elements })); 151 } 152 153 Ok(None) 154 } 155 get_text_slice( &mut self, ) -> Result<(usize, usize, TextElementType, TextElementTermination)>156 fn get_text_slice( 157 &mut self, 158 ) -> Result<(usize, usize, TextElementType, TextElementTermination)> { 159 let start_pos = self.ptr; 160 let mut text_element_type = TextElementType::Blank; 161 162 while let Some(b) = get_current_byte!(self) { 163 match b { 164 b' ' => self.ptr += 1, 165 b'\n' => { 166 self.ptr += 1; 167 return Ok(( 168 start_pos, 169 self.ptr, 170 text_element_type, 171 TextElementTermination::LineFeed, 172 )); 173 } 174 b'\r' if self.is_byte_at(b'\n', self.ptr + 1) => { 175 self.ptr += 1; 176 return Ok(( 177 start_pos, 178 self.ptr - 1, 179 text_element_type, 180 TextElementTermination::CRLF, 181 )); 182 } 183 b'{' => { 184 return Ok(( 185 start_pos, 186 self.ptr, 187 text_element_type, 188 TextElementTermination::PlaceableStart, 189 )); 190 } 191 b'}' => { 192 return error!(ErrorKind::UnbalancedClosingBrace, self.ptr); 193 } 194 _ => { 195 text_element_type = TextElementType::NonBlank; 196 self.ptr += 1 197 } 198 } 199 } 200 Ok(( 201 start_pos, 202 self.ptr, 203 text_element_type, 204 TextElementTermination::EOF, 205 )) 206 } 207 } 208