1use crate::parser::ast::{BinaryOp, LogicalOp, UnaryOp};
2use miette::{miette, LabeledSpan, SourceSpan};
3use std::collections::HashMap;
4use std::fmt;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, PartialEq)]
8pub enum TokenType {
9 SoftSemi,
11
12 LeftParen,
14 RightParen,
15 LeftBracket,
16 RightBracket,
17 LeftBrace,
18 RightBrace,
19 Comma,
20 Dot,
21 Minus,
22 Plus,
23 Slash,
24 Star,
25
26 Arrow,
28 EqualEqual,
29 BangEqual,
30 Greater,
31 GreaterEqual,
32 Less,
33 LessEqual,
34
35 Identifier,
37 Number,
38 StringLiteral,
39
40 Mod,
42 If,
43 Else,
44 Repeat,
45 Times,
46 Until,
47 For,
48 Each,
49 Continue,
50 Break,
51 In,
52 Procedure,
53 Return,
54 Not,
55 And,
56 Or,
57
58 True,
60 False,
61 Null,
62
63 Import,
65 Export,
66 From,
67
68 Eof,
69}
70
71#[derive(Debug, Clone)]
72pub struct Token {
73 pub token_type: TokenType,
74 pub lexeme: String,
75 pub literal: Option<LiteralValue>,
76 pub span: SourceSpan,
77 pub line_number: usize,
78 pub source: Arc<str>,
79}
80
81impl fmt::Display for Token {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "{} ", self.lexeme)
85 }
86}
87
88impl Token {
89 pub fn debug_many(tokens: &Vec<Token>) -> String {
90 let string: Vec<String> = tokens.iter().map(|t| format!("{t}")).collect();
91 format!("[{}]", string.join(", "))
92 }
93}
94
95impl Token {
96 pub fn is_soft_semi(&self) -> bool {
97 matches!(self.token_type, TokenType::SoftSemi)
98 }
99}
100
101pub fn get_keywords_hashmap() -> HashMap<&'static str, TokenType> {
102 use mapro::map;
103 use TokenType::*;
104 map! {
105 "mod" => Mod, "MOD" => Mod,
106 "if" => If, "IF" => If,
107 "else" => Else, "ELSE" => Else,
108 "repeat" => Repeat, "REPEAT" => Repeat,
109 "times" => Times, "TIMES" => Times,
110 "until" => Until, "UNTIL" => Until,
111 "for" => For, "FOR" => For,
112 "each" => Each, "EACH" => Each,
113 "continue" => Continue, "CONTINUE" => Continue,
114 "break" => Break, "BREAK" => Break,
115 "in" => In, "IN" => In,
116 "procedure" => Procedure, "PROCEDURE" => Procedure,
117 "return" => Return, "RETURN" => Return,
118 "not" => Not, "NOT" => Not,
119 "and" => And, "AND" => And,
120 "or" => Or, "OR" => Or,
121 "true" => True, "TRUE" => True,
122 "false" => False, "FALSE" => False,
123 "null" => Null, "NULL" => Null,
124 "import" => Import, "IMPORT" => Import,
125 "export" => Export, "EXPORT" => Export,
126 "from" => From, "FROM" => From,
127 }
128}
129
130impl Token {
131 pub fn token_type(&self) -> &TokenType {
132 &self.token_type
133 }
134 pub fn label(&self, label: impl Into<String>) -> LabeledSpan {
135 LabeledSpan::at(self.span, label)
136 }
137
138 pub fn span_to_label(&self, other: SourceSpan, label: impl Into<String>) -> LabeledSpan {
139 LabeledSpan::at(self.span_to(other), label)
140 }
141
142 pub fn span(&self) -> SourceSpan {
143 self.span
144 }
145
146 pub fn span_to(&self, other: SourceSpan) -> SourceSpan {
147 join_spans(self.span(), other)
148 }
149
150 pub fn span_until(&self, other: SourceSpan) -> SourceSpan {
151 span_between(self.span(), other)
152 }
153
154 pub fn span_to_token(&self, other: &Token) -> SourceSpan {
155 self.span_to(other.span())
156 }
157
158 pub fn span_until_token(&self, other: &Token) -> SourceSpan {
159 self.span_until(other.span())
160 }
161}
162
163pub fn join_spans(left: SourceSpan, right: SourceSpan) -> SourceSpan {
164 let length = right.offset() - left.offset() + right.len();
165 SourceSpan::from(left.offset()..length)
166}
167
168pub fn span_between(left: SourceSpan, right: SourceSpan) -> SourceSpan {
169 SourceSpan::from(left.offset() + left.len()..right.offset())
170}
171
172impl Token {
173 pub fn to_binary_op(&self) -> miette::Result<BinaryOp> {
174 match self.token_type {
175 TokenType::EqualEqual => Ok(BinaryOp::EqualEqual),
176 TokenType::BangEqual => Ok(BinaryOp::NotEqual),
177 TokenType::Less => Ok(BinaryOp::Less),
178 TokenType::LessEqual => Ok(BinaryOp::LessEqual),
179 TokenType::Greater => Ok(BinaryOp::Greater),
180 TokenType::GreaterEqual => Ok(BinaryOp::GreaterEqual),
181 TokenType::Plus => Ok(BinaryOp::Plus),
182 TokenType::Minus => Ok(BinaryOp::Minus),
183 TokenType::Star => Ok(BinaryOp::Star),
184 TokenType::Slash => Ok(BinaryOp::Slash),
185 TokenType::Mod => Ok(BinaryOp::Modulo),
186 _ => Err(miette!(
188 "Conversion to Binary Op Error, Token is not binary Op"
189 )),
190 }
191 }
192
193 pub fn to_unary_op(&self) -> miette::Result<UnaryOp> {
194 match self.token_type {
195 TokenType::Minus => Ok(UnaryOp::Minus),
196 TokenType::Not => Ok(UnaryOp::Not),
197 _ => Err(miette!(
199 "Conversion to Binary Unary Error, Token is not Unary op"
200 )),
201 }
202 }
203
204 pub fn to_logical_op(&self) -> miette::Result<LogicalOp> {
205 match self.token_type {
206 TokenType::Or => Ok(LogicalOp::Or),
207 TokenType::And => Ok(LogicalOp::And),
208 _ => Err(miette!(
210 "Conversion to Binary Logical Error, Token is not Logical op"
211 )),
212 }
213 }
214}
215
216#[derive(Debug, Clone, PartialEq)]
217pub enum LiteralValue {
218 Number(f64),
219 String(String),
220}
221
222impl TryInto<f64> for LiteralValue {
223 type Error = String;
224
225 fn try_into(self) -> miette::Result<f64, Self::Error> {
226 let Self::Number(num) = self else {
227 return Err(
228 "Trying to convert to number when literal is not of type number".to_string(),
229 );
230 };
231
232 Ok(num)
233 }
234}
235
236impl TryInto<String> for LiteralValue {
237 type Error = String;
238
239 fn try_into(self) -> miette::Result<String, Self::Error> {
240 let Self::String(string) = self else {
241 return Err(
242 "Trying to convert to string when literal is not of type string".to_string(),
243 );
244 };
245
246 Ok(string)
247 }
248}