Skip to main content

nom_kconfig/
string.rs

1use nom::{
2    branch::alt,
3    bytes::complete::tag,
4    character::complete::{alphanumeric1, one_of},
5    combinator::{map, recognize},
6    error::{Error, ErrorKind, ParseError},
7    multi::many1,
8    sequence::delimited,
9    IResult, Input, Parser,
10};
11
12use crate::{util::ws, KconfigInput};
13
14pub fn parse_string(input: KconfigInput) -> IResult<KconfigInput, String> {
15    map(
16        alt((
17            delimited(tag("'"), take_until_unbalanced('\''), tag("'")),
18            delimited(tag("\""), take_until_unbalanced('"'), tag("\"")),
19        )),
20        |d| d.fragment().to_string(),
21    )
22    .parse(input)
23}
24
25pub fn take_until_unbalanced(
26    delimiter: char,
27) -> impl Fn(KconfigInput) -> IResult<KconfigInput, KconfigInput> {
28    move |i: KconfigInput| {
29        let mut index: usize = 0;
30        let mut delimiter_counter = 0;
31
32        let end_of_line = match &i.find('\n') {
33            Some(e) => *e,
34            None => i.len(),
35        };
36
37        while let Some(n) = &i[index..end_of_line].find(delimiter) {
38            delimiter_counter += 1;
39            index += n + 1;
40        }
41
42        // we split just before the last double quote
43        match index.checked_sub(1) {
44            Some(i) => index = i,
45            None => {
46                return Err(nom::Err::Error(Error::from_error_kind(
47                    i,
48                    ErrorKind::TakeUntil,
49                )))
50            }
51        }
52        // Last delimiter is the string delimiter
53        delimiter_counter -= 1;
54
55        match delimiter_counter % 2 == 0 {
56            true => Ok(i.take_split(index)),
57            false => Err(nom::Err::Error(Error::from_error_kind(
58                i,
59                ErrorKind::TakeUntil,
60            ))),
61        }
62    }
63}
64
65/// A first word is `'something here'` or `"something here"` or just a normal word without spaces. It is used in places where Kconfig allows either a string or a symbol, such as in `default` attributes.
66pub fn parse_first_word(input: KconfigInput) -> IResult<KconfigInput, KconfigInput> {
67    alt((
68        recognize((tag("'"), take_until_unbalanced('\''), tag("'"))),
69        recognize((tag("\""), take_until_unbalanced('"'), tag("\""))),
70        recognize(ws(many1(alt((alphanumeric1, recognize(one_of("-._'\""))))))),
71    ))
72    .parse(input)
73}