anillo/parser.rs
1//! Types and implementations for parsing a pre-built Token buffer.
2
3use std::{collections::VecDeque, error::Error};
4
5use crate::{
6 error::CompilationError,
7 token::{
8 Ast, CallArg, ExternalFunctionCall, ExternalFunctionNode, FuncArg, FuncArgType, Ingot,
9 IsrNode, Ring, Token, TokenInfo,
10 },
11};
12
13/// The Anillo Parser
14///
15/// The parser lives at a slightly higher level than the lexer. At this level,
16/// We can begin to do some additional reasoning about the program as we build
17/// the AST (see `AST`).
18///
19/// The parse_* methods of this struct are each intended to be called **_only_**
20/// when its caller has determined that it must succeed (either due to the
21/// grammar mandating it, or a peek that indicated an optional token was found).
22/// When these functions fail, this thus causes the whole compilation to fail
23/// with a diagnostic.
24#[derive(Debug)]
25pub struct Parser {
26 tokens: VecDeque<TokenInfo>,
27}
28
29impl Parser {
30 pub fn new(tokens: VecDeque<TokenInfo>) -> Parser {
31 Parser { tokens }
32 }
33
34 /// Generates the AST from a `TokenInfo` buffer input
35 ///
36 /// Since we have been fed rich token info from the lexer at this point,
37 /// we can model the grammar closely here (no need to worry about whitespace!).
38 /// Every token type has a very small set of expected token types that may
39 /// appear next, which makes a combinator style (as-in what was shown in
40 /// class) of failure easy to detect.
41 pub fn run(&mut self, verbose: bool) -> Result<Ast, Box<dyn Error>> {
42 let mut ast_vec: Vec<Ingot> = Vec::new();
43
44 while let Some(token) = self.tokens.pop_front() {
45 if verbose {
46 println!(
47 "###########################################################################"
48 );
49 println!("Consumed token: {}", token);
50 println!("Remaining tokens:");
51 dbg!(&self.tokens);
52 println!("AST in progress:");
53 dbg!(&ast_vec);
54 println!();
55 println!(
56 "###########################################################################"
57 );
58 }
59
60 match token.borrow_token() {
61 Token::KeywordExtern => {
62 let ext = self.parse_extern(token.line(), token.col())?;
63 ast_vec.push(Ingot::ExternalFunction(ext));
64 }
65 Token::KeywordIsr => {
66 let isr: IsrNode = self.parse_isr(token.line(), token.col())?;
67 ast_vec.push(Ingot::Isr(isr));
68 }
69 other => {
70 return Err(Box::new(CompilationError::new(
71 token.line(),
72 token.col(),
73 format!("Expected the start of an Ingot, got {}", other),
74 )));
75 }
76 }
77 }
78
79 Ok(Ast::new(ast_vec))
80 }
81
82 /// Parse info about an external function declaration
83 fn parse_extern(
84 &mut self,
85 last_line: u32,
86 last_col: u32,
87 ) -> Result<ExternalFunctionNode, CompilationError> {
88 match self.tokens.pop_front() {
89 // found something, need to see if it's a func
90 Some(func) => match func.borrow_token() {
91 // found a func name, need to see if '(' is next
92 Token::Identifier(func_name) => match self.tokens.pop_front() {
93 // found something, need to see if it's '('
94 Some(lparen) => match lparen.borrow_token() {
95 Token::LeftParen => {
96 let args = self.parse_func_args()?;
97 let mut privilege: Option<Ring> = None;
98 if let Some(token) = self.tokens.front()
99 && let &Token::KeywordWithLevel = token.borrow_token()
100 {
101 privilege =
102 Some(self.parse_withlevel(lparen.line(), lparen.col())?);
103 }
104
105 // weird rust return expression
106 Ok(ExternalFunctionNode::new(
107 func_name.clone(),
108 args,
109 privilege,
110 ))
111 }
112 other_token => Err(CompilationError::new(
113 lparen.line(),
114 lparen.col(),
115 format!(
116 "Expected '(' after extern declaration, found: {}",
117 other_token
118 ),
119 )),
120 },
121 None => Err(CompilationError::new(
122 func.line(),
123 func.col(),
124 String::from("Expected '(' after extern declaration, found EOF"),
125 )),
126 },
127 other_token => Err(CompilationError::new(
128 func.line(),
129 func.col(),
130 format!(
131 "Expected function name after extern declaration, found: {}",
132 other_token
133 ),
134 )),
135 },
136 None => Err(CompilationError::new(
137 last_line,
138 last_col,
139 String::from("Expected function name after extern declaration, found EOF"),
140 )),
141 }
142 }
143
144 /// Parse info about the valid args in an external function declaration
145 fn parse_func_args(&mut self) -> Result<Vec<FuncArg>, CompilationError> {
146 #[derive(Clone, Copy)]
147 enum SeekState {
148 Start,
149 SeekArg,
150 SeekType,
151 }
152 let mut args: Vec<FuncArg> = Vec::new();
153 let mut state: SeekState = SeekState::Start;
154
155 let mut type_t: FuncArgType = FuncArgType::U8;
156
157 while let Some(token) = self.tokens.pop_front() {
158 match (token.borrow_token(), state) {
159 (Token::RightParen, SeekState::SeekType | SeekState::Start) => break,
160 (Token::RightParen, SeekState::SeekArg) => {
161 return Err(CompilationError::new(
162 token.line(),
163 token.col(),
164 String::from("Expected arg name for function argument, found ')'"),
165 ));
166 }
167 (Token::Identifier(type_str), SeekState::SeekType | SeekState::Start) => {
168 type_t = match type_str.as_str() {
169 "u8" => FuncArgType::U8,
170 "i8" => FuncArgType::I8,
171 "u16" => FuncArgType::U16,
172 "i16" => FuncArgType::I16,
173 "u32" => FuncArgType::U32,
174 "i32" => FuncArgType::I32,
175 "u64" => FuncArgType::U64,
176 "i64" => FuncArgType::I64,
177 other => {
178 return Err(CompilationError::new(
179 token.line(),
180 token.col(),
181 format!("Invalid c type: {}", other),
182 ));
183 }
184 };
185
186 state = SeekState::SeekArg;
187 }
188 (Token::Comma, SeekState::Start) => {
189 return Err(CompilationError::new(
190 token.line(),
191 token.col(),
192 String::from("Expected ')' or fun arg, got ','"),
193 ));
194 }
195 (Token::Comma, SeekState::SeekType) => continue,
196 (Token::Comma, SeekState::SeekArg) => continue,
197 (Token::Identifier(arg), SeekState::SeekArg) => {
198 state = SeekState::SeekType;
199 args.push(FuncArg::new(arg.clone(), type_t));
200 }
201 (unexpected, _) => {
202 return Err(CompilationError::new(
203 token.line(),
204 token.col(),
205 format!("Unexpected token while parsing func_args: {}", unexpected),
206 ));
207 }
208 }
209 }
210
211 Ok(args)
212 }
213
214 /// Parse a found (optional) RingLevel expression.
215 ///
216 /// ExternalFunctions and ISRs that do not find 'WithLevel' won't call this,
217 /// and the AST validator will interpret the None variant of an Option<Ring>
218 /// as implicitly meaning the Super privilege.
219 fn parse_withlevel(
220 &mut self,
221 last_line: u32,
222 last_column: u32,
223 ) -> Result<Ring, CompilationError> {
224 match self.tokens.pop_front() {
225 Some(token) => match token.borrow_token() {
226 Token::KeywordWithLevel => match self.tokens.pop_front() {
227 Some(lparen) => match lparen.borrow_token() {
228 Token::LeftParen => match self.tokens.pop_front() {
229 Some(priv_level) => match priv_level.borrow_token() {
230 Token::KeywordPrivilege(level) => match self.tokens.pop_front() {
231 Some(token) => match token.borrow_token() {
232 Token::RightParen => Ok(*level),
233 other_token => Err(CompilationError::new(
234 token.line(),
235 token.col(),
236 format!(
237 "Expected ')' after privilege level, found {}",
238 other_token
239 ),
240 )),
241 },
242 None => Err(CompilationError::new(
243 priv_level.line(),
244 priv_level.col(),
245 String::from(
246 "Expected ')' after privilege level, found EOF",
247 ),
248 )),
249 },
250 other_token => Err(CompilationError::new(
251 priv_level.line(),
252 priv_level.col(),
253 format!("Expected privilege level, found: {}", other_token),
254 )),
255 },
256 None => Err(CompilationError::new(
257 lparen.line(),
258 lparen.col(),
259 String::from("Expected privilege level, found EOF"),
260 )),
261 },
262 other_token => Err(CompilationError::new(
263 lparen.line(),
264 lparen.col(),
265 format!("Expected '(' after WithLevel, found: {}", other_token),
266 )),
267 },
268 None => Err(CompilationError::new(
269 token.line(),
270 token.col(),
271 String::from("Expected '(' after WithLevel, found EOF"),
272 )),
273 },
274 other_token => Err(CompilationError::new(
275 token.line(),
276 token.col(),
277 format!("Expected 'WithLevel', found: {}", other_token),
278 )),
279 },
280 None => Err(CompilationError::new(
281 last_line,
282 last_column,
283 String::from("Expected 'WithLevel', found EOF"),
284 )),
285 }
286 }
287
288 /// Parse info about a defined isr
289 fn parse_isr(&mut self, last_line: u32, last_column: u32) -> Result<IsrNode, CompilationError> {
290 match self.tokens.pop_front() {
291 Some(name) => match name.borrow_token() {
292 Token::Identifier(isr_name) => match self.tokens.pop_front() {
293 Some(lparen) => match lparen.borrow_token() {
294 Token::LeftParen => match self.tokens.pop_front() {
295 Some(num) => match num.borrow_token() {
296 Token::Identifier(isr_num_str) => {
297 // Isr Numbers above 255 are rare to my knowledge
298 match isr_num_str.parse::<u8>() {
299 Ok(isr_num) => match self.tokens.pop_front() {
300 Some(rparen) => match rparen.borrow_token() {
301 Token::RightParen => {
302 let mut privilege: Option<Ring> = None;
303 if let Some(token) = self.tokens.front()
304 && let Token::KeywordWithLevel =
305 token.borrow_token()
306 {
307 privilege = Some(self.parse_withlevel(
308 num.line(),
309 num.col(),
310 )?);
311 }
312
313 let calling_func =
314 self.parse_isr_body(num.line(), num.col())?;
315 Ok(IsrNode::new(
316 isr_name.clone(),
317 isr_num,
318 privilege,
319 calling_func,
320 ))
321 }
322 other_token => Err(CompilationError::new(
323 rparen.line(),
324 rparen.col(),
325 format!(
326 "Expected closing ')', found: {}",
327 other_token
328 ),
329 )),
330 },
331 None => Err(CompilationError::new(
332 num.line(),
333 num.col(),
334 String::from("Expected closing ')', found EOF"),
335 )),
336 },
337 Err(e) => Err(CompilationError::new(
338 num.line(),
339 num.col(),
340 format!("Expected numeric value, failed with: {}", e),
341 )),
342 }
343 }
344 other_token => Err(CompilationError::new(
345 num.line(),
346 num.col(),
347 format!("Expected an Isr number, found: {}", other_token),
348 )),
349 },
350 None => Err(CompilationError::new(
351 lparen.line(),
352 lparen.col(),
353 String::from(""),
354 )),
355 },
356 other_token => Err(CompilationError::new(
357 lparen.line(),
358 lparen.col(),
359 format!("Expected '(' after Isr name, found: {}", other_token),
360 )),
361 },
362 None => Err(CompilationError::new(
363 name.line(),
364 name.col(),
365 String::from("Expected '(' after Isr name, found EOF"),
366 )),
367 },
368 other_token => Err(CompilationError::new(
369 name.line(),
370 name.col(),
371 format!("Expected ISR name, found: {}", other_token),
372 )),
373 },
374 None => Err(CompilationError::new(
375 last_line,
376 last_column,
377 String::from("Expected ISR name, found EOF"),
378 )),
379 }
380 }
381
382 /// Parse the collection of functions called from within the ISR.
383 ///
384 /// An ISR may only call functions (and those functions must be declared
385 /// with the 'extern' keyword as well). It may call 0 or 1 at the moment,
386 /// but this may be expanded in the future.
387 fn parse_isr_body(
388 &mut self,
389 last_line: u32,
390 last_column: u32,
391 ) -> Result<Option<ExternalFunctionCall>, CompilationError> {
392 match self.tokens.pop_front() {
393 Some(lbracket) => match lbracket.borrow_token() {
394 Token::LeftBracket => match self.tokens.pop_front() {
395 Some(remaining) => match remaining.borrow_token() {
396 Token::KeywordCall => Ok(Some(
397 self.parse_function_call(remaining.line(), remaining.col())?,
398 )),
399 Token::RightBracket => Ok(None),
400 other => Err(CompilationError::new(
401 remaining.line(),
402 remaining.col(),
403 format!("Expected call expression or closing '}}', found: {}", other),
404 )),
405 },
406 None => Err(CompilationError::new(
407 lbracket.line(),
408 lbracket.col(),
409 String::from("Expected call expression or closing '}', found EOF"),
410 )),
411 },
412 other_token => Err(CompilationError::new(
413 lbracket.line(),
414 lbracket.col(),
415 format!("Expected opening '{{', found: {}", other_token),
416 )),
417 },
418 None => Err(CompilationError::new(
419 last_line,
420 last_column,
421 String::from("Expected opening '{', found EOF"),
422 )),
423 }
424 }
425
426 /// Parse the known-to-exist function called from within the ISR.
427 ///
428 /// This will be called from `parse_isr_body` if and only if it has peeked
429 /// ahead and determined a call exists (as calls are optional). Thus,
430 /// failures here are still universally treated as hard compilation errors.
431 fn parse_function_call(
432 &mut self,
433 last_line: u32,
434 last_col: u32,
435 ) -> Result<ExternalFunctionCall, CompilationError> {
436 #[derive(Clone, Copy)]
437 enum SeekState {
438 Start,
439 SeekArg,
440 SeekComma,
441 }
442
443 match self.tokens.pop_front() {
444 Some(func) => match func.borrow_token() {
445 Token::Identifier(func_name) => match self.tokens.pop_front() {
446 Some(lparen) => match lparen.borrow_token() {
447 Token::LeftParen => {
448 let mut args: Vec<CallArg> = Vec::new();
449 let mut state: SeekState = SeekState::Start;
450
451 while let Some(token) = self.tokens.pop_front() {
452 match (token.borrow_token(), state) {
453 (
454 Token::RightParen,
455 SeekState::Start | SeekState::SeekComma,
456 ) => break,
457 (Token::RightParen, SeekState::SeekArg) => {
458 return Err(CompilationError::new(
459 token.line(),
460 token.col(),
461 String::from("Expected argument, found ')'"),
462 ));
463 }
464 (Token::Identifier(arg), SeekState::SeekArg) => {
465 args.push(CallArg::Var(arg.clone()));
466 state = SeekState::SeekComma;
467 }
468 (Token::Dollar, SeekState::Start | SeekState::SeekArg) => {
469 args.push(CallArg::Dollar);
470 state = SeekState::SeekComma;
471 }
472 (unexpected, SeekState::Start | SeekState::SeekArg) => {
473 return Err(CompilationError::new(
474 token.line(),
475 token.col(),
476 format!(
477 "Unexpected token in call expression: {}",
478 unexpected
479 ),
480 ));
481 }
482 (Token::Comma, SeekState::SeekComma) => {
483 state = SeekState::SeekArg;
484 }
485 (unexpected, SeekState::SeekComma) => {
486 return Err(CompilationError::new(
487 token.line(),
488 token.col(),
489 format!(
490 "Unexpected token in call expression: {}",
491 unexpected
492 ),
493 ));
494 }
495 }
496 }
497
498 match self.tokens.pop_front() {
499 Some(rbracket) => match rbracket.borrow_token() {
500 Token::RightBracket => {
501 Ok(ExternalFunctionCall::new(func_name.clone(), args))
502 }
503 other => Err(CompilationError::new(
504 rbracket.line(),
505 rbracket.col(),
506 format!("Expected '}}', found: {}", other),
507 )),
508 },
509 None => Err(CompilationError::new(
510 func.line(),
511 func.col(),
512 String::from("Expected '}', found EOF"),
513 )),
514 }
515 }
516 other => Err(CompilationError::new(
517 lparen.line(),
518 lparen.col(),
519 format!("Expected '(', found: {}", other),
520 )),
521 },
522 None => Err(CompilationError::new(
523 func.line(),
524 func.col(),
525 String::from("Expected '(', found EOF"),
526 )),
527 },
528 other => Err(CompilationError::new(
529 func.line(),
530 func.col(),
531 format!("Expected function name, found: {}", other),
532 )),
533 },
534 None => Err(CompilationError::new(
535 last_line,
536 last_col,
537 String::from("Expected function name, found EOF"),
538 )),
539 }
540 }
541}
542
543#[cfg(test)]
544mod tests {
545
546 #[test]
547 fn total_parse() {}
548
549 #[test]
550 fn only_externs() {}
551
552 #[test]
553 fn only_isrs() {}
554}