safe_drive_msg/
parser.rs

1//! # Grammar
2//!
3//! ```text
4//! $Expr = $Empty | $Comment | $VarDef
5//!
6//! $Empty | $Comment | $VarDef
7//!
8//! $VarDef = $Variable $Comment $End | $Variable $End
9//! $Variable = $TypeName $ID | $TypeName $CapitalID = $Value | $TypeName $ID $Value
10//! $TypeName =
11//!     string<=$PlusNum |
12//!     string<=$PlusNum $ArrayInfo |
13//!     $ID/$ID $ArrayInfo |
14//!     $ID/$ID |
15//!     $ID $ArrayInfo |
16//!     $ID
17//! $ArrayInfo = [] | [$PlusNum] | [<=$PlusNum]
18//! $PlusNum = Regex([0..9]+)
19//!
20//! $Comment = Regex(#.*) $End
21//!
22//! $ID = Regex((_|[a..zA..Z]+)([a..zA..Z0..9]|_)*)
23//! $CapitalID = Regex((_|[A..Z]+)([A..Z0..9]|_)*)
24//!
25//! $Value = $Bool | $Num | $Array | $String | $RawString
26//! $Bool = true | false
27//! $Num = Regex(-?[0..9]+(.[0..9]+)?)
28//! $String = 'characters' | "characters"
29//! $RawString = characters
30//!
31//! $Array = [ $Elements ]
32//! $Elements = $Value | $Value , $Elements
33//! ```
34
35use nom::{
36    branch::alt,
37    bytes::complete::tag,
38    character::{
39        self,
40        complete::{
41            alpha1, anychar, line_ending, not_line_ending, one_of, satisfy, space0, space1,
42        },
43        is_alphanumeric,
44    },
45    combinator::peek,
46    error::VerboseError,
47    multi::{many0, many1, separated_list1},
48    number,
49    sequence::{delimited, preceded},
50    IResult,
51};
52use std::fmt::Display;
53
54type PResult<'a, OUT> = IResult<&'a str, OUT, VerboseError<&'a str>>;
55
56#[derive(Debug)]
57pub enum Expr {
58    Variable {
59        type_name: TypeName,
60        var_name: String,
61        value: Option<ValueType>,
62        comment: Option<String>,
63    },
64    Empty, // comment or empty line
65    Comment,
66    Eof,
67}
68
69#[derive(Debug)]
70pub enum ValueType {
71    Const(Value),
72    Default(Value),
73}
74
75#[derive(Debug)]
76pub enum Value {
77    Bool(bool),
78    String(String),
79    Float(f64),
80    Uint(u64),
81    Int(i64),
82    Array(Vec<Value>),
83}
84
85impl Display for Value {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match self {
88            Value::Int(n) => write!(f, "{n}"),
89            Value::Uint(n) => write!(f, "{n}"),
90            Value::Float(n) => write!(f, "{n}"),
91            Value::String(n) => write!(f, "b\"{n}\\0\""),
92            Value::Bool(n) => write!(f, "{n}"),
93            Value::Array(n) => write!(f, "{:?}", n),
94        }
95    }
96}
97
98#[derive(Debug)]
99pub enum TypeName {
100    Type {
101        type_name: String,
102        array_info: ArrayInfo,
103    },
104    ScopedType {
105        scope: String,
106        type_name: String,
107        array_info: ArrayInfo,
108    },
109    LimitedString {
110        size: usize,
111        array_info: ArrayInfo,
112    },
113    String(ArrayInfo),
114}
115
116#[derive(Debug)]
117pub enum ArrayInfo {
118    NotArray,
119    Dynamic,
120    Static(usize),
121    Limited(usize),
122}
123
124/// Parse .msg file.
125///
126/// # Grammar
127///
128/// ```text
129/// $Msg = $Expr $Expr | $Expr
130/// ```
131pub fn parse_msg(mut input: &str) -> PResult<Vec<Expr>> {
132    let mut result = Vec::new();
133    loop {
134        if input.is_empty() {
135            break;
136        }
137
138        let (next, expr) = parse_expr(input)?;
139        input = next;
140
141        if let Expr::Variable { .. } = &expr {
142            result.push(expr);
143        }
144    }
145
146    Ok((input, result))
147}
148
149pub fn parse_srv(mut input: &str) -> PResult<(Vec<Expr>, Vec<Expr>)> {
150    // parse request
151    let mut request = Vec::new();
152    loop {
153        if peek_tag("---", input).is_ok() {
154            break;
155        }
156
157        let (next, expr) = parse_expr(input)?;
158        input = next;
159
160        if let Expr::Variable { .. } = &expr {
161            request.push(expr);
162        }
163    }
164
165    let (mut input, _) = tag("---")(input)?;
166
167    // parse response
168    let mut response = Vec::new();
169    loop {
170        if input.is_empty() {
171            break;
172        }
173
174        let (next, expr) = parse_expr(input)?;
175        input = next;
176
177        if let Expr::Variable { .. } = &expr {
178            response.push(expr);
179        }
180    }
181
182    Ok(("", (request, response)))
183}
184
185/// ```text
186/// $Expr = $Empty | $Comment | $VarDef
187/// ```
188fn parse_expr(input: &str) -> PResult<Expr> {
189    let (input, _) = space0(input)?;
190    alt((parse_empty, parse_comment, parse_variable))(input)
191}
192
193/// ```text
194/// $VarDef = $Variable $Comment $End | $Variable $End
195/// $Variable = $TypeName $CapitalID = $Value | $TypeName $ID | $TypeName $ID $Value
196/// ```
197fn parse_variable(input: &str) -> PResult<Expr> {
198    let (input, type_name) = parse_typename(input)?;
199
200    // skip whitespaces
201    let (input, _) = space1(input)?;
202
203    // parse mutable or immutable variables
204    let (input, (var_name, value)) = alt((parse_immutable_var, parse_mutable_var))(input)?;
205
206    // skip whitespaces
207    let (input, _) = space0(input)?;
208
209    let (input, comment) = if peek_tag("#", input).is_ok() {
210        // skip coment
211        let (input, _) = tag("#")(input)?;
212        let (input, c) = not_line_ending(input)?;
213        (input, Some(c.to_string()))
214    } else {
215        (input, None)
216    };
217
218    let input = if !input.is_empty() {
219        // skip line ending
220        let (input, _) = line_ending(input)?;
221        input
222    } else {
223        input
224    };
225
226    Ok((
227        input,
228        Expr::Variable {
229            type_name,
230            var_name,
231            value,
232            comment,
233        },
234    ))
235}
236
237fn parse_mutable_var(input: &str) -> PResult<(String, Option<ValueType>)> {
238    // parse variable name
239    // $ID
240    let (input, var_name) = parse_identifier(input)?;
241
242    // having default value?
243    fn get_value(input: &str) -> PResult<Value> {
244        let (input, _) = space1(input)?;
245        parse_value(input)
246    }
247
248    if let Ok((input, val)) = get_value(input) {
249        Ok((input, (var_name, Some(ValueType::Default(val))))) // having default value
250    } else {
251        Ok((input, (var_name, None))) // no default value
252    }
253}
254
255fn parse_immutable_var(input: &str) -> PResult<(String, Option<ValueType>)> {
256    // parse variable name
257    // $CapitalID
258    let (input, var_name) = parse_capital_identifier(input)?;
259
260    // skip whitespaces
261    let (input, _) = space0(input)?;
262
263    let (input, _) = tag("=")(input)?;
264
265    // skip whitespaces
266    let (input, _) = space0(input)?;
267
268    // parse value
269    let (input, val) = parse_value(input)?;
270
271    Ok((input, (var_name, Some(ValueType::Const(val)))))
272}
273
274/// ```text
275/// $TypeName =
276///     string<=$PlusNum |
277///     string<=$PlusNum $ArrayInfo |
278///     $ID/$ID $ArrayInfo |
279///     $ID/$ID |
280///     $ID $ArrayInfo |
281///     $ID
282/// ```
283fn parse_typename(input: &str) -> PResult<TypeName> {
284    // parse type name
285    let (input, scope) = parse_identifier(input)?;
286
287    if scope == "string" {
288        return parse_string_type(input);
289    }
290
291    if peek_tag("/", input).is_ok() {
292        // $ID/$ID
293        let (input, _) = tag("/")(input)?;
294        let (input, type_name) = parse_identifier(input)?;
295
296        // $ArrayInfo
297        //let (input, _) = space0(input)?;
298        let (input, array_info) = parse_array_info(input)?;
299        Ok((
300            input,
301            TypeName::ScopedType {
302                scope,
303                type_name,
304                array_info,
305            },
306        ))
307    } else {
308        // $ArrayInfo
309        let (input, array_info) = parse_array_info(input)?;
310        Ok((
311            input,
312            TypeName::Type {
313                type_name: scope,
314                array_info,
315            },
316        ))
317    }
318}
319
320fn parse_string_type(input: &str) -> PResult<TypeName> {
321    if peek_tag("<=", input).is_ok() {
322        let (input, _) = tag("<=")(input)?;
323        let (input, size) = character::complete::u64(input)?;
324        let (input, array_info) = parse_array_info(input)?;
325        Ok((
326            input,
327            TypeName::LimitedString {
328                size: size as usize,
329                array_info,
330            },
331        ))
332    } else {
333        let (input, array_info) = parse_array_info(input)?;
334        Ok((input, TypeName::String(array_info)))
335    }
336}
337
338/// ```text
339/// $ID = Regex((_|[a..zA..Z]+)([a..zA..Z0..9]|_)*)
340/// ```
341fn parse_identifier(input: &str) -> PResult<String> {
342    // (_|[a..zA..Z]+)
343    let (input, head) = alt((tag("_"), alpha1))(input)?;
344
345    // [a..zA..Z0..9_]*
346    let (input, tail) = many0(satisfy(|c| is_alphanumeric(c as u8) || c == '_'))(input)?;
347
348    let tail: String = tail.iter().collect();
349    Ok((input, head.to_string() + &tail))
350}
351
352/// ```text
353/// $ID = Regex((_|[A..Z]+)([A..Z0..9]|_)*)
354/// ```
355fn parse_capital_identifier(input: &str) -> PResult<String> {
356    // (_|[A..Z]+)
357    let (input, head) = satisfy(|c| ('A'..='Z').contains(&c) || c == '_')(input)?;
358
359    // ([A..Z0..9]|_)*
360    let (input, tail) = many0(satisfy(|c| {
361        ('A'..='Z').contains(&c) || ('0'..='9').contains(&c) || c == '_'
362    }))(input)?;
363
364    let tail: String = tail.iter().collect();
365    Ok((input, format!("{head}{tail}")))
366}
367
368/// ```text
369/// $Comment = Regex(#.*) $End
370/// ```
371fn parse_comment(input: &str) -> PResult<Expr> {
372    let (input, _) = tag("#")(input)?;
373    let (input, _) = not_line_ending(input)?;
374
375    let input = if !input.is_empty() {
376        // skip line ending
377        let (input, _) = line_ending(input)?;
378        input
379    } else {
380        input
381    };
382
383    Ok((input, Expr::Comment))
384}
385
386/// empty line or EOF
387fn parse_empty(input: &str) -> PResult<Expr> {
388    if input.is_empty() {
389        Ok((input, Expr::Eof))
390    } else {
391        let (input, _) = line_ending(input)?;
392        Ok((input, Expr::Empty))
393    }
394}
395
396fn peek_tag<'a>(c: &'static str, input: &'a str) -> PResult<'a, &'a str> {
397    peek(tag(c))(input)
398}
399
400/// ```text
401/// $Value = $Bool | $Num | $Array | $String | $RawString
402/// $Bool = true | false
403/// $Num = Regex(-?[0..9]+(.[0..9]+)?)
404/// $String = 'characters' | "characters"
405/// $RawString = characters
406/// ```
407fn parse_value(input: &str) -> PResult<Value> {
408    alt((
409        parse_num,
410        parse_bool,
411        parse_array,
412        parse_string,
413        parse_raw_string,
414    ))(input)
415}
416
417/// ```text
418/// $Num = Regex(-?[0..9]+(.[0..9]+)?)
419/// ```
420fn parse_num(input: &str) -> PResult<Value> {
421    // parse minus
422    let (input, minus) = if peek_tag("-", input).is_ok() {
423        let (input, _) = tag("-")(input)?;
424        (input, -1)
425    } else {
426        (input, 1)
427    };
428
429    let (input, n) = character::complete::u64(input)?;
430    if peek_tag(".", input).is_ok() {
431        let (input, d) = number::complete::double(input)?;
432        let val = (d + n as f64) * minus as f64;
433        Ok((input, Value::Float(val)))
434    } else if minus == -1 {
435        Ok((input, Value::Int(n as i64 * minus)))
436    } else {
437        Ok((input, Value::Uint(n)))
438    }
439}
440
441/// ```text
442/// $Bool = true | false
443/// ```
444fn parse_bool(input: &str) -> PResult<Value> {
445    let (input, val) = alt((tag("true"), tag("false")))(input)?;
446    if val == "true" {
447        Ok((input, Value::Bool(true)))
448    } else {
449        Ok((input, Value::Bool(false)))
450    }
451}
452
453/// ```text
454/// $Array = [ $Elements ]
455/// $Elements = $Value | $Elements , $Value
456/// ```
457fn parse_array(input: &str) -> PResult<Value> {
458    let p = delimited(space0, parse_value, space0);
459    let (input, val) = delimited(tag("["), separated_list1(tag(","), p), tag("]"))(input)?;
460    Ok((input, Value::Array(val)))
461}
462
463/// ```text
464/// $ArrayInfo = [] | [$PlusNum] | [<=$PlusNum]
465/// $PlusNum = Regex([0..9]+)
466/// ```
467fn parse_array_info(input: &str) -> PResult<ArrayInfo> {
468    fn is_array(input: &str) -> PResult<()> {
469        let (input, _) = peek(preceded(space0, tag("[")))(input)?;
470        Ok((input, ()))
471    }
472
473    if is_array(input).is_ok() {
474        let (input, _) = space0(input)?;
475        let (input, _) = tag("[")(input)?;
476        let (input, _) = space0(input)?;
477        let (input, array_info) = if peek_tag("]", input).is_ok() {
478            // []
479            (input, ArrayInfo::Dynamic)
480        } else if peek_tag("<", input).is_ok() {
481            // [<=$PlusNum]
482            let (input, _) = tag("<=")(input)?;
483            let (input, size) = character::complete::u64(input)?;
484            (input, ArrayInfo::Limited(size as usize))
485        } else {
486            // [$PlusNum]
487            let (input, size) = character::complete::u64(input)?;
488            (input, ArrayInfo::Static(size as usize))
489        };
490
491        let (input, _) = space0(input)?;
492        let (input, _) = tag("]")(input)?;
493
494        Ok((input, array_info))
495    } else {
496        Ok((input, ArrayInfo::NotArray))
497    }
498}
499
500/// # Escaped Characters
501///
502/// - \\
503/// - \r
504/// - \n
505/// - \t
506/// - \' or \"
507fn parse_string(input: &str) -> PResult<Value> {
508    let (mut input, quote) = one_of("\"'")(input)?;
509
510    let mut val = String::new();
511
512    loop {
513        let (next, c) = anychar(input)?;
514        input = next;
515
516        match c {
517            c if c == quote => return Ok((input, Value::String(val))),
518            '\\' => {
519                let (next, c) = alt((one_of("rnt\\"), character::complete::char(quote)))(input)?;
520                input = next;
521
522                match c {
523                    'r' => {
524                        val.push_str("\\r");
525                    }
526                    'n' => {
527                        val.push_str("\\n");
528                    }
529                    't' => {
530                        val.push_str("\\t");
531                    }
532                    '\\' => {
533                        val.push_str("\\\\");
534                    }
535                    c if c == quote => {
536                        if c == '"' {
537                            val.push('\\');
538                        }
539                        val.push(quote);
540                    }
541                    _ => unreachable!(),
542                }
543            }
544            _ => {
545                val.push(c);
546            }
547        }
548    }
549}
550
551fn parse_raw_string(input: &str) -> PResult<Value> {
552    let (input, result) = many1(satisfy(|c| c != '\r' && c != '\n'))(input)?;
553    let result = result.iter().fold(String::new(), |s, c| match c {
554        '\\' => format!("{s}\\\\"),
555        '"' => format!("{s}\\\""),
556        _ => format!("{s}{c}"),
557    });
558
559    Ok((input, Value::String(result)))
560}
561
562#[cfg(test)]
563mod tests {
564    use nom::Finish;
565
566    use super::parse_raw_string;
567
568    #[test]
569    fn test_parse_raw() {
570        let s = "\\";
571        let (_, v) = parse_raw_string(s).finish().unwrap();
572        println!("{v}");
573
574        let s = "\"";
575        let (_, v) = parse_raw_string(s).finish().unwrap();
576        println!("{v}");
577
578        let s = "\\\"";
579        let (_, v) = parse_raw_string(s).finish().unwrap();
580        println!("{v}");
581    }
582}