at_commands/parser/
command_parser.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// The functions in this module convert the untyped pest Pair syntax nodes to typed AT command AST nodes.
6///
7/// These functions should not fail, but report failures from the next_* methods if the
8/// expected element is not found.  This should only happen if the code here does not correctly
9/// match the parse tree defined in command_grammar.rs.
10use {
11    crate::{
12        lowlevel::Command,
13        parser::{
14            arguments_parser::ArgumentsParser,
15            command_grammar::{Grammar, Rule},
16            common::{ParseError, ParseResult, next_match, next_match_one_of, parse_name},
17        },
18    },
19    pest::{Parser, iterators::Pair},
20};
21
22static ARGUMENTS_PARSER: ArgumentsParser<Rule> = ArgumentsParser {
23    argument_list: Rule::argument_list,
24    argument: Rule::argument,
25    arguments: Rule::arguments,
26    key_value_argument: Rule::key_value_argument,
27    optional_argument_delimiter: Rule::optional_argument_delimiter,
28    optional_argument_terminator: Rule::optional_argument_terminator,
29    parenthesized_argument_lists: Rule::parenthesized_argument_lists,
30    primitive_argument: Rule::primitive_argument,
31};
32
33pub fn parse(string: &str) -> ParseResult<Command, Rule> {
34    let mut parsed = Grammar::parse(Rule::input, string).map_err(|pest_error| {
35        ParseError::PestParseFailure { string: string.into(), pest_error: Box::new(pest_error) }
36    })?;
37
38    let input = next_match(&mut parsed, Rule::input)?;
39    let mut input_elements = input.into_inner();
40    let command = next_match(&mut input_elements, Rule::command)?;
41
42    parse_command(command)
43}
44
45fn parse_command(command: Pair<'_, Rule>) -> ParseResult<Command, Rule> {
46    let mut command_elements = command.into_inner();
47    let command_variant =
48        next_match_one_of(&mut command_elements, vec![Rule::execute, Rule::read, Rule::test])?;
49
50    let parsed_command = match command_variant.as_rule() {
51        Rule::execute => parse_execute(command_variant),
52        Rule::read => parse_read(command_variant),
53        Rule::test => parse_test(command_variant),
54        // This is unreachable since next_match_one_of only returns success if one of the rules
55        // passed into it matches; otherwise it returns Err and this method will return early
56        // before reaching this point.
57        _ => unreachable!(),
58    }?;
59
60    Ok(parsed_command)
61}
62
63fn parse_execute(execute: Pair<'_, Rule>) -> ParseResult<Command, Rule> {
64    let mut execute_elements = execute.into_inner();
65
66    let optional_extension = next_match(&mut execute_elements, Rule::optional_extension)?;
67    let parsed_optional_extension = parse_optional_extension(optional_extension)?;
68
69    let name = next_match(&mut execute_elements, Rule::command_name)?;
70    let parsed_name = parse_name(name)?;
71
72    let delimited_arguments = next_match(&mut execute_elements, Rule::delimited_arguments)?;
73    let parsed_delimited_arguments =
74        ARGUMENTS_PARSER.parse_delimited_arguments(delimited_arguments)?;
75
76    Ok(Command::Execute {
77        name: parsed_name,
78        is_extension: parsed_optional_extension,
79        arguments: parsed_delimited_arguments,
80    })
81}
82
83fn parse_read(read: Pair<'_, Rule>) -> ParseResult<Command, Rule> {
84    let mut read_elements = read.into_inner();
85
86    let optional_extension = next_match(&mut read_elements, Rule::optional_extension)?;
87    let parsed_optional_extension = parse_optional_extension(optional_extension)?;
88
89    let name = next_match(&mut read_elements, Rule::command_name)?;
90    let parsed_name = parse_name(name)?;
91
92    Ok(Command::Read { name: parsed_name, is_extension: parsed_optional_extension })
93}
94
95fn parse_test(test: Pair<'_, Rule>) -> ParseResult<Command, Rule> {
96    let mut test_elements = test.into_inner();
97
98    let optional_extension = next_match(&mut test_elements, Rule::optional_extension)?;
99    let parsed_optional_extension = parse_optional_extension(optional_extension)?;
100
101    let name = next_match(&mut test_elements, Rule::command_name)?;
102    let parsed_name = parse_name(name)?;
103
104    Ok(Command::Test { name: parsed_name, is_extension: parsed_optional_extension })
105}
106
107fn parse_optional_extension(optional_extension: Pair<'_, Rule>) -> ParseResult<bool, Rule> {
108    let extension_str = optional_extension.as_span().as_str();
109
110    match extension_str {
111        "" => Ok(false),
112        "+" => Ok(true),
113        c => Err(Box::new(ParseError::UnknownExtensionCharacter(c.to_string()))),
114    }
115}