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, tuple},
13    IResult, InputTake,
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        tuple((
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    )(input)
201}
202
203pub fn parse_and_expression(input: KconfigInput) -> IResult<KconfigInput, AndExpression> {
204    map(
205        tuple((
206            wsi(parse_term),
207            many0(preceded(wsi(tag("&&")), wsi(parse_term))),
208        )),
209        |(l, ee)| {
210            if ee.is_empty() {
211                AndExpression::Term(l)
212            } else {
213                let mut ll = vec![l];
214                ll.extend(ee);
215                AndExpression::Expression(ll)
216            }
217        },
218    )(input)
219}
220
221pub fn parse_term(input: KconfigInput) -> IResult<KconfigInput, Term> {
222    alt((
223        map(preceded(wsi(tag("!")), parse_atom), Term::Not),
224        map(parse_atom, Term::Atom),
225    ))(input)
226}
227
228pub fn parse_atom(input: KconfigInput) -> IResult<KconfigInput, Atom> {
229    alt((
230        wsi(parse_compare),
231        map(parse_function_call, Atom::Function),
232        map(
233            delimited(wsi(tag("(")), parse_expression, wsi(tag(")"))),
234            |expr: Expression| Atom::Parenthesis(Box::new(expr)),
235        ),
236        map(parse_string, Atom::String),
237        parse_number_or_symbol,
238        // needed to parse negative numbers, see test_parse_expression_number() in expression_test.rs
239        map(parse_number, Atom::Number),
240    ))(input)
241}
242
243pub fn parse_string(input: KconfigInput) -> IResult<KconfigInput, String> {
244    map(
245        delimited(tag("\""), take_until_unbalanced('"'), tag("\"")),
246        |d| d.fragment().to_string(),
247    )(input)
248}
249
250pub fn take_until_unbalanced(
251    delimiter: char,
252) -> impl Fn(KconfigInput) -> IResult<KconfigInput, KconfigInput> {
253    move |i: KconfigInput| {
254        let mut index: usize = 0;
255        let mut delimiter_counter = 0;
256
257        let end_of_line = match &i.find('\n') {
258            Some(e) => *e,
259            None => i.len(),
260        };
261
262        while let Some(n) = &i[index..end_of_line].find(delimiter) {
263            delimiter_counter += 1;
264            index += n + 1;
265        }
266
267        // we split just before the last double quote
268        index -= 1;
269        // Last delimiter is the string delimiter
270        delimiter_counter -= 1;
271
272        match delimiter_counter % 2 == 0 {
273            true => Ok(i.take_split(index)),
274            false => Err(nom::Err::Error(Error::from_error_kind(
275                i,
276                ErrorKind::TakeUntil,
277            ))),
278        }
279    }
280}
281
282pub fn parse_expression(input: KconfigInput) -> IResult<KconfigInput, Expression> {
283    parse_or_expression(input)
284}
285
286pub fn parse_compare_operator(input: KconfigInput) -> IResult<KconfigInput, CompareOperator> {
287    alt((
288        value(CompareOperator::GreaterOrEqual, tag(">=")),
289        value(CompareOperator::LowerOrEqual, tag("<=")),
290        value(CompareOperator::GreaterThan, tag(">")),
291        value(CompareOperator::LowerThan, tag("<")),
292        value(CompareOperator::Equal, tag("=")),
293        value(CompareOperator::NotEqual, tag("!=")),
294    ))(input)
295}
296
297pub fn parse_compare(input: KconfigInput) -> IResult<KconfigInput, Atom> {
298    map(
299        tuple((
300            wsi(parse_symbol),
301            wsi(parse_compare_operator),
302            wsi(parse_symbol),
303        )),
304        |(l, o, r)| {
305            Atom::Compare(CompareExpression {
306                left: l,
307                operator: o,
308                right: r,
309            })
310        },
311    )(input)
312}
313
314// TODO ugly
315pub fn parse_number_or_symbol(input: KconfigInput) -> IResult<KconfigInput, Atom> {
316    let (input, sym) = parse_symbol(input)?;
317    match sym.clone() {
318        Symbol::Constant(s) => match string_to_number(s.as_str()) {
319            Ok((_, i)) => Ok((input, Atom::Number(i))),
320            Err(_) => Ok((input, Atom::Symbol(sym))),
321        },
322        Symbol::NonConstant(_) => Ok((input, Atom::Symbol(sym))),
323    }
324}
325
326pub fn string_to_number(input: &str) -> IResult<&str, i64> {
327    all_consuming(map_res(recognize(pair(opt(char('-')), digit1)), |d| {
328        FromStr::from_str(d)
329    }))(input)
330}
331
332pub fn parse_if_attribute(input: KconfigInput) -> IResult<KconfigInput, Option<Expression>> {
333    opt(parse_if_expression)(input)
334}
335
336pub fn parse_if_expression(input: KconfigInput) -> IResult<KconfigInput, Expression> {
337    map(pair(wsi(tag("if")), wsi(parse_expression)), |(_, e)| e)(input)
338}
339
340pub fn parse_number(input: KconfigInput) -> IResult<KconfigInput, i64> {
341    map_res(
342        recognize(pair(opt(char('-')), digit1)),
343        |d: KconfigInput| FromStr::from_str(d.fragment()),
344    )(input)
345}