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    pub(crate) fn tok(&self) -> &'t Tok {
54        self.nth(0)
55    }
56
57    /// The token `n` ahead (the last, `Eof`, past the end).
58    pub(crate) fn nth(&self, n: usize) -> &'t Tok {
59        let last = self.toks.len().saturating_sub(1);
60        &self.toks[(self.pos + n).min(last)].tok
61    }
62
63    pub(crate) fn span(&self) -> Span {
64        self.toks[self.pos.min(self.toks.len() - 1)].span
65    }
66
67    /// Where the token before this one ends.
68    pub(crate) fn prev(&self) -> Span {
69        self.pos
70            .checked_sub(1)
71            .map_or_else(|| self.span(), |i| self.toks[i].span)
72    }
73
74    pub(crate) fn bump(&mut self) -> &'t Token {
75        let t = &self.toks[self.pos.min(self.toks.len() - 1)];
76        if t.tok != Tok::Eof {
77            self.pos += 1;
78        }
79        t
80    }
81
82    pub(crate) fn at(&self, p: Punct) -> bool {
83        *self.tok() == Tok::Punct(p)
84    }
85
86    pub(crate) fn nth_is(&self, n: usize, p: Punct) -> bool {
87        *self.nth(n) == Tok::Punct(p)
88    }
89
90    pub(crate) fn eat(&mut self, p: Punct) -> bool {
91        let here = self.at(p);
92        if here {
93            self.bump();
94        }
95        here
96    }
97
98    pub(crate) fn need(&mut self, p: Punct) -> PResult<Span> {
99        if self.at(p) {
100            return Ok(self.bump().span);
101        }
102        Err(self.expected(&format!("`{}`", p.text())))
103    }
104
105    /// The `;` ending a statement or a member.
106    pub(crate) fn end(&mut self) -> PResult<()> {
107        self.need(Punct::Semi).map(drop)
108    }
109
110    /// Whether `tok` is `word` (reserved or written as a name).
111    pub(crate) fn is_word(tok: &Tok, word: &Word) -> bool {
112        match tok {
113            Tok::Keyword(w) => *w == word,
114            Tok::Ident(text) => !word.reserved && text == word.text,
115            _ => false,
116        }
117    }
118
119    pub(crate) fn at_word(&self, word: &Word) -> bool {
120        Self::is_word(self.tok(), word)
121    }
122
123    pub(crate) fn nth_word(&self, n: usize, word: &Word) -> bool {
124        Self::is_word(self.nth(n), word)
125    }
126
127    pub(crate) fn eat_word(&mut self, word: &Word) -> bool {
128        let here = self.at_word(word);
129        if here {
130            self.bump();
131        }
132        here
133    }
134
135    pub(crate) fn need_word(&mut self, word: &Word) -> PResult<Span> {
136        if self.at_word(word) {
137            return Ok(self.bump().span);
138        }
139        Err(self.expected(&format!("`{}`", word.text)))
140    }
141
142    /// Whether a form of `word` starts here.
143    pub(crate) fn starts(&self, word: &Word, starts: Starts) -> bool {
144        starts.map_or_else(|| self.at_word(word), |f| f(self))
145    }
146
147    /// `what` was expected here: reported.
148    pub(crate) fn expected(&mut self, what: &str) -> Fail {
149        let found = describe(self.tok());
150        self.error(
151            script_problem::EXPECTED,
152            self.span(),
153            format!("expected {what}, found {found}"),
154        )
155    }
156
157    pub(crate) fn error(
158        &mut self,
159        code: &str,
160        span: Span,
161        message: impl std::fmt::Display,
162    ) -> Fail {
163        self.report.error(code, span, message);
164        Fail
165    }
166
167    /// Past a form that failed: through its `;`, or through the `{ … }`
168    /// that ended it, or up to a `}` closing what it's in.
169    pub(crate) fn recover(&mut self) {
170        let mut depth = 0usize;
171        loop {
172            match self.tok() {
173                Tok::Eof => return,
174                Tok::Punct(Punct::Semi) if depth == 0 => {
175                    self.bump();
176                    return;
177                }
178                Tok::Punct(Punct::LBrace | Punct::LParen | Punct::LBracket) => depth += 1,
179                Tok::Punct(Punct::RBrace | Punct::RParen | Punct::RBracket) => {
180                    if depth == 0 {
181                        return;
182                    }
183                    depth -= 1;
184                    if depth == 0 && self.at(Punct::RBrace) {
185                        self.bump();
186                        return;
187                    }
188                }
189                _ => {}
190            }
191            self.bump();
192        }
193    }
194
195    /// `f` with `{` ending expressions (a system's period, a guard).
196    pub(crate) fn no_brace<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
197        let was = std::mem::replace(&mut self.no_brace, true);
198        let out = f(self);
199        self.no_brace = was;
200        out
201    }
202
203    /// `f` with `{` allowed again (inside brackets and blocks).
204    pub(crate) fn braces<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
205        let was = std::mem::replace(&mut self.no_brace, false);
206        let out = f(self);
207        self.no_brace = was;
208        out
209    }
210}
211
212/// A token as a problem names it.
213fn describe(tok: &Tok) -> String {
214    match tok {
215        Tok::Ident(name) => format!("`{name}`"),
216        Tok::Keyword(w) => format!("`{}`", w.text),
217        Tok::Punct(p) => format!("`{}`", p.text()),
218        Tok::Int(_) | Tok::Float(_) | Tok::Unit(..) => "a number".into(),
219        Tok::Version(v) => format!("the version `{v}`"),
220        Tok::Str(_) | Tok::RawStr(_) | Tok::Text(_) => "a string".into(),
221        Tok::Char(_) => "a character".into(),
222        Tok::Content(kind, id) => format!("`@{kind}:{id}`"),
223        Tok::Tag(id) => format!("`#{id}`"),
224        Tok::Doc(_) => "a doc comment".into(),
225        Tok::Eof => "the end of the file".into(),
226    }
227}