1 use super::errors::{ErrorKind, ParserError}; 2 use super::{Parser, Result, 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.is_current_byte(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 self.ptr >= self.length { 74 break; 75 } 76 let b = self.source.as_ref().as_bytes().get(self.ptr); 77 if indent == 0 { 78 if b != Some(&b'\n') { 79 break; 80 } 81 } else if !Self::is_byte_pattern_continuation(*b.unwrap()) { 82 self.ptr = slice_start; 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::Continuation, 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 ast::PatternElement::TextElement { value } 146 } else { 147 ast::PatternElement::TextElement { value } 148 } 149 } 150 }) 151 .collect(); 152 return Ok(Some(ast::Pattern { elements })); 153 } 154 155 Ok(None) 156 } 157 get_text_slice( &mut self, ) -> Result<(usize, usize, TextElementType, TextElementTermination)>158 fn get_text_slice( 159 &mut self, 160 ) -> Result<(usize, usize, TextElementType, TextElementTermination)> { 161 let start_pos = self.ptr; 162 let mut text_element_type = TextElementType::Blank; 163 164 while let Some(b) = self.source.as_ref().as_bytes().get(self.ptr) { 165 match b { 166 b' ' => self.ptr += 1, 167 b'\n' => { 168 self.ptr += 1; 169 return Ok(( 170 start_pos, 171 self.ptr, 172 text_element_type, 173 TextElementTermination::LineFeed, 174 )); 175 } 176 b'\r' if self.is_byte_at(b'\n', self.ptr + 1) => { 177 self.ptr += 1; 178 return Ok(( 179 start_pos, 180 self.ptr - 1, 181 text_element_type, 182 TextElementTermination::CRLF, 183 )); 184 } 185 b'{' => { 186 return Ok(( 187 start_pos, 188 self.ptr, 189 text_element_type, 190 TextElementTermination::PlaceableStart, 191 )); 192 } 193 b'}' => { 194 return error!(ErrorKind::UnbalancedClosingBrace, self.ptr); 195 } 196 _ => { 197 text_element_type = TextElementType::NonBlank; 198 self.ptr += 1 199 } 200 } 201 } 202 Ok(( 203 start_pos, 204 self.ptr, 205 text_element_type, 206 TextElementTermination::EOF, 207 )) 208 } 209 } 210