Skip to main content

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        alt((
72            delimited(tag("'"), take_until_unbalanced('\''), tag("'")),
73            delimited(tag("\""), take_until_unbalanced('"'), tag("\"")),
74        )),
75        |d| d.fragment().to_string(),
76    )
77    .parse(input)
78}
79
80pub fn take_until_unbalanced(
81    delimiter: char,
82) -> impl Fn(KconfigInput) -> IResult<KconfigInput, KconfigInput> {
83    move |i: KconfigInput| {
84        let mut index: usize = 0;
85        let mut delimiter_counter = 0;
86
87        let end_of_line = match &i.find('\n') {
88            Some(e) => *e,
89            None => i.len(),
90        };
91
92        while let Some(n) = &i[index..end_of_line].find(delimiter) {
93            delimiter_counter += 1;
94            index += n + 1;
95        }
96
97        // we split just before the last double quote
98        index -= 1;
99        // Last delimiter is the string delimiter
100        delimiter_counter -= 1;
101
102        match delimiter_counter % 2 == 0 {
103            true => Ok(i.take_split(index)),
104            false => Err(nom::Err::Error(Error::from_error_kind(
105                i,
106                ErrorKind::TakeUntil,
107            ))),
108        }
109    }
110}
111
112// TODO ugly
113pub fn parse_number_or_symbol(input: KconfigInput) -> IResult<KconfigInput, Atom> {
114    let (input, sym) = parse_symbol(input)?;
115    match sym.clone() {
116        Symbol::Constant(s) => match string_to_number(s.as_str()) {
117            Ok((_, i)) => Ok((input, Atom::Number(i))),
118            Err(_) => Ok((input, Atom::Symbol(sym))),
119        },
120        Symbol::NonConstant(_) => Ok((input, Atom::Symbol(sym))),
121    }
122}
123
124pub fn string_to_number(input: &str) -> IResult<&str, i64> {
125    all_consuming(map_res(recognize(pair(opt(char('-')), digit1)), |d| {
126        FromStr::from_str(d)
127    }))
128    .parse(input)
129}