1use std::collections::HashMap;
4
5use rand::{RngCore, seq::IndexedRandom};
6
7#[derive(Debug, PartialEq, Clone, Copy)]
9pub enum Keyword {
10 And,
12 False,
14 True,
16 ForLoopInit,
18 ConditionalCheck,
20 ConditionalElse,
22 EmptyValue,
24 Or,
26 Print,
28 VariableDeclaration,
30 WhileLoopInit,
32
33 OpenBrace,
35 CloseBrace,
37
38 Equal,
40 Greater,
42 GreaterEqual,
44 EqualEqual,
46 BangEqual,
48 Bang,
50 Less,
52 LessEqual,
54}
55
56const EQUAL_VARIANTS: &[&str] = &["=", "equals", "is"];
58const GREATER_VARIANTS: &[&str] = &[">", "gt", "greater"];
60const GREATEREQUAL_VARIANTS: &[&str] = &[">=", "gte"];
62const EQUALEQUAL_VARIANTS: &[&str] = &["==", "equals"];
64const BANGEQUAL_VARIANTS: &[&str] = &["!=", "inequal"];
66const BANG_VARIANTS: &[&str] = &["!", "not"];
68const LESS_VARIANTS: &[&str] = &["<", "lt", "less"];
70const LESSEQUAL_VARIANTS: &[&str] = &["<=", "lte"];
72
73const OPEN_BRACE_VARIANTS: &[&str] = &["{", ":"];
75const CLOSE_BRACE_VARIANTS: &[&str] = &["}", "end"];
77
78const AND_VARIANTS: &[&str] = &["and", "&&", r#"/\"#, "alongside"];
80const OR_VARIANTS: &[&str] = &["or", "||", r#"\/"#];
82
83const FALSE_VARIANTS: &[&str] = &["false", "False", "FALSE", "incorrect", "nah", ":("];
85const TRUE_VARIANTS: &[&str] = &["true", "True", "TRUE", "correct", "yah", ":)"];
87
88const FOR_VARIANTS: &[&str] = &["for", "each"];
90const IF_VARIANTS: &[&str] = &["if", "case", "check", "cond"];
92const ELSE_VARIANTS: &[&str] = &["else", "then", "otherwise"];
94const EMPTY_VARIANTS: &[&str] = &["nil", "None", "null", "NULL", "undefined", "void"];
96
97const PRINT_VARIANTS: &[&str] = &[
99 "print",
100 "puts",
101 "echo",
102 "Console.WriteLine",
103 "System.out.println",
104 "println",
105 "fmt.Println",
106 "console.log",
107 "say",
108];
109
110const WHILE_VARIANTS: &[&str] = &["while", "during", "whilst", "until", "as_long_as"];
112
113const VARIABLE_DECLARATION_VARIANTS: &[&str] = &["var", "let", "auto", "$", "val", "new"];
115
116impl Keyword {
117 pub fn all() -> Vec<(Keyword, &'static [&'static str])> {
119 vec![
120 (Self::And, AND_VARIANTS),
121 (Self::Or, OR_VARIANTS),
122 (Self::True, TRUE_VARIANTS),
123 (Self::False, FALSE_VARIANTS),
124 (Self::ForLoopInit, FOR_VARIANTS),
125 (Self::ConditionalCheck, IF_VARIANTS),
126 (Self::ConditionalElse, ELSE_VARIANTS),
127 (Self::EmptyValue, EMPTY_VARIANTS),
128 (Self::Print, PRINT_VARIANTS),
129 (Self::WhileLoopInit, WHILE_VARIANTS),
130 (Self::VariableDeclaration, VARIABLE_DECLARATION_VARIANTS),
131 (Self::OpenBrace, OPEN_BRACE_VARIANTS),
132 (Self::CloseBrace, CLOSE_BRACE_VARIANTS),
133 (Self::Equal, EQUAL_VARIANTS),
134 (Self::Greater, GREATER_VARIANTS),
135 (Self::GreaterEqual, GREATEREQUAL_VARIANTS),
136 (Self::EqualEqual, EQUALEQUAL_VARIANTS),
137 (Self::BangEqual, BANGEQUAL_VARIANTS),
138 (Self::Bang, BANG_VARIANTS),
139 (Self::Less, LESS_VARIANTS),
140 (Self::LessEqual, LESSEQUAL_VARIANTS),
141 ]
142 }
143}
144
145#[derive(Debug)]
149pub struct KeywordRandomizer {
150 ctx: HashMap<&'static str, Keyword>,
152}
153
154impl KeywordRandomizer {
155 pub fn try_parse(&self, stream: &str, idx: usize, len: &mut usize) -> Option<Keyword> {
157 let characters: Vec<_> = stream.chars().collect();
158 let mut idx2 = idx;
159
160 while !characters[idx2].is_whitespace() && idx2 < characters.len() - 1 {
161 idx2 += 1
162 }
163
164 while idx <= idx2 {
165 let pot_kwrd = &stream[idx..=idx2];
166 if let Ok(keyword) = self.try_from_str(pot_kwrd) {
167 *len = idx2 - idx + 1;
168 return Some(keyword);
169 }
170
171 if idx2 == 0 {
172 break;
173 }
174
175 idx2 -= 1;
176 }
177
178 None
179 }
180
181 pub fn seeded_start<RNG: RngCore>(rng: &mut RNG) -> Self {
183 let mut ctx = HashMap::new();
184
185 for (root, variants) in Keyword::all() {
186 let selected = variants.choose(rng).unwrap();
188 ctx.insert(*selected, root);
189 }
190
191 Self { ctx }
192 }
193
194 pub fn try_from_str(&self, from: &str) -> Result<Keyword, Option<&'static str>> {
197 if let Some(keyword) = self.ctx.get(from) {
198 Ok(*keyword)
199 } else {
200 for (root, variants) in Keyword::all() {
201 if variants.contains(&from) {
202 if let Some(proper_variant) = self
203 .ctx
204 .iter()
205 .find(|(_, kwrd)| **kwrd == root)
206 .map(|(var, _)| *var)
207 {
208 return Err(Some(proper_variant));
209 }
210 }
211 }
212
213 Err(None)
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use rand::SeedableRng;
221 use rand_chacha::ChaCha8Rng;
222
223 use crate::tokenizer::keyword::Keyword;
224
225 use super::KeywordRandomizer;
226
227 #[test]
228 fn print() {
229 let mut rng = ChaCha8Rng::seed_from_u64(42);
230 let keywords = KeywordRandomizer::seeded_start(&mut rng);
231
232 let attempt = keywords.try_from_str("fmt.Println");
233
234 assert_eq!(attempt.unwrap(), Keyword::Print)
235 }
236
237 #[test]
238 fn try_from_string() {
239 let mut rng = ChaCha8Rng::seed_from_u64(42);
240 let keywords = KeywordRandomizer::seeded_start(&mut rng);
241
242 let attempt = keywords.try_from_str("var");
243
244 assert_eq!(attempt.unwrap_err(), Some("$"))
245 }
246
247 #[test]
248 fn try_parse_bottom_up() {
249 let mut rng = ChaCha8Rng::seed_from_u64(42);
250 let mut keywords = KeywordRandomizer::seeded_start(&mut rng);
251 let mut len = 0;
252 keywords.ctx.insert(">=", Keyword::GreaterEqual);
253
254 let attempt = keywords.try_parse(">=", 0, &mut len);
255 assert_eq!(attempt.unwrap(), Keyword::GreaterEqual);
256 assert_eq!(len, 2);
257
258 let attempt = keywords.try_parse(" >=", 1, &mut len);
259 assert_eq!(attempt.unwrap(), Keyword::GreaterEqual);
260 assert_eq!(len, 2);
261
262 let attempt = keywords.try_parse(" > =", 1, &mut len);
263 assert_eq!(attempt.unwrap(), Keyword::Greater);
264 assert_eq!(len, 1);
265
266 let attempt = keywords.try_parse(" > =", 3, &mut len);
267 assert_eq!(attempt.unwrap(), Keyword::Equal);
268 assert_eq!(len, 1);
269 }
270}