celestial_hub_compass/lexer/
mod.rs1use logos::{Logos, SpannedIter};
2
3use self::tokens::Token;
4
5pub mod tokens;
6pub mod traits;
7pub mod types;
8
9pub struct Lexer<'input> {
10 pub token_stream: SpannedIter<'input, Token>,
11 pub filepath: &'input str,
12 pub source_code: &'input str,
13}
14
15#[derive(Debug)]
16pub struct ErrorTip {
17 pub message: String,
18 pub location: std::ops::Range<usize>,
19}
20
21#[derive(Debug)]
22pub enum LexicalError {
23 InvalidToken,
24 WrongType {
25 error: Vec<ErrorTip>,
26 help: Option<String>,
27 },
28 UnknownVariable {
29 error: Vec<ErrorTip>,
30 help: Option<String>,
31 },
32 UnknownFunction {
33 error: Vec<ErrorTip>,
34 help: Option<String>,
35 },
36 WrongArgumentCount {
37 error: Vec<ErrorTip>,
38 help: Option<String>,
39 },
40 FunctionIsBuiltin {
41 error: Vec<ErrorTip>,
42 help: Option<String>,
43 },
44 UnusedValue {
45 error: Vec<ErrorTip>,
46 help: Option<String>,
47 },
48}
49
50impl<'input> Lexer<'input> {
51 pub fn new(source_code: &'input str, filepath: &'input str) -> Result<Self, LexicalError> {
52 let lexer = Self {
53 token_stream: Token::lexer(source_code).spanned(),
54 filepath,
55 source_code,
56 };
57
58 lexer.validate()?;
59
60 Ok(lexer)
61 }
62}
63
64impl<'input> Lexer<'input> {
65 fn validate(&self) -> Result<(), LexicalError> {
66 let token_stream = self.token_stream.clone();
67 let mut error = false;
68
69 let filename = self.filepath.split('/').last().unwrap();
70
71 for (token, span) in token_stream.spanned() {
72 if token.is_err() {
73 use ariadne::{Color, ColorGenerator, Config, Fmt, Label, Report, ReportKind, Source};
74
75 let mut colors = ColorGenerator::default();
76 let color = colors.next();
77
78 Report::build(ReportKind::Error, filename, 12)
79 .with_code(3)
80 .with_config(Config::default().with_tab_width(2))
81 .with_message("Invalid token".fg(Color::Red))
82 .with_label(
83 Label::new((filename, span))
84 .with_message("Invalid token")
85 .with_color(color),
86 )
87 .with_help("You probably added a character that is not allowed in the language")
88 .with_note(format!(
89 "If you think this is a bug, please file an issue at {}",
90 "github.com/celestial-hub/compass/issues".fg(Color::Blue)
91 ))
92 .finish()
93 .print((filename, Source::from(self.source_code)))
94 .unwrap();
95
96 error = true;
97 }
98 }
99
100 if error {
101 return Err(LexicalError::InvalidToken);
102 }
103
104 Ok(())
105 }
106}