1use logos::{Lexer as Lex, Logos, Span};
3
4#[derive(Clone, Default)]
6pub struct Extras;
7
8#[derive(Logos, Clone, Debug, Eq, PartialEq)]
10#[logos(extras = Extras)]
11#[logos(subpattern identifier = r#"[^\s"!#%&'()*+,./;<=>@\[/\]^`{|}~]"#)]
12pub enum Block {
13 #[regex(r"\{\{\{\{~?[\t ]*")]
15 StartRawBlock,
16
17 #[regex(r"\{\{!--")]
19 StartRawComment,
20
21 #[regex(r"\\\{\{\{?")]
23 StartRawStatement,
24
25 #[regex(r"\{\{!")]
27 StartComment,
28
29 #[regex(r"\{\{\{?~?[\t ]*")]
31 StartStatement,
32
33 #[regex(r"\{\{\~?[\t ]*#[\t ]*")]
35 StartBlockScope,
36
37 #[regex(r"\\?\[\[")]
39 StartLink,
40
41 #[regex(r"\{\{\~?[\t ]*/")]
43 EndBlockScope,
44
45 #[regex(r"\{\{\{\{~?[\t ]*/")]
47 EndRawBlock,
48
49 #[regex(r".")]
51 Text,
52
53 #[token("\n")]
55 Newline,
56
57 #[error]
59 Error,
60}
61
62#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
68#[logos(extras = Extras)]
69pub enum RawComment {
70 #[regex(r".")]
72 Text,
73
74 #[regex(r"--\}\}")]
76 End,
77
78 #[token("\n")]
80 Newline,
81
82 #[error]
84 Error,
85}
86
87#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
92#[logos(extras = Extras)]
93pub enum RawStatement {
94 #[regex(r".")]
96 Text,
97
98 #[regex(r"~?\}?\}\}")]
100 End,
101
102 #[token("\n")]
104 Newline,
105
106 #[error]
108 Error,
109}
110
111#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
117#[logos(extras = Extras)]
118pub enum Comment {
119 #[regex(r".")]
121 Text,
122
123 #[regex(r"\}\}")]
125 End,
126
127 #[token("\n")]
129 Newline,
130
131 #[error]
133 Error,
134}
135
136#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
142#[logos(extras = Extras)]
143#[logos(subpattern identifier = r#"[^\s"!#%&'()*+,./;<=>@\[/\]^`{|}~]"#)]
144pub enum Parameters {
145 #[token(r">")]
147 Partial,
148
149 #[token(r"else")]
151 ElseKeyword,
152
153 #[token(r"this")]
155 ExplicitThisKeyword,
156
157 #[token("./")]
159 ExplicitThisDotSlash,
160
161 #[token("../")]
163 ParentRef,
164
165 #[regex(r"(?&identifier)+", priority = 2)]
167 Identifier,
168
169 #[regex(r"@(?&identifier)+")]
171 LocalIdentifier,
172
173 #[regex(r"[./]")]
175 PathDelimiter,
176
177 #[token("\"")]
179 DoubleQuoteString,
180
181 #[token("'")]
183 SingleQuoteString,
184
185 #[token("[")]
187 StartArray,
188
189 #[token("(", priority = 3)]
191 StartSubExpression,
192
193 #[token(")")]
195 EndSubExpression,
196
197 #[regex(r"(?&identifier)+=")]
199 HashKey,
200
201 #[regex(r"-?([0-9]+\.)?[0-9]+((e|E)[+-]?[0-9]+)?", priority = 3)]
205 Number,
206
207 #[token("true")]
209 True,
210
211 #[token("false")]
213 False,
214
215 #[token("null")]
217 Null,
218
219 #[regex(r"[ \t]+")]
221 WhiteSpace,
222
223 #[regex(r"~?\}?\}?\}\}")]
225 End,
226
227 #[token("\n")]
229 Newline,
230
231 #[error]
233 Error,
234}
235
236#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
238#[logos(extras = Extras)]
239pub enum DoubleQuoteString {
240 #[regex(r#"[^\\"\n]+"#)]
242 Text,
243
244 #[token("\\n")]
246 EscapedNewline,
247
248 #[token(r#"\""#)]
250 Escaped,
251
252 #[token("\"")]
254 End,
255
256 #[token("\n")]
258 Newline,
259
260 #[error]
262 Error,
263}
264
265#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
267#[logos(extras = Extras)]
268pub enum SingleQuoteString {
269 #[regex(r#"[^\\'\n]+"#)]
271 Text,
272
273 #[token("\\n")]
275 EscapedNewline,
276
277 #[token(r#"\'"#)]
279 Escaped,
280
281 #[token("'")]
283 End,
284
285 #[token("\n")]
287 Newline,
288
289 #[error]
291 Error,
292}
293
294#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
296#[logos(extras = Extras)]
297pub enum Array {
298 #[regex(r#"[^\]\n]+"#)]
300 Text,
301
302 #[token(r#"\]"#)]
306 Escaped,
307
308 #[token("]")]
310 End,
311
312 #[token("\n")]
314 Newline,
315
316 #[error]
318 Error,
319}
320
321#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Logos)]
323#[logos(extras = Extras)]
324pub enum Link {
325 #[regex(r#"[^\\|\]]+"#)]
327 Text,
328
329 #[token("|")]
331 Pipe,
332
333 #[token("\\n")]
335 EscapedNewline,
336
337 #[token(r#"\|"#)]
339 EscapedPipe,
340
341 #[token(r#"\]"#)]
343 Escaped,
344
345 #[token(r"]]")]
347 End,
348
349 #[token("\n")]
351 Newline,
352
353 #[error]
355 Error,
356}
357
358#[derive(Clone, Debug, Eq, PartialEq)]
360pub enum Token {
361 Block(Block, Span),
363 RawComment(RawComment, Span),
365 RawStatement(RawStatement, Span),
367 Comment(Comment, Span),
369 Parameters(Parameters, Span),
371 DoubleQuoteString(DoubleQuoteString, Span),
373 SingleQuoteString(SingleQuoteString, Span),
375 Array(Array, Span),
377 Link(Link, Span),
379}
380
381impl Token {
382 pub fn span(&self) -> &Span {
384 match self {
385 Token::Block(_, ref span) => span,
386 Token::RawComment(_, ref span) => span,
387 Token::RawStatement(_, ref span) => span,
388 Token::Comment(_, ref span) => span,
389 Token::Parameters(_, ref span) => span,
390 Token::DoubleQuoteString(_, ref span) => span,
391 Token::SingleQuoteString(_, ref span) => span,
392 Token::Array(_, ref span) => span,
393 Token::Link(_, ref span) => span,
394 }
395 }
396
397 pub fn is_text(&self) -> bool {
399 match self {
400 Token::Block(ref t, _) => t == &Block::Text || t == &Block::Newline,
401 Token::RawComment(ref t, _) => {
402 t == &RawComment::Text || t == &RawComment::Newline
403 }
404 Token::RawStatement(ref t, _) => {
405 t == &RawStatement::Text || t == &RawStatement::Newline
406 }
407 Token::Comment(ref t, _) => {
408 t == &Comment::Text || t == &Comment::Newline
409 }
410 Token::Parameters(_, _) => false,
411 Token::DoubleQuoteString(_, _) => false,
412 Token::SingleQuoteString(_, _) => false,
413 Token::Array(_, _) => false,
414 Token::Link(_, _) => false,
415 }
416 }
417
418 pub fn is_newline(&self) -> bool {
420 match *self {
421 Token::RawComment(ref lex, _) => lex == &RawComment::Newline,
422 Token::RawStatement(ref lex, _) => lex == &RawStatement::Newline,
423 Token::Comment(ref lex, _) => lex == &Comment::Newline,
424 Token::Block(ref lex, _) => lex == &Block::Newline,
426 Token::Parameters(ref lex, _) => lex == &Parameters::Newline,
427 Token::DoubleQuoteString(ref lex, _) => {
428 lex == &DoubleQuoteString::Newline
429 }
430 Token::SingleQuoteString(ref lex, _) => {
431 lex == &SingleQuoteString::Newline
432 }
433 Token::Array(ref lex, _) => lex == &Array::Newline,
434 Token::Link(ref lex, _) => lex == &Link::Newline,
435 }
436 }
437}
438
439enum Modes<'source> {
440 Block(Lex<'source, Block>),
441 RawComment(Lex<'source, RawComment>),
442 RawStatement(Lex<'source, RawStatement>),
443 Comment(Lex<'source, Comment>),
444 Parameters(Lex<'source, Parameters>),
445 DoubleQuoteString(Lex<'source, DoubleQuoteString>),
446 SingleQuoteString(Lex<'source, SingleQuoteString>),
447 Array(Lex<'source, Array>),
448 Link(Lex<'source, Link>),
449}
450
451impl<'source> Modes<'source> {
452 fn new(s: &'source str) -> Self {
453 Self::Block(Block::lexer(s))
454 }
455}
456
457pub struct Lexer<'source> {
459 mode: Modes<'source>,
460}
461
462impl<'source> Lexer<'source> {
463 pub(crate) fn set_parameters_mode(&mut self) {
468 match &mut self.mode {
469 Modes::Block(lexer) => {
470 self.mode = Modes::Parameters(lexer.to_owned().morph())
471 }
472 _ => {}
473 }
474 }
475
476 pub(crate) fn until_mode(&mut self) -> Option<Token> {
481 while let Some(token) = self.next() {
482 match token {
483 Token::Block(_, _) => return Some(token),
484 _ => {}
485 }
486 }
487 None
488 }
489}
490
491impl<'source> Iterator for Lexer<'source> {
493 type Item = Token;
494 fn next(&mut self) -> Option<Self::Item> {
495 match &mut self.mode {
496 Modes::Block(lexer) => {
497 let result = lexer.next();
498 let span = lexer.span();
499
500 if let Some(token) = result {
501 if Block::StartRawBlock == token {
502 self.mode = Modes::Parameters(lexer.to_owned().morph());
503 } else if Block::EndRawBlock == token {
504 self.mode = Modes::Parameters(lexer.to_owned().morph());
505 } else if Block::StartRawComment == token {
506 self.mode = Modes::RawComment(lexer.to_owned().morph());
507 } else if Block::StartRawStatement == token {
508 self.mode =
509 Modes::RawStatement(lexer.to_owned().morph());
510 } else if Block::StartComment == token {
511 self.mode = Modes::Comment(lexer.to_owned().morph());
512 } else if Block::StartStatement == token {
513 self.mode = Modes::Parameters(lexer.to_owned().morph());
514 } else if Block::StartBlockScope == token {
515 self.mode = Modes::Parameters(lexer.to_owned().morph());
516 } else if Block::EndBlockScope == token {
517 self.mode = Modes::Parameters(lexer.to_owned().morph());
518 } else if Block::StartLink == token {
519 self.mode = Modes::Link(lexer.to_owned().morph());
520 }
521 Some(Token::Block(token, span))
522 } else {
523 None
524 }
525 }
526 Modes::RawComment(lexer) => {
527 let result = lexer.next();
528 let span = lexer.span();
529
530 if let Some(token) = result {
531 if RawComment::End == token {
532 self.mode = Modes::Block(lexer.to_owned().morph());
533 }
534 Some(Token::RawComment(token, span))
535 } else {
536 None
537 }
538 }
539 Modes::RawStatement(lexer) => {
540 let result = lexer.next();
541 let span = lexer.span();
542
543 if let Some(token) = result {
544 if RawStatement::End == token {
545 self.mode = Modes::Block(lexer.to_owned().morph());
546 }
547 Some(Token::RawStatement(token, span))
548 } else {
549 None
550 }
551 }
552 Modes::Comment(lexer) => {
553 let result = lexer.next();
554 let span = lexer.span();
555
556 if let Some(token) = result {
557 if Comment::End == token {
558 self.mode = Modes::Block(lexer.to_owned().morph());
559 }
560 Some(Token::Comment(token, span))
561 } else {
562 None
563 }
564 }
565 Modes::Parameters(lexer) => {
566 let result = lexer.next();
567 let span = lexer.span();
568
569 if let Some(token) = result {
570 if Parameters::DoubleQuoteString == token {
571 self.mode =
572 Modes::DoubleQuoteString(lexer.to_owned().morph());
573 } else if Parameters::SingleQuoteString == token {
574 self.mode =
575 Modes::SingleQuoteString(lexer.to_owned().morph());
576 } else if Parameters::StartArray == token {
577 self.mode = Modes::Array(lexer.to_owned().morph());
578 } else if Parameters::End == token {
579 self.mode = Modes::Block(lexer.to_owned().morph());
580 }
581 Some(Token::Parameters(token, span))
582 } else {
583 None
584 }
585 }
586 Modes::DoubleQuoteString(lexer) => {
587 let result = lexer.next();
588 let span = lexer.span();
589
590 if let Some(token) = result {
591 if DoubleQuoteString::End == token {
592 self.mode = Modes::Parameters(lexer.to_owned().morph());
593 }
594 Some(Token::DoubleQuoteString(token, span))
595 } else {
596 None
597 }
598 }
599 Modes::SingleQuoteString(lexer) => {
600 let result = lexer.next();
601 let span = lexer.span();
602
603 if let Some(token) = result {
604 if SingleQuoteString::End == token {
605 self.mode = Modes::Parameters(lexer.to_owned().morph());
606 }
607 Some(Token::SingleQuoteString(token, span))
608 } else {
609 None
610 }
611 }
612 Modes::Array(lexer) => {
613 let result = lexer.next();
614 let span = lexer.span();
615
616 if let Some(token) = result {
617 if Array::End == token {
618 self.mode = Modes::Parameters(lexer.to_owned().morph());
619 }
620 Some(Token::Array(token, span))
621 } else {
622 None
623 }
624 }
625 Modes::Link(lexer) => {
626 let result = lexer.next();
627 let span = lexer.span();
628
629 if let Some(token) = result {
630 if Link::End == token {
631 self.mode = Modes::Block(lexer.to_owned().morph());
632 }
633 Some(Token::Link(token, span))
634 } else {
635 None
636 }
637 }
638 }
639 }
640}
641
642fn normalize(tokens: Vec<Token>) -> Vec<Token> {
643 let mut normalized: Vec<Token> = Vec::new();
644 let mut span: Option<Span> = None;
645
646 for t in tokens.into_iter() {
647 if t.is_text() {
648 if let Some(ref mut span) = span {
649 span.end = t.span().end;
650 } else {
651 span = Some(t.span().clone());
652 }
653 } else {
654 if let Some(span) = span.take() {
655 normalized.push(Token::Block(Block::Text, span));
656 normalized.push(t);
657 } else {
658 normalized.push(t);
659 }
660 }
661 }
662
663 if let Some(span) = span.take() {
664 normalized.push(Token::Block(Block::Text, span));
665 }
666
667 normalized
668}
669
670pub fn lex(s: &str) -> Lexer {
674 Lexer {
675 mode: Modes::new(s),
676 }
677}
678
679pub fn collect(s: &str, normalized: bool) -> Vec<Token> {
688 let tokens = lex(s).collect();
689 if normalized {
690 normalize(tokens)
691 } else {
692 tokens
693 }
694}