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, 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 {
36 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
37 write!(
38 f,
39 "{}",
40 self.tokens
41 .iter()
42 .map(|d: &ExpressionToken| d.to_string())
43 .collect::<Vec<_>>()
44 .join("")
45 )
46 }
47}
48
49impl Display for ExpressionToken {
50 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
51 match self {
52 ExpressionToken::Literal(s) => write!(f, "{}", s),
53 ExpressionToken::Variable(v) => write!(f, "${}", v),
54 ExpressionToken::DoubleQuotes(s) => write!(
55 f,
56 r#""{}""#,
57 s.iter().map(|d| d.to_string()).collect::<Vec<_>>().join("")
58 ),
59 ExpressionToken::SingleQuotes(s) => write!(f, "'{}'", s),
60 ExpressionToken::Backtick(c) => write!(f, "`{}`", c),
61 ExpressionToken::Function(func) => write!(f, "{}", func),
62 ExpressionToken::Space => write!(f, " "),
63 }
64 }
65}
66
67impl Display for FunctionCall {
68 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69 if self.parameters.is_empty() {
70 return write!(f, "$({})", self.name);
71 }
72 write!(
73 f,
74 "$({}, {})",
75 self.name,
76 self.parameters
77 .iter()
78 .map(|d| d.to_string())
79 .collect::<Vec<_>>()
80 .join(", ")
81 )
82 }
83}
84
85#[derive(Debug, PartialEq, Clone, Default)]
86#[cfg_attr(feature = "hash", derive(Hash))]
87#[cfg_attr(feature = "serialize", derive(Serialize))]
88#[cfg_attr(feature = "deserialize", derive(Deserialize))]
89pub struct Parameter {
90 pub tokens: Vec<ExpressionToken>,
91}
92
93#[derive(Debug, PartialEq, Clone)]
94#[cfg_attr(feature = "hash", derive(Hash))]
95#[cfg_attr(feature = "serialize", derive(Serialize))]
96#[cfg_attr(feature = "deserialize", derive(Deserialize))]
97pub enum ExpressionToken {
98 Literal(String),
99 Variable(String),
100 DoubleQuotes(Vec<ExpressionToken>),
101 SingleQuotes(String),
102 Backtick(String),
103 Function(Box<FunctionCall>),
104 Space,
105}
106
107pub fn parse_expression_token_variable_parameter(
108 input: KconfigInput,
109) -> IResult<KconfigInput, ExpressionToken> {
110 map(
111 delimited(
112 tag("$("),
113 recognize(ws(many1(alt((alphanumeric1, recognize(one_of("_-"))))))),
114 tag(")"),
115 ),
116 |d: KconfigInput| ExpressionToken::Variable(d.fragment().to_string()),
117 )
118 .parse(input)
119}
120
121fn parse_expression_token_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
122 alt((
123 map(tag("="), |_| ExpressionToken::Literal("=".to_string())),
124 map(space1, |_| ExpressionToken::Space),
125 map(tag("2>"), |_| ExpressionToken::Literal("2>".to_string())),
126 map(
127 delimited(tag("\""), parse_expression_parameter, tag("\"")),
128 ExpressionToken::DoubleQuotes,
129 ),
130 map(
131 delimited(tag("("), take_until(")"), tag(")")),
132 |d: KconfigInput| ExpressionToken::Literal("(".to_string() + d.fragment() + ")"),
133 ),
134 map(
135 delimited(tag("`"), take_until("`"), tag("`")),
136 |d: KconfigInput| ExpressionToken::Backtick(d.to_string()),
137 ),
138 map(
139 delimited(
140 ws(char::<KconfigInput, _>('\'')),
141 take_until("'"),
142 char('\''),
143 ),
144 |d| ExpressionToken::SingleQuotes(d.to_string()),
145 ),
146 parse_literal_parameter,
147 parse_expression_token_variable_parameter,
148 map(parse_function_call, |f| {
149 ExpressionToken::Function(Box::new(f))
150 }),
151 ))
152 .parse(input)
153}
154
155fn parse_instruction_parameter(input: KconfigInput) -> IResult<KconfigInput, String> {
156 map(
157 (
158 tag("%"),
159 recognize(ws(many1(alt((alphanumeric1, recognize(one_of("_"))))))),
160 delimited(tag("("), alphanumeric1, tag(")")),
161 ),
162 |(_a, b, c)| format!("%{}({})", b, c),
163 )
164 .parse(input)
165}
166
167fn parse_env_variable_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
168 map(
169 ws(preceded(tag("$"), recognize(many1(alphanumeric1)))),
170 |d| ExpressionToken::Literal(format!("${}", d)),
171 )
172 .parse(input)
173}
174
175fn parse_literal_parameter(input: KconfigInput) -> IResult<KconfigInput, ExpressionToken> {
176 alt((
177 parse_env_variable_parameter,
178 map(parse_instruction_parameter, ExpressionToken::Literal),
179 map(
180 recognize(ws(many1(alt((
181 alphanumeric1,
182 tag("\\$"),
183 recognize(one_of("+(<>%@&\\[]_|'.-:\n\\/")),
184 ))))),
185 |d: KconfigInput| ExpressionToken::Literal(d.fragment().to_string()),
186 ),
187 ))
188 .parse(input)
189}
190
191pub fn parse_expression_parameter(
192 input: KconfigInput,
193) -> IResult<KconfigInput, Vec<ExpressionToken>> {
194 alt((many1(parse_expression_token_parameter),)).parse(input)
195}
196
197pub fn parse_parameter(input: KconfigInput) -> IResult<KconfigInput, Parameter> {
198 map(alt((parse_expression_parameter,)), |d| Parameter {
199 tokens: d,
200 })
201 .parse(input)
202}
203
204fn parse_function_name(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, &str> {
205 map(
206 recognize(ws(many1(alt((alphanumeric1, recognize(one_of("=-_"))))))),
207 |d: KconfigInput| d.fragment().to_owned(),
208 )
209 .parse(input)
210}
211
212pub fn parse_function_call(input: KconfigInput) -> IResult<KconfigInput, FunctionCall> {
213 ws(alt((
214 parse_function_call_inner,
215 delimited(char('"'), parse_function_call_inner, char('"')),
216 )))
217 .parse(input)
218}
219
220fn parse_function_call_inner(input: KconfigInput) -> IResult<KconfigInput, FunctionCall> {
221 map(
222 delimited(
223 tag("$("),
224 (
225 terminated(parse_function_name, ws(tag(","))),
226 separated_list0(ws(tag(",")), ws(parse_parameter)),
227 ),
228 ws(tag(")")),
229 ),
230 |(name, parameters)| FunctionCall {
231 name: name.to_string(),
232 parameters,
233 },
234 )
235 .parse(input)
236}