Skip to main content

tardis_cli/parser/
token.rs

1//! Token types and span tracking for the TARDIS lexer.
2
3/// Byte offset range into the original input string.
4#[must_use]
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct ByteSpan {
7    pub start: usize,
8    pub end: usize,
9}
10
11/// A token paired with its source position.
12#[must_use]
13#[derive(Debug, Clone, PartialEq)]
14pub struct SpannedToken {
15    pub kind: Token,
16    pub span: ByteSpan,
17}
18
19/// Temporal duration unit.
20#[non_exhaustive]
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum TemporalUnit {
23    Year,
24    Month,
25    Week,
26    Day,
27    Hour,
28    Minute,
29    Second,
30}
31
32/// Epoch timestamp precision levels.
33#[non_exhaustive]
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum EpochPrecision {
36    Seconds,
37    Milliseconds,
38    Microseconds,
39    Nanoseconds,
40}
41
42/// Boundary keywords for period start/end.
43///
44/// Current period (so/eo = start-of / end-of),
45/// previous period (sop/eop), and next period (son/eon).
46#[non_exhaustive]
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum BoundaryKind {
49    Sod,
50    Eod,
51    Sow,
52    Eow,
53    Soww,
54    Eoww,
55    Som,
56    Eom,
57    Soq,
58    Eoq,
59    Soy,
60    Eoy,
61    Sopd,
62    Eopd,
63    Sopw,
64    Eopw,
65    Sopm,
66    Eopm,
67    Sopq,
68    Eopq,
69    Sopy,
70    Eopy,
71    Sond,
72    Eond,
73    Sonw,
74    Eonw,
75    Sonm,
76    Eonm,
77    Sonq,
78    Eonq,
79    Sony,
80    Eony,
81}
82
83/// Lexer token types.
84///
85/// Keywords are simple enum variants with zero heap allocation.
86/// Only `Word(String)` carries owned data (for unrecognized words in error messages).
87#[non_exhaustive]
88#[derive(Debug, Clone, PartialEq)]
89pub enum Token {
90    Now,
91    Today,
92    Tomorrow,
93    Yesterday,
94    Overmorrow,
95    Ereyesterday,
96    Next,
97    Last,
98    This,
99    In,
100    Ago,
101    From,
102    A,
103    An,
104    At,
105    And,
106    Am,
107    Pm,
108    Unit(TemporalUnit),
109    Weekday(jiff::civil::Weekday),
110    Month(i8),
111    Number(i64),
112    Colon,
113    Dash,
114    Slash,
115    AtSign,
116    Plus,
117    After,
118    Before,
119    Quarter(i8),
120    Boundary(BoundaryKind),
121    EpochSuffix(EpochPrecision),
122    Word(String),
123}
124
125#[cfg(test)]
126mod tests {
127    #![allow(clippy::unwrap_used, clippy::expect_used)]
128    use super::*;
129
130    #[test]
131    fn bytespan_equality() {
132        let a = ByteSpan { start: 0, end: 5 };
133        let b = ByteSpan { start: 0, end: 5 };
134        assert_eq!(a, b);
135    }
136
137    #[test]
138    fn token_variants_no_heap_alloc() {
139        let t = Token::Now;
140        assert_eq!(t, Token::Now);
141        let t2 = Token::Unit(TemporalUnit::Day);
142        assert_eq!(t2, Token::Unit(TemporalUnit::Day));
143    }
144
145    #[test]
146    fn boundary_kind_equality() {
147        assert_eq!(BoundaryKind::Sod, BoundaryKind::Sod);
148        assert_ne!(BoundaryKind::Sod, BoundaryKind::Eod);
149    }
150
151    #[test]
152    fn boundary_token_construction() {
153        assert_eq!(
154            Token::Boundary(BoundaryKind::Eod),
155            Token::Boundary(BoundaryKind::Eod)
156        );
157        assert_ne!(
158            Token::Boundary(BoundaryKind::Eod),
159            Token::Boundary(BoundaryKind::Sod)
160        );
161    }
162
163    #[test]
164    fn spanned_token_construction() {
165        let st = SpannedToken {
166            kind: Token::Number(42),
167            span: ByteSpan { start: 0, end: 2 },
168        };
169        assert_eq!(st.kind, Token::Number(42));
170        assert_eq!(st.span.start, 0);
171    }
172}