nom_kconfig/attribute/
function.rs

1use std::fmt::Display;
2
3use nom::{
4    branch::alt,
5    bytes::complete::{tag, take_until},
6    character::complete::{alphanumeric1, char, one_of, space1},
7    combinator::{map, opt, recognize},
8    multi::{many1, separated_list0},
9    sequence::{delimited, preceded, terminated, tuple},
10    IResult,
11};
12#[cfg(feature = "deserialize")]
13use serde::Deserialize;
14#[cfg(feature = "serialize")]
15use serde::Serialize;
16
17use crate::{util::ws, KconfigInput};
18
19#[derive(Debug, PartialEq, Clone, Default)]
20#[cfg_attr(feature = "hash", derive(Hash))]
21#[cfg_attr(feature = "serialize", derive(Serialize))]
22#[cfg_attr(feature = "deserialize", derive(Deserialize))]
23pub struct FunctionCall {
24    pub name: String,
25    pub parameters: Vec<Parameter>,
26}
27
28impl Display for Parameter {
29    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
30        write!(
31            f,
32            "{}",
33            self.tokens
34                .iter()
35                .map(|d: &ExpressionToken| d.to_string())
36                .collect::<Vec<_>>()
37                .join("")
38        )
39    }
40}
41
42impl Display for ExpressionToken {
43    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
44        match self {
45            ExpressionToken::Literal(s) => write!(f, "{}", s),
46            ExpressionToken::Variable(v) => write!(f, "${}", v),
47            ExpressionToken::DoubleQuotes(s) => write!(
48                f,
49                r#""{}""#,
50                s.iter().map(|d| d.to_string()).collect::<Vec<_>>().join("")
51            ),
52            ExpressionToken::SingleQuotes(s) => write!(f, "'{}'", s),
53            ExpressionToken::Backtick(c) => write!(f, "`{}`", c),
54            ExpressionToken::Function(func) => write!(f, "{}", func),
55            ExpressionToken::Space => write!(f, " "),
56        }
57    }
58}
59
60impl Display for FunctionCall {
61    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
62        if self.parameters.is_empty() {
63            return write!(f, "$({})", self.name);
64        }
65        write!(
66            f,
67            "$({}, {})",
68            self.name,
69            self.parameters
70                .iter()
71                .map(|d| d.to_string())
72                .collect::<Vec<_>>()
73                .join(", ")
74        )
75    }
76}
77
78#[derive(Debug, PartialEq, Clone, Default)]
79#[cfg_attr(feature = "hash", derive(Hash))]
80#[cfg_attr(feature = "serialize", derive(Serialize))]
81#[cfg_attr(feature = "deserialize", derive(Deserialize))]
82pub struct Parameter {
83    pub tokens: Vec<ExpressionToken>,
84}
85
86#[derive(Debug, PartialEq, Clone)]
87#[cfg_attr(feature = "hash", derive(Hash))]
88#[cfg_attr(feature = "serialize", derive(Serialize))]
89#[cfg_attr(feature = "deserialize", derive(Deserialize))]
90pub enum ExpressionToken {
91    Literal(String),
92    Variable(String),
93    DoubleQuotes(Vec<ExpressionToken>),
94    SingleQuotes(String),
95    Backtick(String),
96    Function(Box<FunctionCall>),
97    Space,
98}
99
100pub fn parse_expression_token_variable_parameter(
101    input: KconfigInput,
102) -> IResult<KconfigInput, ExpressionToken> {
103    map(
104        delimited(
105            tag("$("),
106            recognize(ws(many1(recognize(one_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ_"))))),
107            tag(")"),
108        ),
109        |d: KconfigInput| ExpressionToken::Variable(d.fragment().to_string()),
110    )(input)
111}
112
113fn parse_expression_token_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
114    alt((
115        map(tag("="), |_| ExpressionToken::Literal("=".to_string())),
116        map(space1, |_| ExpressionToken::Space),
117        map(tag("2>"), |_| ExpressionToken::Literal("2>".to_string())),
118        map(
119            delimited(tag("\""), parse_expression_parameter, tag("\"")),
120            ExpressionToken::DoubleQuotes,
121        ),
122        map(
123            delimited(tag("("), take_until(")"), tag(")")),
124            |d: KconfigInput| ExpressionToken::Literal("(".to_string() + d.fragment() + ")"),
125        ),
126        map(
127            delimited(tag("`"), take_until("`"), tag("`")),
128            |d: KconfigInput| ExpressionToken::Backtick(d.to_string()),
129        ),
130        map(
131            delimited(
132                ws(char::<KconfigInput, _>('\'')),
133                take_until("'"),
134                char('\''),
135            ),
136            |d| ExpressionToken::SingleQuotes(d.to_string()),
137        ),
138        parse_literal_parameter,
139        parse_expression_token_variable_parameter,
140        map(parse_function_call, |f| {
141            ExpressionToken::Function(Box::new(f))
142        }),
143    ))(input)
144}
145
146fn parse_instruction_parameter(input: KconfigInput) -> IResult<KconfigInput, String> {
147    map(
148        tuple((
149            tag("%"),
150            recognize(ws(many1(alt((alphanumeric1, recognize(one_of("_"))))))),
151            delimited(tag("("), alphanumeric1, tag(")")),
152        )),
153        |(_a, b, c)| format!("%{}({})", b, c),
154    )(input)
155}
156
157fn parse_env_variable_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
158    map(
159        ws(preceded(tag("$"), recognize(many1(alphanumeric1)))),
160        |d| ExpressionToken::Literal(format!("${}", d)),
161    )(input)
162}
163
164fn parse_literal_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
165    alt((
166        parse_env_variable_parameter,
167        map(parse_instruction_parameter, ExpressionToken::Literal),
168        map(
169            recognize(ws(many1(alt((
170                alphanumeric1,
171                tag("\\$"),
172                recognize(one_of("+(<>%&\\[]_|'.-:\n\\/")),
173            ))))),
174            |d: KconfigInput| ExpressionToken::Literal(d.fragment().to_string()),
175        ),
176    ))(input)
177}
178
179pub fn parse_expression_parameter(
180    input: KconfigInput,
181) -> IResult<KconfigInput, Vec<ExpressionToken>> {
182    alt((many1(parse_expression_token_parameter),))(input)
183}
184
185pub fn parse_parameter(input: KconfigInput) -> IResult<KconfigInput, Parameter> {
186    map(alt((parse_expression_parameter,)), |d| Parameter {
187        tokens: d,
188    })(input)
189}
190
191fn parse_function_name(input: KconfigInput) -> IResult<KconfigInput, &str> {
192    map(
193        recognize(ws(many1(alt((alphanumeric1, recognize(one_of("=-"))))))),
194        |d: KconfigInput| d.fragment().to_owned(),
195    )(input)
196}
197
198pub fn parse_function_call(input: KconfigInput) -> IResult<KconfigInput, FunctionCall> {
199    map(
200        delimited(
201            tag("$("),
202            tuple((
203                terminated(parse_function_name, opt(ws(tag(",")))),
204                separated_list0(ws(tag(",")), ws(parse_parameter)),
205            )),
206            ws(tag(")")),
207        ),
208        |(name, parameters)| FunctionCall {
209            name: name.to_string(),
210            parameters,
211        },
212    )(input)
213}