Skip to main content

yog_script_analyze/parser/
mod.rs

1//! The parser core: a cursor over tokens, problems and recovery, and the
2//! turn-taking — each form is parsed by its word (`words/`) or by a generic
3//! form here (types, members, blocks, expressions by precedence).
4
5mod block;
6mod decl;
7mod expr;
8mod header;
9mod lambda;
10mod member;
11mod params;
12pub(crate) mod patch;
13mod pattern;
14mod postfix;
15mod primary;
16mod types;
17
18use yog_ids::script_problem;
19
20use crate::source::token::{Punct, Span, Tok, Token};
21use crate::words::{Starts, Word};
22use yog_analysis::Report;
23
24/// A form that didn't parse: its problem is reported already.
25#[derive(Debug)]
26pub struct Fail;
27
28pub type PResult<T> = Result<T, Fail>;
29
30pub struct Parser<'t, 'r, 'a> {
31    toks: &'t [Token],
32    pos: usize,
33    pub(crate) report: &'r mut Report<'a>,
34    /// Patches are allowed (a `.yns` source).
35    pub(crate) nanites: bool,
36    /// A `{` here ends the expression (a system's period, a guard) instead
37    /// of starting a trailing lambda.
38    no_brace: bool,
39}
40
41impl<'t, 'r, 'a> Parser<'t, 'r, 'a> {
42    /// Over `toks`, which end with `Eof`.
43    pub fn new(toks: &'t [Token], report: &'r mut Report<'a>, nanites: bool) -> Self {
44        Parser {
45            toks,
46            pos: 0,
47            report,
48            nanites,
49            no_brace: false,
50        }
51    }
52
53    /// How many tokens are read.
54    pub(crate) fn consumed(&self) -> usize {
55        self.pos
56    }
57
58    pub(crate) fn tok(&self) -> &'t Tok {
59        self.nth(0)
60    }
61
62    /// The token `n` ahead (the last, `Eof`, past the end).
63    pub(crate) fn nth(&self, n: usize) -> &'t Tok {
64        let last = self.toks.len().saturating_sub(1);
65        &self.toks[(self.pos + n).min(last)].tok
66    }
67
68    pub(crate) fn span(&self) -> Span {
69        self.span_at(0)
70    }
71
72    /// Where the token `n` ahead is.
73    pub(crate) fn span_at(&self, n: usize) -> Span {
74        self.toks[(self.pos + n).min(self.toks.len() - 1)].span
75    }
76
77    /// Where the token before this one ends.
78    pub(crate) fn prev(&self) -> Span {
79        self.pos
80            .checked_sub(1)
81            .map_or_else(|| self.span(), |i| self.toks[i].span)
82    }
83
84    pub(crate) fn bump(&mut self) -> &'t Token {
85        let t = &self.toks[self.pos.min(self.toks.len() - 1)];
86        if t.tok != Tok::Eof {
87            self.pos += 1;
88        }
89        t
90    }
91
92    pub(crate) fn at(&self, p: Punct) -> bool {
93        *self.tok() == Tok::Punct(p)
94    }
95
96    pub(crate) fn nth_is(&self, n: usize, p: Punct) -> bool {
97        *self.nth(n) == Tok::Punct(p)
98    }
99
100    pub(crate) fn eat(&mut self, p: Punct) -> bool {
101        let here = self.at(p);
102        if here {
103            self.bump();
104        }
105        here
106    }
107
108    pub(crate) fn need(&mut self, p: Punct) -> PResult<Span> {
109        if self.at(p) {
110            return Ok(self.bump().span);
111        }
112        Err(self.expected(&format!("`{}`", p.text())))
113    }
114
115    /// The `;` ending a statement or a member.
116    pub(crate) fn end(&mut self) -> PResult<()> {
117        self.need(Punct::Semi).map(drop)
118    }
119
120    /// Whether `tok` is `word` (reserved or written as a name).
121    pub(crate) fn is_word(tok: &Tok, word: &Word) -> bool {
122        match tok {
123            Tok::Keyword(w) => *w == word,
124            Tok::Ident(text) => !word.reserved && text == word.text,
125            _ => false,
126        }
127    }
128
129    pub(crate) fn at_word(&self, word: &Word) -> bool {
130        Self::is_word(self.tok(), word)
131    }
132
133    pub(crate) fn nth_word(&self, n: usize, word: &Word) -> bool {
134        Self::is_word(self.nth(n), word)
135    }
136
137    pub(crate) fn eat_word(&mut self, word: &Word) -> bool {
138        let here = self.at_word(word);
139        if here {
140            self.bump();
141        }
142        here
143    }
144
145    pub(crate) fn need_word(&mut self, word: &Word) -> PResult<Span> {
146        if self.at_word(word) {
147            return Ok(self.bump().span);
148        }
149        Err(self.expected(&format!("`{}`", word.text)))
150    }
151
152    /// Whether a form of `word` starts here.
153    pub(crate) fn starts(&self, word: &Word, starts: Starts) -> bool {
154        starts.map_or_else(|| self.at_word(word), |f| f(self))
155    }
156
157    /// `what` was expected here: reported.
158    pub(crate) fn expected(&mut self, what: &str) -> Fail {
159        let found = describe(self.tok());
160        self.error(
161            script_problem::EXPECTED,
162            self.span(),
163            format!("expected {what}, found {found}"),
164        )
165    }
166
167    pub(crate) fn error(
168        &mut self,
169        code: &str,
170        span: Span,
171        message: impl std::fmt::Display,
172    ) -> Fail {
173        self.report.error(code, span, message);
174        Fail
175    }
176
177    /// Past a form that failed: through its `;`, or through the `{ … }`
178    /// that ended it, or up to a `}` closing what it's in.
179    pub(crate) fn recover(&mut self) {
180        let mut depth = 0usize;
181        loop {
182            match self.tok() {
183                Tok::Eof => return,
184                Tok::Punct(Punct::Semi) if depth == 0 => {
185                    self.bump();
186                    return;
187                }
188                Tok::Punct(Punct::LBrace | Punct::LParen | Punct::LBracket) => depth += 1,
189                Tok::Punct(Punct::RBrace | Punct::RParen | Punct::RBracket) => {
190                    if depth == 0 {
191                        return;
192                    }
193                    depth -= 1;
194                    if depth == 0 && self.at(Punct::RBrace) {
195                        self.bump();
196                        return;
197                    }
198                }
199                _ => {}
200            }
201            self.bump();
202        }
203    }
204
205    /// `f` with `{` ending expressions (a system's period, a guard).
206    pub(crate) fn no_brace<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
207        let was = std::mem::replace(&mut self.no_brace, true);
208        let out = f(self);
209        self.no_brace = was;
210        out
211    }
212
213    /// `f` with `{` allowed again (inside brackets and blocks).
214    pub(crate) fn braces<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
215        let was = std::mem::replace(&mut self.no_brace, false);
216        let out = f(self);
217        self.no_brace = was;
218        out
219    }
220}
221
222/// A token as a problem names it.
223fn describe(tok: &Tok) -> String {
224    match tok {
225        Tok::Ident(name) => format!("`{name}`"),
226        Tok::Keyword(w) => format!("`{}`", w.text),
227        Tok::Punct(p) => format!("`{}`", p.text()),
228        Tok::Int(_) | Tok::Float(_) | Tok::Unit(..) => "a number".into(),
229        Tok::Version(v) => format!("the version `{v}`"),
230        Tok::Str(_) | Tok::RawStr(_) | Tok::Text(_) => "a string".into(),
231        Tok::Char(_) => "a character".into(),
232        Tok::Lifetime(name) => format!("the lifetime `'{name}`"),
233        Tok::Content(kind, id) => format!("`@{kind}:{id}`"),
234        Tok::Tag(id) => format!("`#{id}`"),
235        Tok::Doc(_) => "a doc comment".into(),
236        Tok::Eof => "the end of the file".into(),
237    }
238}