celestial_hub_compass/parser/
mod.rs

1use ariadne::ReportBuilder;
2use lalrpop_util::{lalrpop_mod, ParseError};
3
4use crate::{
5  ast::{self, context::Context},
6  lexer::{Lexer, LexicalError},
7};
8
9pub struct Parser;
10
11lalrpop_mod!(pub compass_grammar, "/parser/compass_grammar.rs");
12
13impl Parser {
14  pub fn new() -> Self {
15    Self
16  }
17
18  pub fn parse(&self, lexer: Lexer) -> Result<Vec<ast::Statement>, Box<dyn std::error::Error>> {
19    use ariadne::{Color, ColorGenerator, Config, Fmt, Label, Report, ReportKind, Source};
20
21    let mut colors = ColorGenerator::default();
22
23    let filename = lexer.filepath.split('/').last().unwrap();
24    let source = lexer.source_code;
25
26    let mut report: ReportBuilder<(&str, std::ops::Range<usize>)> =
27      Report::build(ReportKind::Error, filename, 12)
28        .with_code(3)
29        .with_config(Config::default().with_tab_width(2))
30        .with_note(format!(
31          "If you think this is a bug, please file an issue at {}",
32          "github.com/celestial-hub/compass/issues".fg(Color::Blue)
33        ));
34
35    // NOTE: I should probably use this context in the codegen module
36    let mut context = Context::new(0);
37    match compass_grammar::ProgramParser::new().parse(&mut context, lexer) {
38      Ok(ast) => Ok(ast),
39      Err(err) => match err {
40        ParseError::InvalidToken { location } => {
41          report
42            .with_message("Invalid token".fg(Color::Red))
43            .with_label(
44              Label::new((filename, location..location))
45                .with_message("Invalid token")
46                .with_color(colors.next()),
47            )
48            .finish()
49            .print((filename, Source::from(source)))
50            .unwrap();
51
52          Err(Box::new(err))
53        }
54        ParseError::User { ref error } => {
55          let (errors, help) = match error {
56            LexicalError::WrongType { error, help } => (error, help),
57            LexicalError::UnknownVariable { error, help } => (error, help),
58            LexicalError::UnknownFunction { error, help } => (error, help),
59            LexicalError::WrongArgumentCount { error, help } => (error, help),
60            LexicalError::FunctionIsBuiltin { error, help } => (error, help),
61            LexicalError::UnusedValue { error, help } => (error, help),
62            LexicalError::InvalidToken => unreachable!(),
63          };
64
65          report = report.with_message("Error".fg(Color::Red));
66
67          for error in errors {
68            report = report.with_label(
69              Label::new((filename, error.location.clone()))
70                .with_message(error.message.clone())
71                .with_color(colors.next()),
72            );
73          }
74
75          if let Some(help) = help {
76            report = report.with_help(help.clone());
77          }
78
79          report
80            .finish()
81            .print((filename, Source::from(source)))
82            .unwrap();
83
84          Err(Box::new(err))
85        }
86        ParseError::UnrecognizedToken {
87          ref token,
88          ref expected,
89        } => {
90          report
91            .with_message("Unrecognized token".fg(Color::Red))
92            .with_label(
93              Label::new((filename, token.0..token.2))
94                .with_message("Unrecognized token")
95                .with_color(colors.next()),
96            )
97            .with_help(format!(
98              "Expected one of the following: {}",
99              expected
100                .iter()
101                .map(|token| {
102                  // Remove surrounding quotes
103                  let token = &token[1..token.len() - 1];
104
105                  format!("{}", token.fg(Color::Yellow))
106                })
107                .collect::<Vec<String>>()
108                .join(", ")
109            ))
110            .finish()
111            .print((filename, Source::from(source)))
112            .unwrap();
113          Err(Box::new(err))
114        }
115        _ => Err(Box::new(err)),
116      },
117    }
118  }
119}
120
121impl Default for Parser {
122  fn default() -> Self {
123    Self::new()
124  }
125}