nom_kconfig/
symbol.rs

1use super::util::ws;
2use crate::number::parse_number;
3use crate::KconfigInput;
4use nom::bytes::tag;
5use nom::combinator::complete;
6use nom::{
7    branch::alt,
8    bytes::{tag_no_case, take_until},
9    character::complete::{alphanumeric1, char, hex_digit1, one_of},
10    combinator::{all_consuming, map, recognize},
11    multi::many1,
12    sequence::{delimited, pair},
13    IResult, Parser,
14};
15#[cfg(feature = "deserialize")]
16use serde::Deserialize;
17#[cfg(feature = "serialize")]
18use serde::Serialize;
19use std::fmt::Display;
20
21/// There are two types of symbols: constant and non-constant symbols. Non-constant symbols are the most
22/// common ones and are defined with the 'config' statement. Non-constant symbols consist entirely of al-
23/// phanumeric characters or underscores. Constant symbols are only part of expressions. Constant symbols
24/// are always surrounded by single or double quotes. Within the quote, any other character is allowed and
25/// the quotes can be escaped using ''.
26#[derive(Debug, PartialEq, Clone)]
27#[cfg_attr(feature = "hash", derive(Hash))]
28#[cfg_attr(feature = "serialize", derive(Serialize))]
29#[cfg_attr(feature = "deserialize", derive(Deserialize))]
30pub enum Symbol {
31    Constant(String),
32    NonConstant(String),
33}
34
35pub fn parse_constant_symbol(input: KconfigInput) -> IResult<KconfigInput, Symbol> {
36    alt((
37        all_consuming(parse_constant_tristate),
38        all_consuming(parse_constant_bool),
39        all_consuming(parse_constant_hex),
40        all_consuming(parse_constant_int),
41        all_consuming(parse_constant_string),
42    ))
43    .parse(input)
44}
45
46fn parse_env(input: KconfigInput) -> IResult<KconfigInput, Symbol> {
47    all_consuming(complete(map(
48        delimited(tag("\"$("), take_until(")\""), tag(")\"")),
49        |e: KconfigInput| Symbol::NonConstant(format!("$({})", e.fragment())),
50    )))
51    .parse(input)
52}
53
54pub fn parse_symbol(input: KconfigInput) -> IResult<KconfigInput, Symbol> {
55    let first_word = map(
56        alt((
57            complete(recognize(delimited(
58                tag("\"$("),
59                many1(alt((alphanumeric1, recognize(one_of("._"))))),
60                tag(")\""),
61            ))),
62            recognize(ws(many1(alt((alphanumeric1, recognize(one_of("._'\""))))))),
63        )),
64        |c: KconfigInput| c,
65    )
66    .parse(input)?;
67    let ok = alt((
68        parse_env,
69        parse_constant_symbol,
70        map(parse_non_constant_symbol, |c: &str| {
71            Symbol::NonConstant(c.to_string())
72        }),
73        all_consuming(parse_constant_string),
74    ))
75    .parse(first_word.1)?;
76    Ok((first_word.0, ok.1))
77}
78
79pub fn parse_constant_int(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, Symbol> {
80    map(
81        alt((
82            parse_number,
83            delimited(ws(char('"')), parse_number, char('"')),
84            delimited(ws(char('\'')), parse_number, char('\'')),
85        )),
86        |integer: i64| Symbol::Constant(integer.to_string()),
87    )
88    .parse(input)
89}
90
91pub fn parse_constant_bool(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, Symbol> {
92    ws(alt((
93        private_parse_constant_bool,
94        delimited(ws(char('"')), private_parse_constant_bool, char('"')),
95        delimited(ws(char('\'')), private_parse_constant_bool, char('\'')),
96    )))
97    .parse(input)
98}
99
100pub fn parse_constant_hex(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, Symbol> {
101    ws(map(
102        pair(complete(tag_no_case("0x")), hex_digit1),
103        |(prefix, v): (KconfigInput, KconfigInput)| {
104            Symbol::Constant(format!("{}{}", prefix.fragment(), v.fragment()))
105        },
106    ))
107    .parse(input)
108}
109
110pub fn parse_constant_tristate(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, Symbol> {
111    ws(alt((
112        parse_constant_bool,
113        map(char('m'), |_| Symbol::Constant("m".to_string())),
114        map(delimited(ws(char('"')), char('m'), char('"')), |_| {
115            Symbol::Constant("m".to_string())
116        }),
117        map(delimited(ws(char('\'')), char('m'), char('\'')), |_| {
118            Symbol::Constant("m".to_string())
119        }),
120    )))
121    .parse(input)
122}
123
124pub fn parse_constant_string(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, Symbol> {
125    map(
126        complete(alt((
127            delimited(ws(char('"')), take_until("\""), char('"')),
128            delimited(ws(char('\'')), take_until("\'"), char('\'')),
129        ))),
130        |c: KconfigInput| Symbol::Constant(c.fragment().to_string()),
131    )
132    .parse(input)
133}
134
135pub fn parse_non_constant_symbol(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, &str> {
136    map(
137        recognize(ws(many1(alt((alphanumeric1, recognize(one_of("._"))))))),
138        |c: KconfigInput| c.trim(),
139    )
140    .parse(input)
141}
142
143fn private_parse_constant_bool(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, Symbol> {
144    alt((
145        map(char('y'), |_| Symbol::Constant("y".to_string())),
146        map(char('n'), |_| Symbol::Constant("n".to_string())),
147    ))
148    .parse(input)
149}
150
151impl Display for Symbol {
152    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
153        match self {
154            Symbol::Constant(c) => write!(f, "{}", c),
155            Symbol::NonConstant(c) => write!(f, "{}", c),
156        }
157    }
158}
159
160#[test]
161fn test_parse_constant_symbol() {
162    use crate::assert_parsing_eq;
163
164    assert_parsing_eq!(
165        parse_constant_symbol,
166        "\"64BITS\"",
167        Ok(("", Symbol::Constant("64BITS".to_string())))
168    );
169
170    assert_parsing_eq!(
171        parse_constant_symbol,
172        "\"true\"",
173        Ok(("", Symbol::Constant("true".to_string())))
174    );
175    assert_parsing_eq!(
176        parse_constant_symbol,
177        "\'false\'",
178        Ok(("", Symbol::Constant("false".to_string())))
179    );
180
181    assert_parsing_eq!(
182        parse_constant_symbol,
183        "'64BITS'",
184        Ok(("", Symbol::Constant("64BITS".to_string())))
185    );
186
187    assert_parsing_eq!(
188        parse_constant_symbol,
189        "'64'",
190        Ok(("", Symbol::Constant("64".to_string())))
191    )
192}