1use crate::TokenID;
15use clap::value_parser;
16use parlex::{Span, Token};
17use smartstring::alias::String;
18
19#[derive(Debug, Clone)]
54pub enum TokenValue {
55 None,
57
58 Ident(usize),
60
61 Number(i64),
63
64 Comment(String),
66
67 Stat {
69 comments: Vec<String>,
70 value: Option<i64>,
71 },
72}
73
74#[derive(Debug, Clone)]
109pub struct CalcToken {
110 pub token_id: TokenID,
112 pub value: TokenValue,
114 pub span: Option<Span>,
116}
117
118impl CalcToken {
119 pub fn merge_span(&mut self, other_span: &Option<Span>) {
120 match other_span {
121 Some(other_span) => match &mut self.span {
122 Some(my_span) => {
123 *my_span = my_span.merge(other_span);
124 }
125 None => {
126 self.span = Some(*other_span);
127 }
128 },
129 None => (),
130 }
131 }
132
133 pub fn to_statement(&mut self, comment: Option<String>) {
134 self.token_id = TokenID::Stat;
135 match &mut self.value {
136 TokenValue::None => {
137 self.value = TokenValue::Stat {
138 comments: if let Some(comment) = comment {
139 vec![comment]
140 } else {
141 vec![]
142 },
143 value: None,
144 };
145 }
146 TokenValue::Comment(comment2) => {
147 self.value = TokenValue::Stat {
148 comments: if let Some(comment) = comment {
149 vec![comment, std::mem::take(comment2)]
150 } else {
151 vec![std::mem::take(comment2)]
152 },
153 value: None,
154 };
155 }
156 TokenValue::Ident(_) => panic!("unexpected token value in `to_statement`"),
157 TokenValue::Number(value) => {
158 self.value = TokenValue::Stat {
159 comments: if let Some(comment) = comment {
160 vec![comment]
161 } else {
162 vec![]
163 },
164 value: Some(*value),
165 };
166 }
167 TokenValue::Stat { comments, value } => {
168 let mut cs = std::mem::take(comments);
169 if let Some(comment) = comment {
170 cs.insert(0, comment);
171 }
172 self.value = TokenValue::Stat {
173 comments: cs,
174 value: std::mem::take(value),
175 };
176 }
177 }
178 }
179}
180
181impl Token for CalcToken {
182 type TokenID = TokenID;
184
185 fn token_id(&self) -> Self::TokenID {
187 self.token_id
188 }
189
190 fn span(&self) -> Option<Span> {
192 self.span
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use parlex::{Position, span};
200
201 #[test]
202 fn token_value_number_extraction_with_let_else() {
203 let tok = TokenValue::Number(42);
204
205 let TokenValue::Number(n) = tok else {
207 panic!("Expected a numeric token");
208 };
209
210 assert_eq!(n, 42);
211 }
212
213 #[test]
214 #[should_panic(expected = "Expected a numeric token")]
215 fn token_value_number_extraction_should_panic_if_not_number() {
216 let tok = TokenValue::Ident(5);
217
218 let TokenValue::Number(_n) = tok else {
220 panic!("Expected a numeric token");
221 };
222 }
223
224 #[test]
225 fn token_value_ident_stores_symbol_index() {
226 let idx = 7usize;
227 let tok = TokenValue::Ident(idx);
228
229 if let TokenValue::Ident(i) = tok {
230 assert_eq!(i, idx);
231 } else {
232 panic!("Expected Ident token");
233 }
234 }
235
236 #[test]
237 fn token_value_none_matches() {
238 let tok = TokenValue::None;
239 assert!(matches!(tok, TokenValue::None));
240 }
241
242 #[test]
243 fn calc_token_trait_accessors_return_values() {
244 let t = CalcToken {
245 token_id: TokenID::Number,
246 value: TokenValue::Number(99),
247 span: span!(1, 2, 1, 10),
248 };
249
250 assert_eq!(t.token_id(), TokenID::Number);
251 assert_eq!(t.span().unwrap().start.column, 2);
252 }
253
254 #[test]
255 fn calc_token_with_identifier_round_trip() {
256 let t = CalcToken {
257 token_id: TokenID::Ident,
258 value: TokenValue::Ident(5),
259 span: span!(1, 2, 1, 10),
260 };
261
262 assert_eq!(t.token_id(), TokenID::Ident);
263
264 if let TokenValue::Ident(i) = t.value {
265 assert_eq!(i, 5);
266 } else {
267 panic!("Expected TokenValue::Ident");
268 }
269
270 assert_eq!(t.span().unwrap().display(), "span 1:2 to 1:10");
271 }
272
273 #[test]
274 fn calc_token_is_cloneable_and_debuggable() {
275 let t1 = CalcToken {
276 token_id: TokenID::Number,
277 value: TokenValue::Number(-1),
278 span: span!(10, 20, 12, 20),
279 };
280
281 let t2 = t1.clone();
282 assert_eq!(t2.token_id(), t1.token_id());
283 assert_eq!(t2.span().unwrap().display(), "span 10:20 to 12:20");
284
285 let dbg_out = format!("{t1:?}");
286 assert!(dbg_out.contains("CalcToken"));
287 }
288
289 #[test]
290 #[should_panic(expected = "Expected TokenValue::Ident")]
291 fn calc_token_with_identifier_should_panic_if_wrong_kind() {
292 let t = CalcToken {
293 token_id: TokenID::Number,
294 value: TokenValue::Number(0),
295 span: span!(10, 20, 12, 20),
296 };
297
298 if let TokenValue::Ident(_) = t.value {
299 } else {
301 panic!("Expected TokenValue::Ident");
302 }
303 }
304
305 fn sp(sl: usize, sc: usize, el: usize, ec: usize) -> Span {
306 Span::new(Position::new(sl, sc), Position::new(el, ec))
307 }
308
309 fn tok(token_id: TokenID, value: TokenValue, span: Option<Span>) -> CalcToken {
310 CalcToken {
311 token_id,
312 value,
313 span,
314 }
315 }
316
317 #[test]
319 fn merge_span_expands_existing_span_to_cover_both() {
320 let mut t = tok(
321 TokenID::Number,
322 TokenValue::Number(1),
323 Some(sp(0, 5, 0, 10)),
324 );
325 let other = Some(sp(0, 2, 0, 12));
326
327 t.merge_span(&other);
328
329 let m = t.span.unwrap();
330 assert_eq!(m.start, Position::new(0, 2));
331 assert_eq!(m.end, Position::new(0, 12));
332 }
333
334 #[test]
336 fn merge_span_sets_when_self_is_none() {
337 let mut t = tok(TokenID::Ident, TokenValue::Ident(0), None);
338 let new_span = Some(sp(1, 0, 1, 3));
339
340 t.merge_span(&new_span);
341
342 assert_eq!(t.span, new_span);
343 }
344
345 #[test]
347 fn merge_span_is_noop_when_other_is_none() {
348 let before = Some(sp(2, 4, 2, 9));
349 let mut t = tok(TokenID::Number, TokenValue::Number(0), before);
350
351 t.merge_span(&None);
352
353 assert_eq!(t.span, before);
354 }
355
356 #[test]
358 fn merge_span_both_none_remains_none() {
359 let mut t = tok(TokenID::Number, TokenValue::Number(0), None);
360
361 t.merge_span(&None);
362
363 assert!(t.span.is_none());
364 }
365
366 #[test]
368 fn merge_span_other_within_self_no_change() {
369 let mut t = tok(
370 TokenID::Number,
371 TokenValue::Number(0),
372 Some(sp(5, 2, 5, 10)),
373 );
374 let inner = Some(sp(5, 4, 5, 7)); t.merge_span(&inner);
377
378 assert_eq!(t.span, Some(sp(5, 2, 5, 10)));
379 }
380
381 #[test]
383 fn merge_span_cross_line_expands() {
384 let mut t = tok(TokenID::Ident, TokenValue::Ident(1), Some(sp(1, 5, 2, 3)));
386 let other = Some(sp(0, 9, 3, 1));
387
388 t.merge_span(&other);
389
390 let m = t.span.unwrap();
391 assert_eq!(m.start, Position::new(0, 9));
392 assert_eq!(m.end, Position::new(3, 1));
393 }
394}