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