yog_script_analyze/parser/
mod.rs1mod await_using;
6mod block;
7mod decl;
8pub(crate) mod expr;
9mod header;
10mod lambda;
11mod member;
12mod params;
13pub(crate) mod patch;
14pub(crate) mod pattern;
15mod postfix;
16mod primary;
17mod query;
18mod types;
19
20use yog_ids::script_problem;
21
22use crate::source::token::{Punct, Span, Tok, Token};
23use crate::words::{Starts, Word};
24use yog_analysis::Report;
25
26#[derive(Debug)]
28pub struct Fail;
29
30pub type PResult<T> = Result<T, Fail>;
31
32pub struct Parser<'t, 'r, 'a> {
33 toks: &'t [Token],
34 pos: usize,
35 pub(crate) report: &'r mut Report<'a>,
36 pub(crate) nanites: bool,
38 no_brace: bool,
41}
42
43impl<'t, 'r, 'a> Parser<'t, 'r, 'a> {
44 pub fn new(toks: &'t [Token], report: &'r mut Report<'a>, nanites: bool) -> Self {
46 Parser {
47 toks,
48 pos: 0,
49 report,
50 nanites,
51 no_brace: false,
52 }
53 }
54
55 pub(crate) fn consumed(&self) -> usize {
57 self.pos
58 }
59
60 pub(crate) fn tok(&self) -> &'t Tok {
61 self.nth(0)
62 }
63
64 pub(crate) fn nth(&self, n: usize) -> &'t Tok {
66 let last = self.toks.len().saturating_sub(1);
67 &self.toks[(self.pos + n).min(last)].tok
68 }
69
70 pub(crate) fn span(&self) -> Span {
71 self.span_at(0)
72 }
73
74 pub(crate) fn span_at(&self, n: usize) -> Span {
76 self.toks[(self.pos + n).min(self.toks.len() - 1)].span
77 }
78
79 pub(crate) fn prev(&self) -> Span {
81 self.pos
82 .checked_sub(1)
83 .map_or_else(|| self.span(), |i| self.toks[i].span)
84 }
85
86 pub(crate) fn bump(&mut self) -> &'t Token {
87 let t = &self.toks[self.pos.min(self.toks.len() - 1)];
88 if t.tok != Tok::Eof {
89 self.pos += 1;
90 }
91 t
92 }
93
94 pub(crate) fn at(&self, p: Punct) -> bool {
95 *self.tok() == Tok::Punct(p)
96 }
97
98 pub(crate) fn joined(&self, p: Punct) -> bool {
101 self.nth_is(1, p) && self.span_at(0).end == self.span_at(1).start
102 }
103
104 pub(crate) fn nth_is(&self, n: usize, p: Punct) -> bool {
105 *self.nth(n) == Tok::Punct(p)
106 }
107
108 pub(crate) fn eat(&mut self, p: Punct) -> bool {
109 let here = self.at(p);
110 if here {
111 self.bump();
112 }
113 here
114 }
115
116 pub(crate) fn need(&mut self, p: Punct) -> PResult<Span> {
117 if self.at(p) {
118 return Ok(self.bump().span);
119 }
120 Err(self.expected(&format!("`{}`", p.text())))
121 }
122
123 pub(crate) fn end(&mut self) -> PResult<()> {
125 self.need(Punct::Semi).map(drop)
126 }
127
128 pub(crate) fn is_word(tok: &Tok, word: &Word) -> bool {
130 match tok {
131 Tok::Keyword(w) => *w == word,
132 Tok::Ident(text) => !word.reserved && text == word.text,
133 _ => false,
134 }
135 }
136
137 pub(crate) fn at_word(&self, word: &Word) -> bool {
138 Self::is_word(self.tok(), word)
139 }
140
141 pub(crate) fn nth_word(&self, n: usize, word: &Word) -> bool {
142 Self::is_word(self.nth(n), word)
143 }
144
145 pub(crate) fn eat_word(&mut self, word: &Word) -> bool {
146 let here = self.at_word(word);
147 if here {
148 self.bump();
149 }
150 here
151 }
152
153 pub(crate) fn need_word(&mut self, word: &Word) -> PResult<Span> {
154 if self.at_word(word) {
155 return Ok(self.bump().span);
156 }
157 Err(self.expected(&format!("`{}`", word.text)))
158 }
159
160 pub(crate) fn starts(&self, word: &Word, starts: Starts) -> bool {
162 starts.map_or_else(|| self.at_word(word), |f| f(self))
163 }
164
165 pub(crate) fn expected(&mut self, what: &str) -> Fail {
167 let found = describe(self.tok());
168 self.error(
169 script_problem::EXPECTED,
170 self.span(),
171 format!("expected {what}, found {found}"),
172 )
173 }
174
175 pub(crate) fn error(
176 &mut self,
177 code: &str,
178 span: Span,
179 message: impl std::fmt::Display,
180 ) -> Fail {
181 self.report.error(code, span, message);
182 Fail
183 }
184
185 pub(crate) fn recover(&mut self) {
188 let start = self.pos;
189 let mut depth = 0usize;
190 loop {
191 match self.tok() {
192 Tok::Eof => return,
193 Tok::Punct(Punct::Semi) if depth == 0 => {
194 self.bump();
195 return;
196 }
197 Tok::Punct(Punct::LBrace | Punct::LParen | Punct::LBracket) => depth += 1,
198 Tok::Punct(Punct::RBrace | Punct::RParen | Punct::RBracket) => {
199 if depth == 0 {
200 if self.pos == start && !self.at(Punct::RBrace) {
204 self.bump();
205 }
206 return;
207 }
208 depth -= 1;
209 if depth == 0 && self.at(Punct::RBrace) {
210 self.bump();
211 return;
212 }
213 }
214 _ => {}
215 }
216 self.bump();
217 }
218 }
219
220 pub(crate) fn no_brace<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
222 let was = std::mem::replace(&mut self.no_brace, true);
223 let out = f(self);
224 self.no_brace = was;
225 out
226 }
227
228 pub(crate) fn braces<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
230 let was = std::mem::replace(&mut self.no_brace, false);
231 let out = f(self);
232 self.no_brace = was;
233 out
234 }
235}
236
237fn describe(tok: &Tok) -> String {
239 match tok {
240 Tok::Ident(name) => format!("`{name}`"),
241 Tok::Keyword(w) => format!("`{}`", w.text),
242 Tok::Punct(p) => format!("`{}`", p.text()),
243 Tok::Int(_) | Tok::Float(_) | Tok::Unit(..) => "a number".into(),
244 Tok::Version(v) => format!("the version `{v}`"),
245 Tok::Str(_) | Tok::RawStr(_) => "a string".into(),
246 Tok::Char(_) => "a character".into(),
247 Tok::Lifetime(name) => format!("the lifetime `'{name}`"),
248 Tok::Doc(_) => "a doc comment".into(),
249 Tok::Eof => "the end of the file".into(),
250 }
251}