Skip to main content

nom_kconfig/entry/
variable.rs

1use nom::combinator::all_consuming;
2use nom::{
3    branch::alt,
4    bytes::complete::tag,
5    character::complete::{alphanumeric1, one_of},
6    combinator::{map, map_parser, recognize},
7    multi::many1,
8    IResult, Parser,
9};
10#[cfg(feature = "deserialize")]
11use serde::Deserialize;
12#[cfg(feature = "serialize")]
13use serde::Serialize;
14
15use crate::attribute::parse_function_call;
16use crate::{
17    attribute::{
18        function::{parse_expression_token_variable_parameter, ExpressionToken},
19        FunctionCall,
20    },
21    string::parse_string,
22    util::{parse_until_eol, ws},
23    KconfigInput,
24};
25
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 struct VariableAssignment {
31    pub identifier: VariableIdentifier,
32    pub operator: String,
33    pub right: Value,
34}
35
36impl VariableIdentifier {
37    fn raw(&self) -> String {
38        match self {
39            VariableIdentifier::Identifier(s) => s.clone(),
40            VariableIdentifier::VariableRef(reff) => reff
41                .iter()
42                .map(|e| e.to_string())
43                .collect::<Vec<_>>()
44                .join(" "),
45        }
46    }
47}
48
49#[derive(Debug, PartialEq, Clone)]
50#[cfg_attr(feature = "hash", derive(Hash))]
51#[cfg_attr(feature = "serialize", derive(Serialize))]
52#[cfg_attr(feature = "deserialize", derive(Deserialize))]
53pub enum VariableIdentifier {
54    Identifier(String),
55    VariableRef(Vec<ExpressionToken>),
56}
57
58#[derive(Debug, PartialEq, Clone)]
59#[cfg_attr(feature = "hash", derive(Hash))]
60#[cfg_attr(feature = "serialize", derive(Serialize))]
61#[cfg_attr(feature = "deserialize", derive(Deserialize))]
62pub enum Value {
63    Literal(String),
64    ExpandedVariable(String),
65    FunctionCall(FunctionCall),
66}
67
68impl Value {
69    fn raw(&self) -> String {
70        match self {
71            Value::Literal(s) => s.clone(),
72            Value::ExpandedVariable(s) => s.clone(),
73            Value::FunctionCall(function_call) => function_call.to_string(),
74        }
75    }
76}
77
78pub fn parse_value(input: KconfigInput) -> IResult<KconfigInput, Value> {
79    alt((
80        map(map_parser(parse_until_eol, parse_string), |s| {
81            Value::Literal(s)
82        }),
83        map(
84            all_consuming(map_parser(parse_until_eol, ws(parse_function_call))),
85            Value::FunctionCall,
86        ),
87        map(parse_until_eol, |s| {
88            Value::Literal(s.fragment().to_string())
89        }),
90    ))
91    .parse(input)
92}
93
94pub fn parse_variable_identifier(input: KconfigInput) -> IResult<KconfigInput, VariableIdentifier> {
95    alt((
96        map(
97            recognize(ws(many1(alt((alphanumeric1, recognize(one_of("-_"))))))),
98            |l: KconfigInput| VariableIdentifier::Identifier(l.trim().to_string()),
99        ),
100        map(many1(parse_expression_token_variable_parameter), |v| {
101            VariableIdentifier::VariableRef(v)
102        }),
103    ))
104    .parse(input)
105}
106
107pub fn parse_variable_assignment(input: KconfigInput) -> IResult<KconfigInput, VariableAssignment> {
108    let (mut remaining, assignment) = map(
109        (
110            ws(parse_variable_identifier),
111            ws(parse_assign),
112            ws(parse_value),
113        ),
114        |(l, o, r)| VariableAssignment {
115            identifier: l,
116            operator: o.to_string(),
117            right: r,
118        },
119    )
120    .parse(input)?;
121
122    // If the parsing is successful, we add the variable assignment to the local variables of the KconfigFile.
123    // variables can be used by the preprocessor.
124    remaining
125        .extra
126        .add_local_var(assignment.identifier.raw(), assignment.right.raw());
127    Ok((remaining, assignment))
128}
129
130pub fn parse_assign(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, &str> {
131    map(alt((tag("="), tag(":="), tag("+="))), |d: KconfigInput| {
132        d.fragment().to_owned()
133    })
134    .parse(input)
135}
136
137#[test]
138#[ignore]
139fn test_parse_value() {
140    assert_eq!(
141        parse_value(KconfigInput::new_extra("hello world", Default::default())),
142        Ok((
143            KconfigInput::new_extra("", Default::default()),
144            Value::Literal("hello world".to_string())
145        ))
146    );
147
148    assert_eq!(
149        parse_value(KconfigInput::new_extra(
150            r#""hello world""#,
151            Default::default()
152        )),
153        Ok((
154            KconfigInput::new_extra("", Default::default()),
155            Value::Literal("hello world".to_string())
156        ))
157    );
158}