Skip to main content

celestial_hub_astrolabe/parser/
mod.rs

1use ariadne::ReportBuilder;
2use lalrpop_util::{lalrpop_mod, ParseError};
3
4use crate::{ast, lexer::Lexer};
5
6pub struct Parser;
7
8lalrpop_mod!(pub astrolabe_grammar, "/parser/grammar.rs");
9
10impl Parser {
11  pub fn new() -> Self {
12    Self
13  }
14
15  pub fn parse(&self, lexer: Lexer) -> Result<ast::Program, Box<dyn std::error::Error>> {
16    use ariadne::{Color, ColorGenerator, Config, Fmt, Label, Report, ReportKind, Source};
17
18    let mut colors = ColorGenerator::default();
19
20    let filename = lexer.filepath.split('/').last().unwrap();
21    let source = lexer.source_code;
22
23    let report: ReportBuilder<(&str, std::ops::Range<usize>)> =
24      Report::build(ReportKind::Error, filename, 12)
25        .with_code(3)
26        .with_config(Config::default().with_tab_width(2))
27        .with_note(format!(
28          "If you think this is a bug, please file an issue at {}",
29          "github.com/celestial-hub/compass/issues".fg(Color::Blue)
30        ));
31
32    match astrolabe_grammar::ProgramParser::new().parse(lexer) {
33      Ok(ast) => Ok(ast),
34      Err(err) => match err {
35        ParseError::InvalidToken { location } => {
36          report
37            .with_message("Invalid token".fg(Color::Red))
38            .with_label(
39              Label::new((filename, location..location))
40                .with_message("Invalid token")
41                .with_color(colors.next()),
42            )
43            .finish()
44            .print((filename, Source::from(source)))
45            .unwrap();
46
47          Err(Box::new(err))
48        }
49        ParseError::User { .. } => Err(Box::new(err)),
50        ParseError::UnrecognizedToken {
51          ref token,
52          ref expected,
53        } => {
54          report
55            .with_message("Unrecognized token".fg(Color::Red))
56            .with_label(
57              Label::new((filename, token.0..token.2))
58                .with_message("Unrecognized token")
59                .with_color(colors.next()),
60            )
61            .with_help(format!(
62              "Expected one of the following: {}",
63              expected
64                .iter()
65                .map(|token| {
66                  // Remove surrounding quotes
67                  let token = &token[1..token.len() - 1];
68
69                  format!("{}", token.fg(Color::Yellow))
70                })
71                .collect::<Vec<String>>()
72                .join(", ")
73            ))
74            .finish()
75            .print((filename, Source::from(source)))
76            .unwrap();
77          Err(Box::new(err))
78        }
79        _ => Err(Box::new(err)),
80      },
81    }
82  }
83}
84
85impl Default for Parser {
86  fn default() -> Self {
87    Self::new()
88  }
89}