1use std::{mem::{self, Discriminant, MaybeUninit}};
2use crate::lexer::{Token, TokenKind};
3
4#[macro_export]
5macro_rules! token_kind_list {
6 ($str:literal, [ $( $name:ident ),* ]) => {
7 &TokenKindList::new_with_stringified([$(
8 (TokenKind::$name, std::mem::discriminant(&TokenKind::$name))
9 ),*], Stringified::Single(String::from($str)))
10 };
11
12 ($( $name:ident ),*) => {
13 &TokenKindList::new([$(
14 (TokenKind::$name, std::mem::discriminant(&TokenKind::$name))
15 ),*])
16 };
17
18 ([ $( $name:ident ),* ]) => {
19 token_kind_list!($( $name ),*)
20 };
21}
22
23#[derive(Debug)]
24pub struct TokenKindList<const N: usize>{
25 items: [(TokenKind, Discriminant<TokenKind>); N],
26 stringified: Option<Stringified>
27}
28
29#[derive(Debug, Clone)]
30pub enum Stringified {
31 Single(String),
32 Many(Vec<String>)
33}
34
35impl Stringified {
36 fn iter(&self) -> Box<dyn Iterator<Item = &String> + '_> {
37 match self {
38 Stringified::Single(item) => Box::new(std::iter::once(item)),
39 Stringified::Many(items) => Box::new(items.iter())
40 }
41 }
42
43 fn extend(self, extend_with: Stringified) -> Stringified {
44 Self::Many(match (self, extend_with) {
45 (Stringified::Single(a), Stringified::Single(b)) => vec![a, b],
46 (Stringified::Many(mut a), Stringified::Many(b)) => {
47 a.extend(b);
48 a
49 },
50 (Stringified::Many(mut a), Stringified::Single(b)) |
51 (Stringified::Single(b), Stringified::Many(mut a)) => {
52 a.push(b);
53 a
54 }
55 })
56 }
57
58}
59
60impl<'a, const N: usize> TokenKindList<N> {
61 pub fn new(items: [(TokenKind, Discriminant<TokenKind>); N]) -> Self {
62 Self {
63 items,
64 stringified: None
65 }
66 }
67
68 pub fn new_with_stringified(
69 items: [(TokenKind, Discriminant<TokenKind>); N],
70 stringified: Stringified
71 ) -> Self {
72 Self {
73 items,
74 stringified: Some(stringified)
75 }
76 }
77
78 pub fn has_token(&self, token: &Token) -> bool {
79 let discriminant = &token.discriminant();
80
81 self.items.iter().any(|x| &x.1 == discriminant)
82 }
83
84 pub fn has_discriminant(&self, discriminant: &Discriminant<TokenKind>) -> bool {
85 self.items.iter().any(|x| &x.1 == discriminant)
86 }
87
88 pub fn to_string(&'a self) -> Option<String> {
89 let stringified = &self.stringified;
90 let items = self.items;
91
92 let mut iter: Box<dyn Iterator<Item = &str>> =
93 if let Some(stringified) = stringified { Box::new(stringified.iter().map(String::as_str)) }
94 else { Box::new(items.iter().map(|x| x.0.name())) };
95
96 let Some(mut current_item) = iter.next() else { return None };
97
98 let mut msg = format!("{}", current_item);
99
100 let Some(next_item) = iter.next() else { return Some(msg) };
101 current_item = next_item;
102
103 loop {
104 let next_item = iter.next();
105
106 if let Some(next_item) = next_item {
107 let msg_item = format!(", {}", current_item);
108 current_item = next_item;
109 msg += &msg_item;
110
111 } else {
112 let msg_item = format!(" or {}", current_item);
113 msg += &msg_item;
114 break
115 };
116 }
117
118 return Some(msg)
119 }
120
121 pub fn with_stringified(&self, stringified: Stringified) -> Self {
122 Self {
123 items: self.items,
124 stringified: Some(stringified)
125 }
126 }
127
128 pub fn concat<const B: usize>(&self, b: &TokenKindList<B>) -> TokenKindList<{N + B}>
129 where
130 [(); N + B]:
131 {
132 TokenKindList::<{N + B}> {
133 items: array_concat(&self.items, &b.items),
134 stringified: match (&self.stringified, &b.stringified) {
135 (Some(a), Some(b)) => Some(a.clone().extend(b.clone())),
136 (Some(a), None) => Some(a.clone()),
137 (None, Some(b)) => Some(b.clone()),
138 (None, None) => None
139 }
140 }
141 }
142}
143
144fn array_concat<T, const AN: usize, const BN: usize>(a: &[T; AN], b: &[T; BN]) -> [T; AN + BN]
145where T: Copy, [(); AN + BN]: {
146 let mut output: [MaybeUninit<T>; AN + BN] =
147 unsafe { MaybeUninit::uninit().assume_init() };
148
149 for i in 0..AN {
150 output[i] = MaybeUninit::new(a[i]);
151 }
152
153 for i in 0..BN {
154 output[AN + i] = MaybeUninit::new(b[i]);
155 }
156
157 unsafe {
158 mem::transmute_copy::<_, [T; AN + BN]>(&output)
159 }
160}