nom_kconfig/attribute/
expression.rs

1#[cfg(feature = "display")]
2use std::fmt::Display;
3use std::str::FromStr;
4
5use nom::{
6    branch::alt,
7    bytes::complete::tag,
8    character::complete::{char, digit1},
9    combinator::{all_consuming, map, map_res, opt, recognize, value},
10    error::{Error, ErrorKind, ParseError},
11    multi::many0,
12    sequence::{delimited, pair, preceded},
13    IResult, Input, Parser,
14};
15#[cfg(feature = "deserialize")]
16use serde::Deserialize;
17#[cfg(feature = "serialize")]
18use serde::Serialize;
19
20use crate::{
21    symbol::{parse_symbol, Symbol},
22    util::wsi,
23    KconfigInput,
24};
25
26use super::function::{parse_function_call, FunctionCall};
27
28#[derive(Debug, PartialEq, Clone)]
29#[cfg_attr(feature = "serialize", derive(Serialize))]
30#[cfg_attr(feature = "deserialize", derive(Deserialize))]
31pub enum Operator {
32    And,
33    Or,
34}
35
36#[derive(Debug, PartialEq, Clone)]
37#[cfg_attr(feature = "hash", derive(Hash))]
38#[cfg_attr(feature = "serialize", derive(Serialize))]
39#[cfg_attr(feature = "deserialize", derive(Deserialize))]
40pub enum CompareOperator {
41    GreaterThan,
42    GreaterOrEqual,
43    LowerThan,
44    LowerOrEqual,
45    Equal,
46    NotEqual,
47}
48
49#[cfg(feature = "display")]
50impl Display for CompareOperator {
51    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
52        match self {
53            CompareOperator::GreaterThan => write!(f, ">"),
54            CompareOperator::GreaterOrEqual => write!(f, ">="),
55            CompareOperator::LowerThan => write!(f, "<"),
56            CompareOperator::LowerOrEqual => write!(f, "<="),
57            CompareOperator::Equal => write!(f, "="),
58            CompareOperator::NotEqual => write!(f, "!="),
59        }
60    }
61}
62
63// https://stackoverflow.com/questions/9509048/antlr-parser-for-and-or-logic-how-to-get-expressions-between-logic-operators
64pub type Expression = OrExpression;
65#[cfg_attr(feature = "hash", derive(Hash))]
66#[cfg_attr(feature = "serialize", derive(Serialize))]
67#[cfg_attr(feature = "deserialize", derive(Deserialize))]
68#[derive(Debug, PartialEq, Clone)]
69pub enum AndExpression {
70    #[cfg_attr(feature = "serialize", serde(rename = "AndTerm"))]
71    Term(Term),
72    #[cfg_attr(feature = "serialize", serde(rename = "And"))]
73    Expression(Vec<Term>),
74}
75
76#[derive(Debug, PartialEq, Clone)]
77#[cfg_attr(feature = "hash", derive(Hash))]
78#[cfg_attr(feature = "serialize", derive(Serialize))]
79#[cfg_attr(feature = "deserialize", derive(Deserialize))]
80pub enum OrExpression {
81    #[cfg_attr(feature = "serialize", serde(rename = "OrTerm"))]
82    Term(AndExpression),
83    #[cfg_attr(feature = "serialize", serde(rename = "Or"))]
84    Expression(Vec<AndExpression>),
85}
86
87#[derive(Debug, PartialEq, Clone)]
88#[cfg_attr(feature = "hash", derive(Hash))]
89#[cfg_attr(feature = "serialize", derive(Serialize))]
90#[cfg_attr(feature = "deserialize", derive(Deserialize))]
91pub enum Term {
92    Not(Atom),
93    Atom(Atom),
94}
95
96#[derive(Debug, PartialEq, Clone)]
97#[cfg_attr(feature = "hash", derive(Hash))]
98#[cfg_attr(feature = "serialize", derive(Serialize))]
99#[cfg_attr(feature = "deserialize", derive(Deserialize))]
100#[cfg_attr(feature = "serialize", serde(rename = "Compare"))]
101pub struct CompareExpression {
102    pub left: Symbol,
103    pub operator: CompareOperator,
104    pub right: Symbol,
105}
106
107#[cfg(feature = "display")]
108impl Display for CompareExpression {
109    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
110        write!(f, "{} {} {}", self.left, self.operator, self.right)
111    }
112}
113
114#[derive(Debug, PartialEq, Clone)]
115#[cfg_attr(feature = "hash", derive(Hash))]
116#[cfg_attr(feature = "serialize", derive(Serialize))]
117#[cfg_attr(feature = "deserialize", derive(Deserialize))]
118pub enum Atom {
119    Symbol(Symbol),
120    Number(i64),
121    Compare(CompareExpression),
122    Function(FunctionCall),
123    Parenthesis(Box<Expression>),
124    String(String),
125}
126
127#[cfg(feature = "display")]
128impl Display for AndExpression {
129    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
130        match self {
131            Self::Term(t) => write!(f, "{}", t),
132            Self::Expression(t) => write!(
133                f,
134                "{}",
135                t.iter()
136                    .map(|a| a.to_string())
137                    .collect::<Vec<_>>()
138                    .join(" && ")
139            ),
140        }
141    }
142}
143
144#[cfg(feature = "display")]
145impl Display for OrExpression {
146    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
147        match self {
148            Self::Term(t) => write!(f, "{}", t),
149            Self::Expression(t) => write!(
150                f,
151                "{}",
152                t.iter()
153                    .map(|a| a.to_string())
154                    .collect::<Vec<_>>()
155                    .join(" || ")
156            ),
157        }
158    }
159}
160
161#[cfg(feature = "display")]
162impl Display for Term {
163    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
164        match self {
165            Term::Not(atom) => write!(f, "!{}", atom),
166            Term::Atom(atom) => write!(f, "{}", atom),
167        }
168    }
169}
170
171#[cfg(feature = "display")]
172impl Display for Atom {
173    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
174        match self {
175            Atom::Symbol(s) => write!(f, "{}", s),
176            Atom::Number(d) => write!(f, "{}", d),
177            Atom::Compare(c) => write!(f, "{}", c),
178            Atom::Function(func) => write!(f, "{}", func),
179            Atom::Parenthesis(d) => write!(f, "({})", d),
180            Atom::String(s) => write!(f, r#""{}""#, s),
181        }
182    }
183}
184
185pub fn parse_or_expression(input: KconfigInput) -> IResult<KconfigInput, OrExpression> {
186    map(
187        (
188            wsi(parse_and_expression),
189            many0(preceded(wsi(tag("||")), wsi(parse_and_expression))),
190        ),
191        |(l, ee)| {
192            if ee.is_empty() {
193                OrExpression::Term(l)
194            } else {
195                let mut ll = vec![l];
196                ll.extend(ee);
197                OrExpression::Expression(ll)
198            }
199        },
200    )
201    .parse(input)
202}
203
204pub fn parse_and_expression(input: KconfigInput) -> IResult<KconfigInput, AndExpression> {
205    map(
206        (
207            wsi(parse_term),
208            many0(preceded(wsi(tag("&&")), wsi(parse_term))),
209        ),
210        |(l, ee)| {
211            if ee.is_empty() {
212                AndExpression::Term(l)
213            } else {
214                let mut ll = vec![l];
215                ll.extend(ee);
216                AndExpression::Expression(ll)
217            }
218        },
219    )
220    .parse(input)
221}
222
223pub fn parse_term(input: KconfigInput) -> IResult<KconfigInput, Term> {
224    alt((
225        map(preceded(wsi(tag("!")), parse_atom), Term::Not),
226        map(parse_atom, Term::Atom),
227    ))
228    .parse(input)
229}
230
231pub fn parse_atom(input: KconfigInput) -> IResult<KconfigInput, Atom> {
232    alt((
233        wsi(parse_compare),
234        map(parse_function_call, Atom::Function),
235        map(
236            delimited(wsi(tag("(")), parse_expression, wsi(tag(")"))),
237            |expr: Expression| Atom::Parenthesis(Box::new(expr)),
238        ),
239        map(parse_string, Atom::String),
240        parse_number_or_symbol,
241        // needed to parse negative numbers, see test_parse_expression_number() in expression_test.rs
242        map(parse_number, Atom::Number),
243    ))
244    .parse(input)
245}
246
247pub fn parse_string(input: KconfigInput) -> IResult<KconfigInput, String> {
248    map(
249        delimited(tag("\""), take_until_unbalanced('"'), tag("\"")),
250        |d| d.fragment().to_string(),
251    )
252    .parse(input)
253}
254
255pub fn take_until_unbalanced(
256    delimiter: char,
257) -> impl Fn(KconfigInput) -> IResult<KconfigInput, KconfigInput> {
258    move |i: KconfigInput| {
259        let mut index: usize = 0;
260        let mut delimiter_counter = 0;
261
262        let end_of_line = match &i.find('\n') {
263            Some(e) => *e,
264            None => i.len(),
265        };
266
267        while let Some(n) = &i[index..end_of_line].find(delimiter) {
268            delimiter_counter += 1;
269            index += n + 1;
270        }
271
272        // we split just before the last double quote
273        index -= 1;
274        // Last delimiter is the string delimiter
275        delimiter_counter -= 1;
276
277        match delimiter_counter % 2 == 0 {
278            true => Ok(i.take_split(index)),
279            false => Err(nom::Err::Error(Error::from_error_kind(
280                i,
281                ErrorKind::TakeUntil,
282            ))),
283        }
284    }
285}
286
287pub fn parse_expression(input: KconfigInput) -> IResult<KconfigInput, Expression> {
288    parse_or_expression(input)
289}
290
291pub fn parse_compare_operator(input: KconfigInput) -> IResult<KconfigInput, CompareOperator> {
292    alt((
293        value(CompareOperator::GreaterOrEqual, tag(">=")),
294        value(CompareOperator::LowerOrEqual, tag("<=")),
295        value(CompareOperator::GreaterThan, tag(">")),
296        value(CompareOperator::LowerThan, tag("<")),
297        value(CompareOperator::Equal, tag("=")),
298        value(CompareOperator::NotEqual, tag("!=")),
299    ))
300    .parse(input)
301}
302
303pub fn parse_compare(input: KconfigInput) -> IResult<KconfigInput, Atom> {
304    map(
305        (
306            wsi(parse_symbol),
307            wsi(parse_compare_operator),
308            wsi(parse_symbol),
309        ),
310        |(l, o, r)| {
311            Atom::Compare(CompareExpression {
312                left: l,
313                operator: o,
314                right: r,
315            })
316        },
317    )
318    .parse(input)
319}
320
321// TODO ugly
322pub fn parse_number_or_symbol(input: KconfigInput) -> IResult<KconfigInput, Atom> {
323    let (input, sym) = parse_symbol(input)?;
324    match sym.clone() {
325        Symbol::Constant(s) => match string_to_number(s.as_str()) {
326            Ok((_, i)) => Ok((input, Atom::Number(i))),
327            Err(_) => Ok((input, Atom::Symbol(sym))),
328        },
329        Symbol::NonConstant(_) => Ok((input, Atom::Symbol(sym))),
330    }
331}
332
333pub fn string_to_number(input: &str) -> IResult<&str, i64> {
334    all_consuming(map_res(recognize(pair(opt(char('-')), digit1)), |d| {
335        FromStr::from_str(d)
336    }))
337    .parse(input)
338}
339
340pub fn parse_if_attribute(input: KconfigInput) -> IResult<KconfigInput, Option<Expression>> {
341    opt(parse_if_expression).parse(input)
342}
343
344pub fn parse_if_expression(input: KconfigInput) -> IResult<KconfigInput, Expression> {
345    map(pair(wsi(tag("if")), wsi(parse_expression)), |(_, e)| e).parse(input)
346}
347
348pub fn parse_number(input: KconfigInput) -> IResult<KconfigInput, i64> {
349    map_res(
350        recognize(pair(opt(char('-')), digit1)),
351        |d: KconfigInput| FromStr::from_str(d.fragment()),
352    )
353    .parse(input)
354}