celestial_hub_compass/parser/
mod.rs1use 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 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 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}