celestial_hub_compass/lexer/
mod.rs

1use 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}