nom_kconfig/attribute/expression/
atom.rs

1use crate::attribute::expression::parse_compare;
2use crate::attribute::{
3    parse_expression, parse_function_call, CompareExpression, Expression, FunctionCall,
4};
5use crate::number::parse_number;
6use crate::symbol::parse_symbol;
7use crate::util::wsi;
8use crate::{KconfigInput, Symbol};
9use nom::{
10    branch::alt,
11    bytes::complete::tag,
12    character::complete::{char, digit1},
13    combinator::{all_consuming, map, map_res, opt, recognize},
14    error::{Error, ErrorKind, ParseError},
15    sequence::{delimited, pair},
16    IResult, Input, Parser,
17};
18#[cfg(feature = "deserialize")]
19use serde::Deserialize;
20#[cfg(feature = "serialize")]
21use serde::Serialize;
22#[cfg(feature = "display")]
23use std::fmt::Display;
24use std::str::FromStr;
25
26#[derive(Debug, PartialEq, Clone)]
27#[cfg_attr(feature = "hash", derive(Hash))]
28#[cfg_attr(feature = "serialize", derive(Serialize))]
29#[cfg_attr(feature = "deserialize", derive(Deserialize))]
30pub enum Atom {
31    Symbol(Symbol),
32    Number(i64),
33    Compare(CompareExpression),
34    Function(FunctionCall),
35    Parenthesis(Box<Expression>),
36    String(String),
37}
38
39#[cfg(feature = "display")]
40impl Display for Atom {
41    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
42        match self {
43            Atom::Symbol(s) => write!(f, "{}", s),
44            Atom::Number(d) => write!(f, "{}", d),
45            Atom::Compare(c) => write!(f, "{}", c),
46            Atom::Function(func) => write!(f, "{}", func),
47            Atom::Parenthesis(d) => write!(f, "({})", d),
48            Atom::String(s) => write!(f, r#""{}""#, s),
49        }
50    }
51}
52
53pub fn parse_atom(input: KconfigInput) -> IResult<KconfigInput, Atom> {
54    alt((
55        wsi(parse_compare),
56        map(parse_function_call, Atom::Function),
57        map(
58            delimited(wsi(tag("(")), parse_expression, wsi(tag(")"))),
59            |expr: Expression| Atom::Parenthesis(Box::new(expr)),
60        ),
61        parse_number_or_symbol,
62        // needed to parse negative numbers, see test_parse_expression_number() in expression_test.rs
63        map(parse_number, Atom::Number),
64        map(parse_string, Atom::String),
65    ))
66    .parse(input)
67}
68
69pub fn parse_string(input: KconfigInput) -> IResult<KconfigInput, String> {
70    map(
71        delimited(tag("\""), take_until_unbalanced('"'), tag("\"")),
72        |d| d.fragment().to_string(),
73    )
74    .parse(input)
75}
76
77pub fn take_until_unbalanced(
78    delimiter: char,
79) -> impl Fn(KconfigInput) -> IResult<KconfigInput, KconfigInput> {
80    move |i: KconfigInput| {
81        let mut index: usize = 0;
82        let mut delimiter_counter = 0;
83
84        let end_of_line = match &i.find('\n') {
85            Some(e) => *e,
86            None => i.len(),
87        };
88
89        while let Some(n) = &i[index..end_of_line].find(delimiter) {
90            delimiter_counter += 1;
91            index += n + 1;
92        }
93
94        // we split just before the last double quote
95        index -= 1;
96        // Last delimiter is the string delimiter
97        delimiter_counter -= 1;
98
99        match delimiter_counter % 2 == 0 {
100            true => Ok(i.take_split(index)),
101            false => Err(nom::Err::Error(Error::from_error_kind(
102                i,
103                ErrorKind::TakeUntil,
104            ))),
105        }
106    }
107}
108
109// TODO ugly
110pub fn parse_number_or_symbol(input: KconfigInput) -> IResult<KconfigInput, Atom> {
111    let (input, sym) = parse_symbol(input)?;
112    match sym.clone() {
113        Symbol::Constant(s) => match string_to_number(s.as_str()) {
114            Ok((_, i)) => Ok((input, Atom::Number(i))),
115            Err(_) => Ok((input, Atom::Symbol(sym))),
116        },
117        Symbol::NonConstant(_) => Ok((input, Atom::Symbol(sym))),
118    }
119}
120
121pub fn string_to_number(input: &str) -> IResult<&str, i64> {
122    all_consuming(map_res(recognize(pair(opt(char('-')), digit1)), |d| {
123        FromStr::from_str(d)
124    }))
125    .parse(input)
126}