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},
10    IResult, Parser,
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    )
111    .parse(input)
112}
113
114fn parse_expression_token_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
115    alt((
116        map(tag("="), |_| ExpressionToken::Literal("=".to_string())),
117        map(space1, |_| ExpressionToken::Space),
118        map(tag("2>"), |_| ExpressionToken::Literal("2>".to_string())),
119        map(
120            delimited(tag("\""), parse_expression_parameter, tag("\"")),
121            ExpressionToken::DoubleQuotes,
122        ),
123        map(
124            delimited(tag("("), take_until(")"), tag(")")),
125            |d: KconfigInput| ExpressionToken::Literal("(".to_string() + d.fragment() + ")"),
126        ),
127        map(
128            delimited(tag("`"), take_until("`"), tag("`")),
129            |d: KconfigInput| ExpressionToken::Backtick(d.to_string()),
130        ),
131        map(
132            delimited(
133                ws(char::<KconfigInput, _>('\'')),
134                take_until("'"),
135                char('\''),
136            ),
137            |d| ExpressionToken::SingleQuotes(d.to_string()),
138        ),
139        parse_literal_parameter,
140        parse_expression_token_variable_parameter,
141        map(parse_function_call, |f| {
142            ExpressionToken::Function(Box::new(f))
143        }),
144    ))
145    .parse(input)
146}
147
148fn parse_instruction_parameter(input: KconfigInput) -> IResult<KconfigInput, String> {
149    map(
150        (
151            tag("%"),
152            recognize(ws(many1(alt((alphanumeric1, recognize(one_of("_"))))))),
153            delimited(tag("("), alphanumeric1, tag(")")),
154        ),
155        |(_a, b, c)| format!("%{}({})", b, c),
156    )
157    .parse(input)
158}
159
160fn parse_env_variable_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
161    map(
162        ws(preceded(tag("$"), recognize(many1(alphanumeric1)))),
163        |d| ExpressionToken::Literal(format!("${}", d)),
164    )
165    .parse(input)
166}
167
168fn parse_literal_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
169    alt((
170        parse_env_variable_parameter,
171        map(parse_instruction_parameter, ExpressionToken::Literal),
172        map(
173            recognize(ws(many1(alt((
174                alphanumeric1,
175                tag("\\$"),
176                recognize(one_of("+(<>%&\\[]_|'.-:\n\\/")),
177            ))))),
178            |d: KconfigInput| ExpressionToken::Literal(d.fragment().to_string()),
179        ),
180    ))
181    .parse(input)
182}
183
184pub fn parse_expression_parameter(
185    input: KconfigInput,
186) -> IResult<KconfigInput, Vec<ExpressionToken>> {
187    alt((many1(parse_expression_token_parameter),)).parse(input)
188}
189
190pub fn parse_parameter(input: KconfigInput) -> IResult<KconfigInput, Parameter> {
191    map(alt((parse_expression_parameter,)), |d| Parameter {
192        tokens: d,
193    })
194    .parse(input)
195}
196
197fn parse_function_name(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, &str> {
198    map(
199        recognize(ws(many1(alt((alphanumeric1, recognize(one_of("=-"))))))),
200        |d: KconfigInput| d.fragment().to_owned(),
201    )
202    .parse(input)
203}
204
205pub fn parse_function_call(input: KconfigInput) -> IResult<KconfigInput, FunctionCall> {
206    map(
207        delimited(
208            tag("$("),
209            (
210                terminated(parse_function_name, opt(ws(tag(",")))),
211                separated_list0(ws(tag(",")), ws(parse_parameter)),
212            ),
213            ws(tag(")")),
214        ),
215        |(name, parameters)| FunctionCall {
216            name: name.to_string(),
217            parameters,
218        },
219    )
220    .parse(input)
221}