Skip to main content

tour_core/
parser.rs

1use crate::{Delimiter, ParseError, Result, visitor::Visitor};
2
3/// Template source code parser.
4///
5/// For more details see the [crate level docs][crate].
6pub struct Parser<'a,V> {
7    source: &'a [u8],
8
9    // parser states
10    index: usize,
11    state: ParseState,
12    visitor: V,
13}
14
15impl<'a, V> Parser<'a, V> {
16    /// Create new [`Parser`].
17    ///
18    /// Requires a [`Visitor`] implementation.
19    ///
20    /// For static content only, use [`StaticVisitor`][super::StaticVisitor].
21    pub fn new(source: &'a str, visitor: V) -> Self {
22        Self {
23            source: source.as_bytes(),
24            index: 0,
25            state: ParseState::Static { start: 0 },
26            visitor,
27        }
28    }
29}
30
31enum ParseState {
32    Static { start: usize },
33    Expr { start: usize, open_delim: Delimiter },
34    OpenExpr { start: usize, brace: usize, },
35    CloseExpr { start: usize, brace: usize, open_delim: Delimiter, close_delim: Delimiter, },
36}
37
38impl<'a,V> Parser<'a,V>
39where
40    V: Visitor<'a>,
41{
42    /// Start parsing.
43    pub fn parse(mut self) -> Result<V> {
44        loop {
45            let current = self.index;
46            let Some(byte) = self.source.get(current) else {
47                break self.parse_leftover()?;
48            };
49
50            match self.state {
51                ParseState::Static { start } => {
52                    self.index += 1;
53                    if matches!(byte,b'{') {
54                        self.state = ParseState::OpenExpr { start, brace: current }
55                    }
56                }
57                ParseState::Expr { start, open_delim } => {
58                    self.index += 1;
59                    if let Some(close_delim) = Delimiter::match_close(*byte) {
60                        self.state = ParseState::CloseExpr {
61                            start, brace: current, open_delim, close_delim,
62                        }
63                    }
64                }
65                ParseState::OpenExpr { start, brace } => {
66                    match Delimiter::match_open(*byte) {
67                        Some(open_delim) => {
68                            self.index += 1;
69                            self.state = ParseState::Expr { start: current + 1, open_delim };
70                            let statics = Self::parse_str(&self.source[start..brace]);
71                            if !statics.is_empty() {
72                                self.visitor.visit_static(statics)?;
73                            }
74                        }
75                        None => self.state = ParseState::Static { start }
76                    }
77                }
78                ParseState::CloseExpr { start, brace, open_delim, close_delim } => {
79                    match byte {
80                        b'}' => {
81                            if open_delim != close_delim {
82                                return Err(ParseError::Generic(format!(
83                                    "delimiter shold be same, open `{}` closed with `{}`",
84                                    open_delim,close_delim,
85                                )));
86                            }
87
88                            self.index += 1;
89                            self.state = ParseState::Static { start: current + 1 };
90                            self.visitor.visit_expr(Self::parse_str(&self.source[start..brace]), open_delim)?;
91                        }
92                        _ => self.state = ParseState::Expr { start, open_delim }
93                    }
94                }
95            }
96        }
97
98        self.visitor.finish()
99    }
100
101    fn parse_leftover(&mut self) -> Result<()> {
102        match self.state {
103            ParseState::Static { start } | ParseState::OpenExpr { start, .. } => {
104                let statics = Self::parse_str(&self.source[start..]);
105                if statics.is_empty() {
106                    Ok(())
107                } else {
108                    self.visitor.visit_static(statics)
109                }
110            },
111            ParseState::Expr { .. } | ParseState::CloseExpr { .. } => {
112                // we dont have the closing delimiter here, just bail out
113                Err(ParseError::Generic("unclosed expression".to_owned()))
114            },
115        }
116    }
117
118    fn parse_str(source: &[u8]) -> &str {
119        std::str::from_utf8(source)
120            .expect("the input is string and we only check using byte char")
121    }
122}
123