pomsky_syntax/parse/
parser.rs

1use std::str::FromStr;
2
3use crate::{
4    diagnose::{NumberError, ParseDiagnostic, ParseError, ParseErrorKind as PEK, ParseWarning},
5    exprs::*,
6    lexer::{tokenize, Token},
7    Span,
8};
9
10/// Parses a source string as a pomsky expression.
11///
12/// The `recursion` argument determines how much nesting is allowed in the
13/// expression. Note that **pomsky will overflow the stack** when parsing an
14/// expression with too much nesting, so the `recursion` argument should be low
15/// enough to prevent that. The recommended default is 256.
16pub fn parse(source: &str, recursion: u32) -> (Option<Rule<'_>>, Vec<ParseDiagnostic>) {
17    let tokens = tokenize(source);
18
19    let mut errors = Vec::new();
20    for &(t, span) in &tokens {
21        match t {
22            Token::Error => errors.push((span, None)),
23            Token::ErrorMsg(m) => errors.push((span, Some(m))),
24            _ => {}
25        }
26    }
27
28    if !errors.is_empty() {
29        let errors = errors
30            .into_iter()
31            .map(|(span, msg)| {
32                msg.map_or(PEK::UnknownToken, PEK::LexErrorWithMessage).at(span).into()
33            })
34            .collect::<Vec<_>>();
35
36        return (None, errors);
37    }
38
39    let mut parser = Parser {
40        source,
41        tokens: tokens.into_boxed_slice(),
42        offset: 0,
43        warnings: Vec::new(),
44        recursion,
45    };
46
47    let rule = match parser.parse_modified() {
48        Ok(rule) => rule,
49        Err(err) => {
50            let mut diagnostics = vec![err.into()];
51            diagnostics.extend(parser.warnings);
52            return (None, diagnostics);
53        }
54    };
55    if parser.is_empty() {
56        (Some(rule), parser.warnings)
57    } else {
58        let mut diagnostics = vec![PEK::LeftoverTokens.at(parser.span()).into()];
59        diagnostics.extend(parser.warnings);
60        (None, diagnostics)
61    }
62}
63
64type PResult<T> = Result<T, ParseError>;
65
66pub(super) struct Parser<'i> {
67    source: &'i str,
68    tokens: Box<[(Token, Span)]>,
69    offset: usize,
70    warnings: Vec<ParseDiagnostic>,
71    recursion: u32,
72}
73
74// Utilities
75impl<'i> Parser<'i> {
76    pub(super) fn is_empty(&self) -> bool {
77        self.tokens.len() == self.offset
78    }
79
80    pub(super) fn source_at(&self, span: Span) -> &'i str {
81        &self.source[span.range_unchecked()]
82    }
83
84    pub(super) fn peek(&self) -> Option<(Token, &'i str)> {
85        self.tokens.get(self.offset).map(|&(t, span)| (t, self.source_at(span)))
86    }
87
88    pub(super) fn peek_pair(&self) -> Option<(Token, Span)> {
89        self.tokens.get(self.offset).copied()
90    }
91
92    /// Returns the span of the next token
93    pub(super) fn span(&self) -> Span {
94        self.tokens
95            .get(self.offset)
96            .map_or_else(|| Span::new(self.source.len(), self.source.len()), |&(_, s)| s)
97    }
98
99    /// Returns the span of the previously consumed token
100    pub(super) fn last_span(&self) -> Span {
101        self.tokens[self.offset - 1].1
102    }
103
104    pub(super) fn advance(&mut self) {
105        self.offset += 1;
106    }
107
108    pub(super) fn recursion_start(&mut self) -> PResult<()> {
109        self.recursion =
110            self.recursion.checked_sub(1).ok_or_else(|| PEK::RecursionLimit.at(self.span()))?;
111        Ok(())
112    }
113
114    pub(super) fn recursion_end(&mut self) {
115        self.recursion += 1;
116    }
117
118    pub(super) fn add_warning(&mut self, warning: ParseWarning) {
119        self.warnings.push(warning.into());
120    }
121
122    pub(super) fn is(&mut self, token: Token) -> bool {
123        matches!(self.peek_pair(), Some((t, _)) if t == token)
124    }
125
126    pub(super) fn consume(&mut self, token: Token) -> bool {
127        match self.peek_pair() {
128            Some((t, _)) if t == token => {
129                self.offset += 1;
130                true
131            }
132            _ => false,
133        }
134    }
135
136    pub(super) fn consume_as(&mut self, token: Token) -> Option<&'i str> {
137        match self.peek_pair() {
138            Some((t, span)) if t == token => {
139                self.offset += 1;
140                Some(self.source_at(span))
141            }
142            _ => None,
143        }
144    }
145
146    pub(super) fn consume_reserved(&mut self, reserved: &str) -> bool {
147        match self.peek_pair() {
148            Some((Token::ReservedName, s)) if self.source_at(s) == reserved => {
149                self.offset += 1;
150                true
151            }
152            _ => false,
153        }
154    }
155
156    pub(super) fn consume_contextual_keyword(&mut self, keyword: &str) -> bool {
157        match self.peek_pair() {
158            Some((Token::Identifier, s)) if self.source_at(s) == keyword => {
159                self.offset += 1;
160                true
161            }
162            _ => false,
163        }
164    }
165
166    pub(super) fn consume_number<T: FromStr + PartialOrd>(&mut self, max: T) -> PResult<Option<T>> {
167        match self.peek_pair() {
168            Some((Token::Number, span)) => {
169                let n = str::parse(self.source_at(span))
170                    .ok()
171                    .and_then(|n| if n > max { None } else { Some(n) })
172                    .ok_or_else(|| PEK::Number(NumberError::TooLarge).at(span))?;
173                self.offset += 1;
174                Ok(Some(n))
175            }
176            _ => Ok(None),
177        }
178    }
179
180    pub(super) fn expect(&mut self, token: Token) -> PResult<()> {
181        match self.peek_pair() {
182            Some((t, _)) if t == token => {
183                self.offset += 1;
184                Ok(())
185            }
186            _ => Err(PEK::ExpectedToken(token).at(self.span())),
187        }
188    }
189
190    pub(super) fn expect_as(&mut self, token: Token) -> PResult<&'i str> {
191        match self.peek_pair() {
192            Some((t, span)) if t == token => {
193                self.offset += 1;
194                Ok(self.source_at(span))
195            }
196            _ => Err(PEK::ExpectedToken(token).at(self.span())),
197        }
198    }
199
200    pub(super) fn expect_number<T: FromStr>(&mut self) -> PResult<T> {
201        match self.peek_pair() {
202            Some((Token::Number, span)) => {
203                let n = str::parse(self.source_at(span))
204                    .map_err(|_| PEK::Number(NumberError::TooLarge).at(span))?;
205                self.offset += 1;
206                Ok(n)
207            }
208            _ => Err(PEK::ExpectedToken(Token::Number).at(self.span())),
209        }
210    }
211}