android_bp/
parser.rs

1use crate::string::parse_string;
2use crate::{context_tag, end_delimiter, utils::*};
3use nom::combinator::map_res;
4use nom::Err;
5use nom::{
6    branch::alt,
7    bytes::complete::tag,
8    character::complete::char,
9    combinator::{cut, map, opt},
10    error::{context, convert_error, VerboseError},
11    multi::{many0, separated_list0},
12    sequence::{delimited, tuple},
13};
14use std::collections::HashMap;
15use std::ops::{Deref, DerefMut};
16use std::path::Path;
17
18/// a dictionary in a blueprint file
19#[derive(Debug, PartialEq, Clone, Eq)]
20pub struct Map(pub HashMap<String, Value>);
21impl Deref for Map {
22    type Target = HashMap<String, Value>;
23    fn deref(&self) -> &Self::Target {
24        &self.0
25    }
26}
27impl DerefMut for Map {
28    fn deref_mut(&mut self) -> &mut Self::Target {
29        &mut self.0
30    }
31}
32fn parse_dict(input: &str) -> VerboseResult<Map> {
33    context(
34        "dict",
35        map(
36            delimited(
37                tuple((space_or_comments, context_tag!("{"), space_or_comments)),
38                separated_list0(char(','), parse_module_entry),
39                end_delimiter!("}"),
40            ),
41            |entries| Map(entries.into_iter().collect()),
42        ),
43    )(input)
44}
45#[derive(Debug, PartialEq, Clone, Eq)]
46pub struct Function {
47    pub name: String,
48    pub args: Vec<Value>,
49}
50fn parse_function(input: &str) -> VerboseResult<Function> {
51    context(
52        "function",
53        map(
54            tuple((
55                space_or_comments,
56                identifier,
57                space_or_comments,
58                delimited(
59                    tuple((space_or_comments, context_tag!("("), space_or_comments)),
60                    separated_list0(comma, parse_expr),
61                    end_delimiter!(")"),
62                ),
63            )),
64            |(_, name, _, args)| Function {
65                name: name.to_string(),
66                args,
67            },
68        ),
69    )(input)
70}
71/// a value in a blueprint file
72#[derive(Debug, PartialEq, Clone, Eq)]
73pub enum Value {
74    String(String),
75    Integer(i64),
76    Array(Vec<Value>),
77    Boolean(bool),
78    Map(Map),
79    Ident(String),
80    ConcatExpr(Vec<Value>),
81    Function(Function),
82}
83// convert value from str
84impl From<&str> for Value {
85    fn from(s: &str) -> Self {
86        Value::String(s.to_string())
87    }
88}
89fn parse_value(input: &str) -> VerboseResult<Value> {
90    context(
91        "value",
92        alt((
93            map(parse_array, Value::Array),
94            map(parse_function, Value::Function),
95            map(string_literal, Value::String),
96            map(parse_bool, Value::Boolean),
97            map(parse_dict, Value::Map),
98            map(parse_int, Value::Integer),
99            map(identifier, |x| Value::Ident(x.to_string())),
100        )),
101    )(input)
102}
103fn concat_value_string(values: Vec<Value>) -> Result<Value, &'static str> {
104    let mut result = String::new();
105    for value in values {
106        match value {
107            Value::String(s) => result.push_str(&s),
108            _ => Err("value is not a string")?,
109        }
110    }
111    Ok(Value::String(result))
112}
113fn concat_value_array(values: Vec<Value>) -> Result<Value, &'static str> {
114    let mut result = Vec::new();
115    for value in values {
116        match value {
117            Value::Array(a) => result.extend(a),
118            _ => Err("value is not an array")?,
119        }
120    }
121    Ok(Value::Array(result))
122}
123pub(crate) fn parse_expr(input: &str) -> VerboseResult<Value> {
124    // in bp, value can be combined with '+' operator
125    // this parser parse the expression and combine the values
126    // into a single value, if there is no Ident in the values
127    context(
128        "expr",
129        map_res(
130            separated_list0(
131                tuple((space_or_comments, char('+'), space_or_comments)),
132                parse_value,
133            ),
134            |values| {
135                match values.len() {
136                    0 => Err("no value"),
137                    1 => Ok(values[0].clone()),
138                    _ => {
139                        // if there is one ident we cannot concat
140                        if values
141                            .iter()
142                            .any(|v| matches!(v, Value::Ident(_) | Value::Function(_)))
143                        {
144                            return Ok(Value::ConcatExpr(values));
145                        }
146                        match &values[0] {
147                            Value::String(_) => concat_value_string(values),
148                            Value::Array(_) => concat_value_array(values),
149                            _ => Err("first value is not a string"),
150                        }
151                    }
152                }
153            },
154        ),
155    )(input)
156}
157pub(crate) fn parse_array(input: &str) -> VerboseResult<Vec<Value>> {
158    context(
159        "array",
160        delimited(
161            ws(char('[')),
162            separated_list0(comma, parse_expr),
163            end_delimiter!("]"),
164        ),
165    )(input)
166}
167
168/// a blueprint file
169#[derive(Debug, PartialEq, Clone, Eq)]
170pub struct BluePrint {
171    /// variables in the blueprint file
172    /// found in root of the file in the form of `key = value`
173    pub variables: HashMap<String, Value>,
174    /// all ordered modules in the blueprint file
175    pub modules: Vec<Module>,
176}
177
178/// a module in a blueprint file
179#[derive(Debug, PartialEq, Clone, Eq)]
180pub struct Module {
181    pub typ: String,
182    pub entries: HashMap<String, Value>,
183}
184impl Module {
185    /// get an attribute value from a module
186    pub fn get(&self, key: &str) -> Option<&Value> {
187        self.entries.get(key)
188    }
189    /// get a string attribute value from a module
190    pub fn get_string(&self, key: &str) -> Option<&String> {
191        match self.get(key) {
192            Some(Value::String(s)) => Some(s),
193            _ => None,
194        }
195    }
196    /// get a boolean attribute value from a module
197    pub fn get_bool(&self, key: &str) -> Option<bool> {
198        match self.get(key) {
199            Some(Value::Boolean(b)) => Some(*b),
200            _ => None,
201        }
202    }
203    /// get an array attribute value from a module
204    pub fn get_array(&self, key: &str) -> Option<&Vec<Value>> {
205        match self.get(key) {
206            Some(Value::Array(a)) => Some(a),
207            _ => None,
208        }
209    }
210    /// get a map attribute value from a module
211    pub fn get_map(&self, key: &str) -> Option<&Map> {
212        match self.get(key) {
213            Some(Value::Map(d)) => Some(d),
214            _ => None,
215        }
216    }
217    /// get an identifier attribute value from a module
218    pub fn get_ident(&self, key: &str) -> Option<&String> {
219        match self.get(key) {
220            Some(Value::Ident(i)) => Some(i),
221            _ => None,
222        }
223    }
224    /// get an integer attribute value from a module
225    pub fn get_int(&self, key: &str) -> Option<i64> {
226        match self.get(key) {
227            Some(Value::Integer(i)) => Some(*i),
228            _ => None,
229        }
230    }
231}
232/// parse a module entry, with `:` as delimiter
233pub(crate) fn parse_module_entry(input: &str) -> VerboseResult<(String, Value)> {
234    _parse_module_entry(input, ':')
235}
236/// second form of module entry, with `=` as delimiter
237pub(crate) fn parse_module_entry2(input: &str) -> VerboseResult<(String, Value)> {
238    _parse_module_entry(input, '=')
239}
240pub(crate) fn _parse_module_entry(input: &str, delimiter: char) -> VerboseResult<(String, Value)> {
241    context(
242        "module entry",
243        map(
244            tuple((
245                space_or_comments,
246                alt((
247                    map(identifier, |x| x.to_string()),
248                    parse_string::<VerboseError<&str>>,
249                )),
250                space_or_comments,
251                char(delimiter),
252                space_or_comments,
253                cut(parse_expr),
254                space_or_comments,
255            )),
256            |(_, key, _, _, _, value, _)| (key.to_string(), value),
257        ),
258    )(input)
259}
260
261pub(crate) fn parse_module(input: &str) -> VerboseResult<Module> {
262    // parse a identifier followed by a module of entries
263    let (input, _) = space_or_comments(input)?;
264    let (input, ident) = identifier(input)?;
265    let (input, _) = space_or_comments(input)?;
266    let (input, module) = context(
267        "module",
268        alt((
269            map(
270                delimited(
271                    tuple((space_or_comments, context_tag!("{"), space_or_comments)),
272                    separated_list0(char(','), parse_module_entry),
273                    end_delimiter!("}"),
274                ),
275                |entries| entries.into_iter().collect(),
276            ),
277            map(
278                delimited(
279                    tuple((space_or_comments, context_tag!("("), space_or_comments)),
280                    separated_list0(char(','), parse_module_entry2),
281                    end_delimiter!(")"),
282                ),
283                |entries| entries.into_iter().collect(),
284            ),
285        )),
286    )(input)?;
287    Ok((
288        input,
289        Module {
290            typ: ident.to_string(),
291            entries: module,
292        },
293    ))
294}
295
296pub(crate) fn parse_define(input: &str) -> VerboseResult<(String, String, Value)> {
297    context(
298        "define",
299        map(
300            tuple((
301                space_or_comments,
302                identifier,
303                space_or_comments,
304                alt((tag("="), tag("+="))),
305                space_or_comments,
306                cut(parse_expr),
307                space_or_comments,
308            )),
309            |(_, key, _, op, _, value, _)| (key.to_string(), op.to_string(), value),
310        ),
311    )(input)
312}
313
314pub(crate) fn parse_blueprint(input: &str) -> VerboseResult<BluePrint> {
315    let mut entries = Vec::new();
316    let mut variables = HashMap::new();
317    let (input, _) = context(
318        "blueprint",
319        many0(alt((
320            map(parse_module, |b| {
321                entries.push(b);
322                ()
323            }),
324            map_res(parse_define, |(k, op, v)| match op.as_str() {
325                "=" => {
326                    variables.insert(k, v);
327                    Ok(())
328                }
329                "+=" => {
330                    let e = variables.entry(k);
331                    match e {
332                        std::collections::hash_map::Entry::Occupied(prev) => {
333                            let prev = prev.into_mut();
334                            match prev {
335                                Value::String(s) => {
336                                    match v {
337                                        Value::String(s2) => {
338                                            s.push_str(&s2);
339                                        }
340                                        _ => Err("cannot append value to string")?,
341                                    }
342                                }
343                                Value::Array(a) => {
344                                    match v {
345                                        Value::Array(a2) => {
346                                            a.extend(a2);
347                                        }
348                                        Value::Ident(_) => {
349                                            Err("FIXME in this case, we should turn the Array into ConcatExpr")?
350                                        }
351                                        _ => Err("cannot append value to array")?,
352                                    }
353                                }
354                                Value::Integer(i) => {
355                                    match v {
356                                        Value::Integer(i2) => {
357                                            *i += i2;
358                                        }
359                                        _ => Err("cannot append value to integer")?,
360                                    }
361                                }
362                                _ => Err("cannot append value to this type")?,
363                            }
364                        }
365                        std::collections::hash_map::Entry::Vacant(_) => Err("variable not found")?,
366                    }
367                    Ok(())
368                }
369                _ => Err("unknown operator"),
370            }),
371            space_or_comments1,
372        ))),
373    )(input)?;
374    Ok((
375        input,
376        BluePrint {
377            variables: variables,
378            modules: entries,
379        },
380    ))
381}
382
383pub(crate) fn format_err(input: &str, err: Err<VerboseError<&str>>) -> String {
384    match err {
385        Err::Error(e) | Err::Failure(e) => convert_error(input, e.into()),
386        Err::Incomplete(_) => "Incomplete".to_string(),
387    }
388}
389impl BluePrint {
390    /// parse an Android.bp file from a string
391    pub fn parse(input: &str) -> Result<Self, String> {
392        match parse_blueprint(input) {
393            Ok((rest, result)) => {
394                if rest.len() > 0 {
395                    return Err(format!("Unexpected left input: {}", rest));
396                }
397                Ok(result)
398            }
399            Err(err) => Err(format_err(input, err)),
400        }
401    }
402    /// parse an Android.bp file from a file path
403    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
404        let input = std::fs::read_to_string(path).map_err(|e| e.to_string())?;
405        Self::parse(&input)
406    }
407    /// get all modules of a specific type
408    pub fn modules_by_type<'a>(&'a self, typ: &'static str) -> impl Iterator<Item = &'a Module> {
409        self.modules.iter().filter(move |b| b.typ == typ)
410    }
411}