1use std::{collections::HashSet, ops::RangeInclusive, option, string};
2
3use specmc_base::{
4 ensure_tokens,
5 parse::{Identifier, Literal, Parse, ParseError},
6};
7use strtoint::strtoint;
8
9use crate::types::Type;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub enum IntegerType {
13 U8,
14 U16,
15 U32,
16 U64,
17 I8,
18 I16,
19 I32,
20 I64,
21 VarInt,
22 VarLong,
23}
24impl IntegerType {
25 pub fn range(&self) -> RangeInclusive<isize> {
26 macro_rules! range {
27 ($ty:ty) => {
28 <$ty>::MIN as isize..=<$ty>::MAX as isize
29 };
30 }
31
32 use IntegerType::*;
33 match self {
34 U8 => range!(u8),
35 U16 => range!(u16),
36 U32 => range!(u32),
37 U64 => range!(u64),
38 I8 => range!(i8),
39 I16 => range!(i16),
40 I32 | VarInt => range!(i32),
41 I64 | VarLong => range!(i64),
42 }
43 }
44
45 pub fn check(&self, value: isize) -> bool {
46 self.range().contains(&value)
47 }
48}
49impl Parse for IntegerType {
50 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
51 use IntegerType::*;
52 match tokens.pop().ok_or(ParseError::EndOfFile)?.as_str() {
53 "u8" => Ok(U8),
54 "u16" => Ok(U16),
55 "u32" => Ok(U32),
56 "u64" => Ok(U64),
57 "i8" => Ok(I8),
58 "i16" => Ok(I16),
59 "i32" => Ok(I32),
60 "i64" => Ok(I64),
61 "VarInt" => Ok(VarInt),
62 "VarLong" => Ok(VarLong),
63 token => {
64 tokens.push(token.to_string());
65 Err(ParseError::InvalidToken {
66 token: token.to_string(),
67 error: "Invalid integer type".to_string(),
68 })
69 }
70 }
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Hash)]
75pub enum BaseType {
76 Bool,
77 Integer(IntegerType),
78 F32,
79 F64,
80 String {
81 length: Option<usize>,
82 },
83 List {
84 ty: Box<Type>,
85 length: Option<usize>,
87 },
88 Nbt,
89 }
91impl Parse for BaseType {
92 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
93 use BaseType::*;
94 match tokens.pop().ok_or(ParseError::EndOfFile)?.as_str() {
95 "bool" => Ok(Bool),
96 "f32" => Ok(F32),
97 "f64" => Ok(F64),
98 "String" => {
99 let mut length: option::Option<usize> = None;
100
101 if !tokens.is_empty() && tokens.last().unwrap() == "[" {
102 tokens.pop();
103 let _length: Literal = Literal::parse(tokens)?;
104 let Literal::Integer(_length) = _length else {
105 return Err(ParseError::InvalidToken {
106 token: format!("{_length:?}"),
107 error: "Invalid list length".to_string(),
108 });
109 };
110 length = Some(_length as usize);
111 ensure_tokens!(tokens, "]");
112 }
113
114 Ok(String { length })
115 }
116 "List" => {
117 ensure_tokens!(tokens, "[");
118 let ty: Box<Type> = Box::new(Type::parse(tokens)?);
119 let mut length: option::Option<usize> = None;
120 if !tokens.is_empty() && tokens.last().unwrap() == ";" {
121 tokens.pop();
122 let _length: string::String = tokens.pop().ok_or(ParseError::EndOfFile)?;
123 length = Some(strtoint(&_length).map_err(|_| ParseError::InvalidToken {
124 token: _length,
125 error: "Invalid list length".to_string(),
126 })?);
127 }
128 ensure_tokens!(tokens, "]");
129 Ok(List { ty, length })
130 }
131 "Nbt" => Ok(Nbt),
132 token => {
139 tokens.push(token.to_string());
140 Ok(Integer(IntegerType::parse(tokens).map_err(|_| {
141 ParseError::InvalidToken {
142 token: token.to_string(),
143 error: "Invalid type".to_string(),
144 }
145 })?))
146 }
147 }
148 }
149}
150
151#[derive(Debug, Clone, PartialEq)]
152pub enum Value {
153 Length(Identifier),
155 Literal(Literal),
157 Identifier(Identifier),
159}
160impl Parse for Value {
161 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
162 if tokens.last().ok_or(ParseError::EndOfFile)? == "len" {
163 tokens.pop();
164 ensure_tokens!(tokens, "(");
165 let length: Identifier = Identifier::parse(tokens)?;
166 ensure_tokens!(tokens, ")");
167 Ok(Value::Length(length))
168 } else if let Ok(literal) = Literal::parse(tokens) {
169 Ok(Value::Literal(literal))
170 } else {
171 Ok(Value::Identifier(Identifier::parse(tokens)?))
172 }
173 }
174}
175
176#[derive(Debug, Clone, PartialEq)]
177pub struct Field {
178 pub ty: Type,
179 pub name: Identifier,
180 pub value: Option<Value>,
181 pub conditions: HashSet<String>,
182}
183impl Parse for Field {
184 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
185 let ty: Type = Type::parse(tokens)?;
186 let name: Identifier = Identifier::parse(tokens)?;
187 let mut value: Option<Value> = None;
188
189 if !tokens.is_empty() && tokens.last().unwrap() == "=" {
190 tokens.pop();
191 value = Some(Value::parse(tokens)?);
192 }
193
194 Ok(Field {
195 name,
196 ty,
197 value,
198 conditions: HashSet::new(),
199 })
200 }
201}
202
203#[derive(Debug, Clone, PartialEq)]
204pub struct FieldList(pub Vec<Field>);
205impl Parse for FieldList {
206 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
207 let mut value: Vec<Field> = vec![];
208 let mut conditions: Vec<String> = vec![];
209 let mut bracket_count: usize = 0;
210 while !tokens.is_empty() {
211 match tokens.pop().unwrap().as_str() {
212 "}" => {
213 if bracket_count == 0 {
214 tokens.push("}".to_string());
215 break;
216 }
217 bracket_count -= 1;
218 conditions.pop();
219 }
220 "if" => {
221 ensure_tokens!(tokens, "(");
222
223 let mut condition: String = "".to_string();
224 let mut paren_count: usize = 1;
225 while !tokens.is_empty() && paren_count != 0 {
226 if tokens.last().unwrap() == "(" {
227 paren_count += 1;
228 } else if tokens.last().unwrap() == ")" {
229 paren_count -= 1;
230 } else {
231 if !condition.is_empty() {
232 condition += " ";
233 }
234 condition += &tokens.last().unwrap();
235 }
236 tokens.pop();
237 }
238 conditions.push(condition);
239
240 ensure_tokens!(tokens, "{");
241 bracket_count += 1;
242 }
243 token => {
244 tokens.push(token.to_string());
245 let mut field: Field = Field::parse(tokens)?;
246
247 if !conditions.is_empty() {
248 field.conditions = HashSet::from_iter(conditions.clone());
249 }
250
251 value.push(field);
252 }
253 }
254 }
255
256 Ok(FieldList(value))
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use specmc_base::tokenize;
263
264 use crate::test_parse;
265
266 use super::*;
267
268 #[test]
269 fn test_integer_type() {
270 let mut tokens: Vec<String> =
271 tokenize!("u8 u16 u32 u64 i8 i16 i32 i64 VarInt VarLong Unknown");
272
273 test_parse!(tokens, IntegerType, Ok(IntegerType::U8));
274 test_parse!(tokens, IntegerType, Ok(IntegerType::U16));
275 test_parse!(tokens, IntegerType, Ok(IntegerType::U32));
276 test_parse!(tokens, IntegerType, Ok(IntegerType::U64));
277 test_parse!(tokens, IntegerType, Ok(IntegerType::I8));
278 test_parse!(tokens, IntegerType, Ok(IntegerType::I16));
279 test_parse!(tokens, IntegerType, Ok(IntegerType::I32));
280 test_parse!(tokens, IntegerType, Ok(IntegerType::I64));
281 test_parse!(tokens, IntegerType, Ok(IntegerType::VarInt));
282 test_parse!(tokens, IntegerType, Ok(IntegerType::VarLong));
283
284 test_parse!(
285 tokens,
286 IntegerType,
287 Err(ParseError::InvalidToken {
288 token: "Unknown".to_string(),
289 error: "Invalid integer type".to_string(),
290 })
291 );
292 assert_eq!(tokens.pop().unwrap(), "Unknown");
293 assert!(tokens.is_empty());
294 test_parse!(tokens, IntegerType, Err(ParseError::EndOfFile));
295 }
296
297 #[test]
298 fn test_base_type() {
299 let mut tokens: Vec<String> =
300 tokenize!("bool VarInt f32 f64 String String[42] List[i32] List[u8; 42] Nbt Unknown");
301
302 test_parse!(tokens, BaseType, Ok(BaseType::Bool));
303 test_parse!(tokens, BaseType, Ok(BaseType::Integer(IntegerType::VarInt)));
304 test_parse!(tokens, BaseType, Ok(BaseType::F32));
305 test_parse!(tokens, BaseType, Ok(BaseType::F64));
306 test_parse!(tokens, BaseType, Ok(BaseType::String { length: None }));
307 test_parse!(tokens, BaseType, Ok(BaseType::String { length: Some(42) }));
308 test_parse!(
309 tokens,
310 BaseType,
311 Ok(BaseType::List {
312 ty: Box::new(Type::BaseType(BaseType::Integer(IntegerType::I32))),
313 length: None,
314 })
315 );
316 test_parse!(
317 tokens,
318 BaseType,
319 Ok(BaseType::List {
320 ty: Box::new(Type::BaseType(BaseType::Integer(IntegerType::U8))),
321 length: Some(42),
322 })
323 );
324 test_parse!(tokens, BaseType, Ok(BaseType::Nbt));
325 test_parse!(
334 tokens,
335 BaseType,
336 Err(ParseError::InvalidToken {
337 token: "Unknown".to_string(),
338 error: "Invalid type".to_string(),
339 })
340 );
341 assert_eq!(tokens.pop().unwrap(), "Unknown");
342 assert!(tokens.is_empty());
343 test_parse!(tokens, BaseType, Err(ParseError::EndOfFile));
344 }
345
346 #[test]
347 fn test_value() {
348 let mut tokens: Vec<String> = tokenize!("len(iden) 42.0 iden");
349
350 test_parse!(
351 tokens,
352 Value,
353 Ok(Value::Length(Identifier("iden".to_string())))
354 );
355 test_parse!(tokens, Value, Ok(Value::Literal(Literal::Float(42.0))));
356 test_parse!(
357 tokens,
358 Value,
359 Ok(Value::Identifier(Identifier("iden".to_string())))
360 );
361
362 assert!(tokens.is_empty());
363 test_parse!(tokens, Value, Err(ParseError::EndOfFile));
364 }
365
366 #[test]
367 fn test_field() {
368 let mut tokens: Vec<String> = tokenize!(
369 "
370 i32 first_field
371 Nbt second_field = 42.0
372 i64 third_field = len(list)
373 List[i32] list
374 "
375 );
376
377 test_parse!(
378 tokens,
379 Field,
380 Ok(Field {
381 ty: Type::BaseType(BaseType::Integer(IntegerType::I32)),
382 name: Identifier("first_field".to_string()),
383 value: None,
384 conditions: HashSet::new(),
385 })
386 );
387 test_parse!(
388 tokens,
389 Field,
390 Ok(Field {
391 ty: Type::BaseType(BaseType::Nbt),
392 name: Identifier("second_field".to_string()),
393 value: Some(Value::Literal(Literal::Float(42.0))),
394 conditions: HashSet::new(),
395 })
396 );
397 test_parse!(
398 tokens,
399 Field,
400 Ok(Field {
401 ty: Type::BaseType(BaseType::Integer(IntegerType::I64)),
402 name: Identifier("third_field".to_string()),
403 value: Some(Value::Length(Identifier("list".to_string()))),
404 conditions: HashSet::new(),
405 })
406 );
407 test_parse!(
408 tokens,
409 Field,
410 Ok(Field {
411 ty: Type::BaseType(BaseType::List {
412 ty: Box::new(Type::BaseType(BaseType::Integer(IntegerType::I32))),
413 length: None
414 }),
415 name: Identifier("list".to_string()),
416 value: None,
417 conditions: HashSet::new(),
418 })
419 );
420
421 assert!(tokens.is_empty());
422 test_parse!(tokens, Field, Err(ParseError::EndOfFile));
423 }
424
425 #[test]
426 fn test_field_list() {
427 let mut tokens: Vec<String> = tokenize!(
428 "
429 bool cond
430 if (cond) {
431 i32 number
432 }
433 if (!cond) {
434 u64 other
435 }
436 "
437 );
438
439 test_parse!(
440 tokens,
441 FieldList,
442 Ok(FieldList(vec![
443 Field {
444 ty: Type::BaseType(BaseType::Bool),
445 name: Identifier("cond".to_string()),
446 value: None,
447 conditions: HashSet::new(),
448 },
449 Field {
450 ty: Type::BaseType(BaseType::Integer(IntegerType::I32)),
451 name: Identifier("number".to_string()),
452 value: None,
453 conditions: HashSet::from_iter(vec!["cond".to_string()]),
454 },
455 Field {
456 ty: Type::BaseType(BaseType::Integer(IntegerType::U64)),
457 name: Identifier("other".to_string()),
458 value: None,
459 conditions: HashSet::from_iter(vec!["!cond".to_string()]),
460 },
461 ]))
462 );
463
464 assert!(tokens.is_empty());
465 test_parse!(tokens, FieldList, Ok(FieldList(vec![])));
466 }
467}