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