specmc_protocol/
base.rs

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_ty: IntegerType,
86        length: Option<usize>,
87    },
88    Nbt,
89    // Option(Box<Type>),
90}
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            // "Option" => {
133            //     ensure_tokens!(tokens, "[");
134            //     let ty: Box<Type> = Box::new(Type::parse(tokens)?);
135            //     ensure_tokens!(tokens, "]");
136            //     Ok(Option(ty))
137            // }
138            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 of a list or nbt
154    Length(Identifier),
155    /// A literal value
156    Literal(Literal),
157    /// Some identifier
158    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!(
326        //     tokens,
327        //     BaseType,
328        //     Ok(BaseType::Option(Box::new(Type::BaseType(
329        //         BaseType::Integer(IntegerType::VarInt)
330        //     ))))
331        // );
332
333        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}