1use std::borrow::Cow;
2use std::fmt::Debug;
3use std::ops::Range;
4use std::str::Chars;
5
6use std::collections::HashMap;
7
8use smol_str::SmolStr;
9
10use super::markers::PositionMarker;
11use super::segments::{BlockType, ErasedSegment, SegmentBuilder, Tables, TemplateInfo};
12use crate::dialects::Dialect;
13use crate::dialects::syntax::SyntaxKind;
14use crate::errors::SQLLexError;
15use crate::slice_helpers::{is_zero_slice, offset_slice};
16use crate::templaters::{TemplateSliceKind, TemplatedFile, TemplatedFileSlice};
17
18#[derive(Debug, Clone)]
20pub struct Element<'a> {
21 name: &'static str,
22 text: Cow<'a, str>,
23 syntax_kind: SyntaxKind,
24}
25
26impl<'a> Element<'a> {
27 fn new(name: &'static str, syntax_kind: SyntaxKind, text: impl Into<Cow<'a, str>>) -> Self {
28 Self {
29 name,
30 syntax_kind,
31 text: text.into(),
32 }
33 }
34}
35
36#[derive(Debug)]
38pub struct TemplateElement<'a> {
39 raw: Cow<'a, str>,
40 template_slice: Range<usize>,
41 matcher: Info,
42}
43
44#[derive(Debug)]
45struct Info {
46 name: &'static str,
47 syntax_kind: SyntaxKind,
48}
49
50impl<'a> TemplateElement<'a> {
51 pub fn from_element(element: Element<'a>, template_slice: Range<usize>) -> Self {
53 TemplateElement {
54 raw: element.text,
55 template_slice,
56 matcher: Info {
57 name: element.name,
58 syntax_kind: element.syntax_kind,
59 },
60 }
61 }
62
63 pub fn to_segment(
64 &self,
65 pos_marker: PositionMarker,
66 subslice: Option<Range<usize>>,
67 ) -> ErasedSegment {
68 let slice = subslice.map_or_else(|| self.raw.as_ref(), |slice| &self.raw[slice]);
69 SegmentBuilder::token(0, slice, self.matcher.syntax_kind)
70 .with_position(pos_marker)
71 .finish()
72 }
73}
74
75#[derive(Debug)]
77pub struct Match<'a> {
78 pub forward_string: &'a str,
79 pub elements: Vec<Element<'a>>,
80}
81
82#[derive(Debug, Clone)]
83pub struct Matcher {
84 pattern: Pattern,
85 subdivider: Option<Pattern>,
86 trim_post_subdivide: Option<Pattern>,
87}
88
89impl Matcher {
90 pub const fn new(pattern: Pattern) -> Self {
91 Self {
92 pattern,
93 subdivider: None,
94 trim_post_subdivide: None,
95 }
96 }
97
98 pub const fn string(
99 name: &'static str,
100 pattern: &'static str,
101 syntax_kind: SyntaxKind,
102 ) -> Self {
103 Self::new(Pattern::string(name, pattern, syntax_kind))
104 }
105
106 #[track_caller]
107 pub fn regex(name: &'static str, pattern: &'static str, syntax_kind: SyntaxKind) -> Self {
108 Self::new(Pattern::regex(name, pattern, syntax_kind))
109 }
110
111 pub fn native(name: &'static str, f: fn(&mut Cursor) -> bool, syntax_kind: SyntaxKind) -> Self {
112 Self::new(Pattern::native(name, f, syntax_kind))
113 }
114
115 #[track_caller]
116 pub fn legacy(
117 name: &'static str,
118 starts_with: fn(&str) -> bool,
119 pattern: &'static str,
120 syntax_kind: SyntaxKind,
121 ) -> Self {
122 Self::new(Pattern::legacy(name, starts_with, pattern, syntax_kind))
123 }
124
125 pub fn subdivider(mut self, subdivider: Pattern) -> Self {
126 assert!(matches!(
127 self.pattern.kind,
128 SearchPatternKind::Legacy(_, _) | SearchPatternKind::Native(_)
129 ));
130 self.subdivider = Some(subdivider);
131 self
132 }
133
134 pub fn post_subdivide(mut self, trim_post_subdivide: Pattern) -> Self {
135 assert!(matches!(
136 self.pattern.kind,
137 SearchPatternKind::Legacy(_, _) | SearchPatternKind::Native(_)
138 ));
139 self.trim_post_subdivide = Some(trim_post_subdivide);
140 self
141 }
142
143 pub fn name(&self) -> &'static str {
144 self.pattern.name
145 }
146
147 #[track_caller]
148 pub fn matches<'a>(&self, forward_string: &'a str) -> Match<'a> {
149 match self.pattern.matches(forward_string) {
150 Some(matched) => {
151 let new_elements = self.subdivide(matched, self.pattern.syntax_kind);
152
153 Match {
154 forward_string: &forward_string[matched.len()..],
155 elements: new_elements,
156 }
157 }
158 None => Match {
159 forward_string,
160 elements: Vec::new(),
161 },
162 }
163 }
164
165 fn subdivide<'a>(&self, matched: &'a str, matched_kind: SyntaxKind) -> Vec<Element<'a>> {
166 match &self.subdivider {
167 Some(subdivider) => {
168 let mut elem_buff = Vec::new();
169 let mut str_buff = matched;
170
171 while !str_buff.is_empty() {
172 let Some(div_pos) = subdivider.search(str_buff) else {
173 let mut trimmed_elems = self.trim_match(str_buff);
174 elem_buff.append(&mut trimmed_elems);
175 break;
176 };
177
178 let mut trimmed_elems = self.trim_match(&str_buff[..div_pos.start]);
179 let div_elem = Element::new(
180 subdivider.name,
181 subdivider.syntax_kind,
182 &str_buff[div_pos.start..div_pos.end],
183 );
184
185 elem_buff.append(&mut trimmed_elems);
186 elem_buff.push(div_elem);
187
188 str_buff = &str_buff[div_pos.end..];
189 }
190
191 elem_buff
192 }
193 None => {
194 vec![Element::new(self.name(), matched_kind, matched)]
195 }
196 }
197 }
198
199 fn trim_match<'a>(&self, matched_str: &'a str) -> Vec<Element<'a>> {
200 let Some(trim_post_subdivide) = &self.trim_post_subdivide else {
201 return Vec::new();
202 };
203
204 let mk_element = |text| {
205 Element::new(
206 trim_post_subdivide.name,
207 trim_post_subdivide.syntax_kind,
208 text,
209 )
210 };
211
212 let mut elem_buff = Vec::new();
213 let mut content_buff = String::new();
214 let mut str_buff = matched_str;
215
216 while !str_buff.is_empty() {
217 let Some(trim_pos) = trim_post_subdivide.search(str_buff) else {
218 break;
219 };
220
221 let start = trim_pos.start;
222 let end = trim_pos.end;
223
224 if start == 0 {
225 elem_buff.push(mk_element(&str_buff[..end]));
226 str_buff = str_buff[end..].into();
227 } else if end == str_buff.len() {
228 let raw = format!("{}{}", content_buff, &str_buff[..start]);
229
230 elem_buff.push(Element::new(
231 trim_post_subdivide.name,
232 trim_post_subdivide.syntax_kind,
233 raw,
234 ));
235 elem_buff.push(mk_element(&str_buff[start..end]));
236
237 content_buff.clear();
238 str_buff = "";
239 } else {
240 content_buff.push_str(&str_buff[..end]);
241 str_buff = &str_buff[end..];
242 }
243 }
244
245 if !content_buff.is_empty() || !str_buff.is_empty() {
246 let raw = format!("{content_buff}{str_buff}");
247 elem_buff.push(Element::new(
248 self.pattern.name,
249 self.pattern.syntax_kind,
250 raw,
251 ));
252 }
253
254 elem_buff
255 }
256}
257
258#[derive(Debug, Clone)]
259pub struct Pattern {
260 name: &'static str,
261 syntax_kind: SyntaxKind,
262 kind: SearchPatternKind,
263}
264
265#[derive(Debug, Clone)]
266pub enum SearchPatternKind {
267 String(&'static str),
268 Regex(&'static str),
269 Native(fn(&mut Cursor) -> bool),
270 Legacy(fn(&str) -> bool, fancy_regex::Regex),
271}
272
273impl Pattern {
274 pub const fn string(
275 name: &'static str,
276 template: &'static str,
277 syntax_kind: SyntaxKind,
278 ) -> Self {
279 Self {
280 name,
281 syntax_kind,
282 kind: SearchPatternKind::String(template),
283 }
284 }
285
286 #[track_caller]
287 pub fn regex(name: &'static str, regex: &'static str, syntax_kind: SyntaxKind) -> Self {
288 #[cfg(debug_assertions)]
289 if regex_automata::dfa::regex::Regex::new(regex).is_err() {
290 panic!("Invalid regex pattern: {}", std::panic::Location::caller());
291 }
292
293 Self {
294 name,
295 syntax_kind,
296 kind: SearchPatternKind::Regex(regex),
297 }
298 }
299
300 pub fn native(name: &'static str, f: fn(&mut Cursor) -> bool, syntax_kind: SyntaxKind) -> Self {
301 Self {
302 name,
303 syntax_kind,
304 kind: SearchPatternKind::Native(f),
305 }
306 }
307
308 #[track_caller]
309 pub fn legacy(
310 name: &'static str,
311 starts_with: fn(&str) -> bool,
312 regex: &'static str,
313 syntax_kind: SyntaxKind,
314 ) -> Self {
315 let regex = format!("^{regex}");
316 Self {
317 name,
318 syntax_kind,
319 kind: SearchPatternKind::Legacy(starts_with, fancy_regex::Regex::new(®ex).unwrap()),
320 }
321 }
322
323 fn matches<'a>(&self, forward_string: &'a str) -> Option<&'a str> {
324 match self.kind {
325 SearchPatternKind::String(template) => {
326 if forward_string.starts_with(template) {
327 return Some(template);
328 }
329 }
330 SearchPatternKind::Legacy(f, ref template) => {
331 if !f(forward_string) {
332 return None;
333 }
334
335 if let Ok(Some(matched)) = template.find(forward_string)
336 && matched.start() == 0
337 {
338 return Some(matched.as_str());
339 }
340 }
341 SearchPatternKind::Native(f) => {
342 let mut cursor = Cursor::new(forward_string);
343 return f(&mut cursor).then(|| cursor.lexed());
344 }
345 _ => unreachable!(),
346 };
347
348 None
349 }
350
351 fn search(&self, forward_string: &str) -> Option<Range<usize>> {
352 match &self.kind {
353 SearchPatternKind::String(template) => forward_string
354 .find(template)
355 .map(|start| start..start + template.len()),
356 SearchPatternKind::Legacy(_, template) => {
357 if let Ok(Some(matched)) = template.find(forward_string) {
358 return Some(matched.range());
359 }
360 None
361 }
362 _ => unreachable!("{:?}", self.kind),
363 }
364 }
365}
366
367pub struct Cursor<'text> {
368 text: &'text str,
369 chars: Chars<'text>,
370}
371
372impl<'text> Cursor<'text> {
373 const EOF: char = '\0';
374
375 fn new(text: &'text str) -> Self {
376 Self {
377 text,
378 chars: text.chars(),
379 }
380 }
381
382 pub fn peek(&self) -> char {
383 self.chars.clone().next().unwrap_or(Self::EOF)
384 }
385
386 pub fn peek_next(&self) -> char {
387 self.chars.clone().nth(1).unwrap_or(Self::EOF)
388 }
389
390 pub fn shift(&mut self) -> char {
391 self.chars.next().unwrap_or(Self::EOF)
392 }
393
394 pub fn shift_while(&mut self, f: impl Fn(char) -> bool + Copy) {
395 while self.peek() != Self::EOF && f(self.peek()) {
396 self.shift();
397 }
398 }
399
400 fn lexed(&self) -> &'text str {
401 let len = self.text.len() - self.chars.as_str().len();
402 &self.text[..len]
403 }
404}
405
406pub fn nested_block_comment(cursor: &mut Cursor) -> bool {
407 if cursor.peek() != '/' || cursor.peek_next() != '*' {
408 return false;
409 }
410 cursor.shift();
411 cursor.shift();
412 let mut depth = 1;
413 loop {
414 let ch = cursor.peek();
415 if ch == Cursor::EOF {
416 return false;
417 }
418 if ch == '/' && cursor.peek_next() == '*' {
419 cursor.shift();
420 cursor.shift();
421 depth += 1;
422 continue;
423 }
424 if ch == '*' && cursor.peek_next() == '/' {
425 cursor.shift();
426 cursor.shift();
427 depth -= 1;
428 if depth == 0 {
429 return true;
430 }
431 continue;
432 }
433 cursor.shift();
434 }
435}
436
437#[derive(Debug, Clone)]
439pub struct Lexer {
440 syntax_map: Vec<(&'static str, SyntaxKind)>,
441 regex: regex_automata::meta::Regex,
442 matchers: Vec<Matcher>,
443 last_resort_lexer: Matcher,
444}
445
446impl<'a> From<&'a Dialect> for Lexer {
447 fn from(dialect: &'a Dialect) -> Self {
448 Lexer::new(dialect.lexer_matchers())
449 }
450}
451
452impl Lexer {
453 pub(crate) fn new(lexer_matchers: &[Matcher]) -> Self {
455 let mut patterns = Vec::new();
456 let mut syntax_map = Vec::new();
457 let mut matchers = Vec::new();
458
459 for matcher in lexer_matchers {
460 match matcher.pattern.kind {
461 SearchPatternKind::String(pattern) | SearchPatternKind::Regex(pattern) => {
462 let pattern = if matches!(matcher.pattern.kind, SearchPatternKind::String(_)) {
463 fancy_regex::escape(pattern)
464 } else {
465 pattern.into()
466 };
467
468 patterns.push(pattern);
469 syntax_map.push((matcher.pattern.name, matcher.pattern.syntax_kind));
470 }
471 SearchPatternKind::Legacy(_, _) | SearchPatternKind::Native(_) => {
472 matchers.push(matcher.clone());
473 }
474 }
475 }
476
477 Lexer {
478 syntax_map,
479 matchers,
480 regex: regex_automata::meta::Regex::new_many(&patterns).unwrap(),
481 last_resort_lexer: Matcher::legacy(
482 "<unlexable>",
483 |_| true,
484 r"[^\t\n.]*",
485 SyntaxKind::Unlexable,
486 ),
487 }
488 }
489
490 pub fn lex(
491 &self,
492 tables: &Tables,
493 template: impl Into<TemplatedFile>,
494 ) -> (Vec<ErasedSegment>, Vec<SQLLexError>) {
495 let template = template.into();
496 let mut str_buff = template.templated_str.as_deref().unwrap();
497
498 let mut element_buffer: Vec<Element> = Vec::new();
500
501 loop {
502 let mut res = self.lex_match(str_buff);
503 element_buffer.append(&mut res.elements);
504
505 if res.forward_string.is_empty() {
506 break;
507 }
508
509 let mut resort_res = self.last_resort_lexer.matches(str_buff);
511 if !resort_res.elements.is_empty() {
512 break;
513 }
514
515 str_buff = resort_res.forward_string;
516 element_buffer.append(&mut resort_res.elements);
517 }
518
519 let templated_buffer = Lexer::map_template_slices(element_buffer, &template);
522 let mut segments = self.elements_to_segments(templated_buffer, &template);
524
525 for seg in &mut segments {
526 seg.get_mut().set_id(tables.next_id())
527 }
528
529 (segments, Vec::new())
530 }
531
532 #[allow(dead_code)]
537 fn violations_from_segments(segments: Vec<ErasedSegment>) -> Vec<SQLLexError> {
538 segments
539 .into_iter()
540 .filter(|s| s.is_type(SyntaxKind::Unlexable))
541 .map(|s| {
542 SQLLexError::new(
543 format!(
544 "Unable to lex characters: {}",
545 s.raw().chars().take(10).collect::<String>()
546 ),
547 s.get_position_marker().unwrap().clone(),
548 )
549 })
550 .collect()
551 }
552
553 fn lex_match<'b>(&self, mut forward_string: &'b str) -> Match<'b> {
555 let mut elem_buff = Vec::new();
556
557 'main: loop {
558 if forward_string.is_empty() {
559 return Match {
560 forward_string,
561 elements: elem_buff,
562 };
563 }
564
565 for matcher in &self.matchers {
566 let mut match_result = matcher.matches(forward_string);
567
568 if !match_result.elements.is_empty() {
569 elem_buff.append(&mut match_result.elements);
570 forward_string = match_result.forward_string;
571 continue 'main;
572 }
573 }
574
575 let input =
576 regex_automata::Input::new(forward_string).anchored(regex_automata::Anchored::Yes);
577
578 if let Some(match_) = self.regex.find(input) {
579 let (name, kind) = self.syntax_map[match_.pattern().as_usize()];
580
581 elem_buff.push(Element::new(
582 name,
583 kind,
584 &forward_string[match_.start()..match_.end()],
585 ));
586 forward_string = &forward_string[match_.end()..];
587
588 continue 'main;
589 }
590
591 return Match {
592 forward_string,
593 elements: elem_buff,
594 };
595 }
596 }
597
598 fn map_template_slices<'b>(
606 elements: Vec<Element<'b>>,
607 template: &TemplatedFile,
608 ) -> Vec<TemplateElement<'b>> {
609 let mut idx = 0;
610 let mut templated_buff: Vec<TemplateElement> = Vec::with_capacity(elements.len());
611
612 for element in elements {
613 let template_slice = offset_slice(idx, element.text.len());
614 idx += element.text.len();
615
616 let templated_string = template.templated();
617 if templated_string[template_slice.clone()] != element.text {
618 panic!(
619 "Template and lexed elements do not match. This should never happen {:?} != \
620 {:?}",
621 element.text, &templated_string[template_slice]
622 );
623 }
624
625 templated_buff.push(TemplateElement::from_element(element, template_slice));
626 }
627
628 templated_buff
629 }
630
631 fn elements_to_segments(
633 &self,
634 elements: Vec<TemplateElement>,
635 templated_file: &TemplatedFile,
636 ) -> Vec<ErasedSegment> {
637 let mut segments = iter_segments(elements, templated_file);
638
639 let position_maker = match segments.last() {
641 Some(segment) => segment.get_position_marker().unwrap().end_point_marker(),
642 None => PositionMarker::from_point(0, 0, templated_file.clone(), None, None),
643 };
644
645 segments.push(
646 SegmentBuilder::token(0, "", SyntaxKind::EndOfFile)
647 .with_position(position_maker)
648 .finish(),
649 );
650
651 segments
652 }
653}
654
655#[derive(Default)]
658struct BlockTracker {
659 stack: Vec<u32>,
660 map: HashMap<(usize, usize), u32>,
661 next: u32,
662}
663
664impl BlockTracker {
665 fn enter(&mut self, src: Range<usize>) {
666 let key = (src.start, src.end);
667 let uuid = match self.map.get(&key) {
668 Some(&u) => u,
669 None => {
670 let id = self.next;
671 self.next += 1;
672 self.map.insert(key, id);
673 id
674 }
675 };
676 self.stack.push(uuid);
677 }
678
679 fn exit(&mut self) {
680 self.stack.pop();
681 }
682
683 fn top(&self) -> Option<u32> {
684 self.stack.last().copied()
685 }
686}
687
688fn block_type_from(kind: TemplateSliceKind) -> BlockType {
689 match kind {
690 TemplateSliceKind::Literal => BlockType::Literal,
691 TemplateSliceKind::Templated => BlockType::Templated,
692 TemplateSliceKind::Comment => BlockType::Comment,
693 TemplateSliceKind::BlockStart => BlockType::BlockStart,
694 TemplateSliceKind::BlockMid => BlockType::BlockMid,
695 TemplateSliceKind::BlockEnd => BlockType::BlockEnd,
696 }
697}
698
699fn make_template_segment(
700 kind: SyntaxKind,
701 block_type: BlockType,
702 block_uuid: Option<u32>,
703 source_str: SmolStr,
704 is_template: bool,
705 position: PositionMarker,
706) -> ErasedSegment {
707 SegmentBuilder::token(0, "", kind)
708 .with_template_info(TemplateInfo {
709 block_type,
710 block_uuid,
711 source_str,
712 is_template,
713 })
714 .with_position(position)
715 .finish()
716}
717
718fn make_placeholder(
719 block_type: BlockType,
720 block_uuid: Option<u32>,
721 source_str: SmolStr,
722 source_slice: Range<usize>,
723 templated_slice: Range<usize>,
724 templated_file: &TemplatedFile,
725) -> ErasedSegment {
726 let position = PositionMarker::new(
727 source_slice,
728 templated_slice,
729 templated_file.clone(),
730 None,
731 None,
732 );
733 make_template_segment(
734 SyntaxKind::Placeholder,
735 block_type,
736 block_uuid,
737 source_str,
738 false,
739 position,
740 )
741}
742
743fn make_template_meta(
745 kind: SyntaxKind,
746 block_uuid: Option<u32>,
747 source_point: usize,
748 templated_point: usize,
749 templated_file: &TemplatedFile,
750) -> ErasedSegment {
751 let position = PositionMarker::from_point(
752 source_point,
753 templated_point,
754 templated_file.clone(),
755 None,
756 None,
757 );
758 make_template_segment(
759 kind,
760 BlockType::Templated,
761 block_uuid,
762 "".into(),
763 true,
764 position,
765 )
766}
767
768fn handle_zero_length_slice(
772 tfs: &TemplatedFileSlice,
773 next_tfs: Option<&TemplatedFileSlice>,
774 block_stack: &mut BlockTracker,
775 templated_file: &TemplatedFile,
776) -> Vec<ErasedSegment> {
777 let mut out = Vec::new();
778 let is_block = matches!(
779 tfs.slice_type,
780 TemplateSliceKind::BlockStart | TemplateSliceKind::BlockMid | TemplateSliceKind::BlockEnd
781 );
782
783 if is_block {
784 if let Some(next) = next_tfs
786 && next.source_slice.start < tfs.source_slice.start
787 {
788 let (sp, tp) = (tfs.source_slice.start, tfs.templated_slice.start);
789 out.push(make_template_meta(
790 SyntaxKind::Dedent,
791 None,
792 sp,
793 tp,
794 templated_file,
795 ));
796 out.push(make_template_segment(
797 SyntaxKind::TemplateLoop,
798 BlockType::Templated,
799 block_stack.top(),
800 "".into(),
801 false,
802 PositionMarker::from_point(sp, tp, templated_file.clone(), None, None),
803 ));
804 out.push(make_template_meta(
805 SyntaxKind::Indent,
806 None,
807 sp,
808 tp,
809 templated_file,
810 ));
811 return out;
812 }
813
814 if tfs.slice_type == TemplateSliceKind::BlockStart {
815 block_stack.enter(tfs.source_slice.clone());
816 } else {
817 out.push(make_template_meta(
819 SyntaxKind::Dedent,
820 block_stack.top(),
821 tfs.source_slice.start,
822 tfs.templated_slice.start,
823 templated_file,
824 ));
825 }
826
827 out.push(make_placeholder(
828 block_type_from(tfs.slice_type),
829 block_stack.top(),
830 templated_file.source_str[tfs.source_slice.clone()].into(),
831 tfs.source_slice.clone(),
832 tfs.templated_slice.clone(),
833 templated_file,
834 ));
835
836 if tfs.slice_type == TemplateSliceKind::BlockEnd {
837 block_stack.exit();
838 } else {
839 out.push(make_template_meta(
841 SyntaxKind::Indent,
842 block_stack.top(),
843 tfs.source_slice.end,
844 tfs.templated_slice.end,
845 templated_file,
846 ));
847 }
848
849 if let Some(next) = next_tfs
851 && next.source_slice.start > tfs.source_slice.end
852 {
853 let gap = tfs.source_slice.end..next.source_slice.start;
854 let mut src = templated_file.source_str[gap.clone()].to_string();
855 if src.len() >= 20 {
856 src = format!("... [{} unused template characters] ...", src.len());
857 }
858 out.push(make_placeholder(
859 BlockType::SkippedSource,
860 None,
861 src.into(),
862 gap,
863 tfs.templated_slice.clone(),
864 templated_file,
865 ));
866 }
867
868 return out;
869 }
870
871 out.push(make_placeholder(
872 block_type_from(tfs.slice_type),
873 None,
874 templated_file.source_str[tfs.source_slice.clone()].into(),
875 tfs.source_slice.clone(),
876 tfs.templated_slice.clone(),
877 templated_file,
878 ));
879 out
880}
881
882fn iter_segments(
883 lexed_elements: Vec<TemplateElement>,
884 templated_file: &TemplatedFile,
885) -> Vec<ErasedSegment> {
886 let mut result: Vec<ErasedSegment> = Vec::with_capacity(lexed_elements.len());
887 let mut tfs_idx = 0;
889 let mut block_stack = BlockTracker::default();
890 let mut handled_zero_until = 0;
892 let templated_file_slices = &templated_file.sliced_file;
893
894 for element in lexed_elements {
896 let consumed_element_length = 0;
897 let mut stashed_source_idx = None;
898
899 for (idx, tfs) in templated_file_slices
900 .iter()
901 .skip(tfs_idx)
902 .enumerate()
903 .map(|(i, tfs)| (i + tfs_idx, tfs))
904 {
905 if is_zero_slice(&tfs.templated_slice) {
907 if idx >= handled_zero_until {
908 let next_tfs = templated_file_slices.get(idx + 1);
909 result.extend(handle_zero_length_slice(
910 tfs,
911 next_tfs,
912 &mut block_stack,
913 templated_file,
914 ));
915 handled_zero_until = idx + 1;
916 }
917 continue;
918 }
919
920 if tfs.has_slice_kind(TemplateSliceKind::Literal) {
921 let tfs_offset =
922 (tfs.source_slice.start as isize) - (tfs.templated_slice.start as isize);
923
924 if element.template_slice.end <= tfs.templated_slice.end {
927 let slice_start = stashed_source_idx.unwrap_or_else(|| {
928 let sum = element.template_slice.start as isize
929 + consumed_element_length as isize
930 + tfs_offset;
931 if sum < 0 {
932 panic!("Slice start is negative: {sum}");
933 }
934 sum.try_into()
935 .unwrap_or_else(|_| panic!("Cannot convert {sum} to usize"))
936 });
937
938 let source_slice_end =
939 (element.template_slice.end as isize + tfs_offset) as usize;
940 result.push(element.to_segment(
941 PositionMarker::new(
942 slice_start..source_slice_end,
943 element.template_slice.clone(),
944 templated_file.clone(),
945 None,
946 None,
947 ),
948 Some(consumed_element_length..element.raw.len()),
949 ));
950
951 if element.template_slice.end == tfs.templated_slice.end {
953 tfs_idx += 1
954 }
955 break;
957 } else if element.template_slice.start >= tfs.templated_slice.end {
958 log::debug!("Element starts at or after slice end, skipping");
962 continue;
963 } else {
964 log::debug!("Consuming whole spanning literal",);
968
969 if element.matcher.name == "whitespace" {
978 if stashed_source_idx.is_some() {
979 panic!("Found literal whitespace with stashed idx!")
980 }
981
982 let incremental_length =
983 tfs.templated_slice.end - element.template_slice.start;
984
985 let source_slice_start = element.template_slice.start as isize
986 + consumed_element_length as isize
987 + tfs_offset;
988 let source_slice_start =
989 source_slice_start.try_into().unwrap_or_else(|_| {
990 panic!("Cannot convert {source_slice_start} to usize")
991 });
992 let source_slice_end =
993 source_slice_start as isize + incremental_length as isize;
994 let source_slice_end = source_slice_end.try_into().unwrap_or_else(|_| {
995 panic!("Cannot convert {source_slice_end} to usize")
996 });
997
998 result.push(element.to_segment(
999 PositionMarker::new(
1000 source_slice_start..source_slice_end,
1001 element.template_slice.clone(),
1002 templated_file.clone(),
1003 None,
1004 None,
1005 ),
1006 offset_slice(consumed_element_length, incremental_length).into(),
1007 ));
1008 continue;
1010 } else {
1011 log::debug!("Spilling over literal slice.");
1015 if stashed_source_idx.is_none() {
1016 stashed_source_idx = (element.template_slice.start + idx).into();
1017 log::debug!("Stashing a source start. {stashed_source_idx:?}");
1018 }
1019 continue;
1021 }
1022 }
1023 } else if matches!(
1024 tfs.slice_kind(),
1025 TemplateSliceKind::Templated | TemplateSliceKind::BlockStart
1026 ) {
1027 if !is_zero_slice(&tfs.templated_slice) {
1030 if tfs.has_slice_kind(TemplateSliceKind::BlockStart) {
1035 block_stack.enter(tfs.source_slice.clone());
1036 }
1037
1038 if element.template_slice.end <= tfs.templated_slice.end {
1040 log::debug!("Contained templated slice.");
1041 let slice_start = if let Some(stashed_source_idx) = stashed_source_idx {
1046 stashed_source_idx
1047 } else {
1048 tfs.source_slice.start + consumed_element_length
1049 };
1050
1051 result.push(element.to_segment(
1052 PositionMarker::new(
1053 slice_start..tfs.source_slice.end,
1054 element.template_slice.clone(),
1055 templated_file.clone(),
1056 None,
1057 None,
1058 ),
1059 Some(consumed_element_length..element.raw.len()),
1060 ));
1061
1062 if element.template_slice.end == tfs.templated_slice.end {
1064 tfs_idx += 1
1065 }
1066 break;
1068 } else {
1069 if stashed_source_idx.is_none() {
1082 stashed_source_idx = Some(tfs.source_slice.start);
1083 continue;
1084 }
1085 continue;
1087 }
1088 }
1089 }
1090 panic!("Unable to process slice: {tfs:?}");
1091 }
1092 }
1093
1094 for idx in handled_zero_until..templated_file_slices.len() {
1097 let tfs = &templated_file_slices[idx];
1098 if is_zero_slice(&tfs.templated_slice) {
1099 let next_tfs = templated_file_slices.get(idx + 1);
1100 result.extend(handle_zero_length_slice(
1101 tfs,
1102 next_tfs,
1103 &mut block_stack,
1104 templated_file,
1105 ));
1106 }
1107 }
1108
1109 result
1110}
1111
1112#[cfg(test)]
1113mod tests {
1114 use super::*;
1115
1116 fn assert_matches(in_string: &str, matcher: &Matcher, match_string: Option<&str>) {
1123 let res = matcher.matches(in_string);
1124 if let Some(match_string) = match_string {
1125 assert_eq!(res.forward_string, &in_string[match_string.len()..]);
1126 assert_eq!(res.elements.len(), 1);
1127 assert_eq!(res.elements[0].text, match_string);
1128 } else {
1129 assert_eq!(res.forward_string, in_string);
1130 assert_eq!(res.elements.len(), 0);
1131 }
1132 }
1133
1134 #[test]
1135 fn test_parser_lexer_trim_post_subdivide() {
1136 let matcher: Vec<Matcher> = vec![
1137 Matcher::legacy(
1138 "function_script_terminator",
1139 |_| true,
1140 r";\s+(?!\*)\/(?!\*)|\s+(?!\*)\/(?!\*)",
1141 SyntaxKind::StatementTerminator,
1142 )
1143 .subdivider(Pattern::string("semicolon", ";", SyntaxKind::Semicolon))
1144 .post_subdivide(Pattern::legacy(
1145 "newline",
1146 |_| true,
1147 r"(\n|\r\n)+",
1148 SyntaxKind::Newline,
1149 )),
1150 ];
1151
1152 let res = Lexer::new(&matcher).lex_match(";\n/\n");
1153 assert_eq!(res.elements[0].text, ";");
1154 assert_eq!(res.elements[1].text, "\n");
1155 assert_eq!(res.elements[2].text, "/");
1156 assert_eq!(res.elements.len(), 3);
1157 }
1158
1159 #[test]
1161 fn test_parser_lexer_regex() {
1162 let tests = &[
1163 ("fsaljk", "f", "f"),
1164 ("fsaljk", r"f", "f"),
1165 ("fsaljk", r"[fas]*", "fsa"),
1166 (" \t fsaljk", r"[^\S\r\n]*", " \t "),
1168 (" \t \n fsaljk", r"[^\S\r\n]*", " \t "),
1170 (
1172 "'something boring' \t \n fsaljk",
1173 r"'[^']*'",
1174 "'something boring'",
1175 ),
1176 (
1177 "' something exciting \t\n ' \t \n fsaljk",
1178 r"'[^']*'",
1179 "' something exciting \t\n '",
1180 ),
1181 ];
1182
1183 for (raw, reg, res) in tests {
1184 let matcher = Matcher::legacy("test", |_| true, reg, SyntaxKind::Word);
1185
1186 assert_matches(raw, &matcher, Some(res));
1187 }
1188 }
1189
1190 #[test]
1192 fn test_parser_lexer_string() {
1193 let matcher = Matcher::string("dot", ".", SyntaxKind::Dot);
1194
1195 assert_matches(".fsaljk", &matcher, Some("."));
1196 assert_matches("fsaljk", &matcher, None);
1197 }
1198
1199 #[test]
1201 fn test_parser_lexer_lex_match() {
1202 let matchers: Vec<Matcher> = vec![
1203 Matcher::string("dot", ".", SyntaxKind::Dot),
1204 Matcher::regex("test", "#[^#]*#", SyntaxKind::Dash),
1205 ];
1206
1207 let res = Lexer::new(&matchers).lex_match("..#..#..#");
1208
1209 assert_eq!(res.forward_string, "#");
1210 assert_eq!(res.elements.len(), 5);
1211 assert_eq!(res.elements[2].text, "#..#");
1212 }
1213}