yaccas/parser/parser.rs
1use std::collections::HashMap;
2use std::convert::Into;
3
4use ::arguments::*;
5use ::scanner::{Scanner, Token};
6
7use super::{FreeArgumentSupport, Result};
8
9/// The parser which parses the `Argument`s upon `Token`s provided by a `Scanner`.
10pub struct Parser<'a> {
11 /// Behavior on free arguments.
12 pub free_arguments: FreeArgumentSupport,
13 names: HashMap<&'a str, usize>,
14 arguments: Vec<Argument<'a>>,
15}
16
17impl<'a> Parser<'a> {
18 /// Registers an `Argument` with specific name(s) and a callback which is called after successful parsing.
19 /// # Example
20 /// ```
21 /// use yaccas::arguments::{Argument, Flag};
22 /// use yaccas::parser::{Parser, Result};
23 /// use yaccas::scanner::Unix;
24 ///
25 /// let mut parser = Parser::default();
26 /// let flag = Flag::default();
27 ///
28 /// parser.register(&["option", "o1", "o2"], Argument::with_callback(flag, | _flag | {
29 /// // Do something with the argument here
30 /// }));
31 ///
32 /// assert_eq!(parser.parse(Unix::new(&["-option"])), Result::Success(Vec::new()));
33 /// ```
34 pub fn register<T: Into<Argument<'a>>>(&mut self, names: &[&'a str], argument: T) {
35 self.arguments.push(argument.into());
36
37 let index = self.arguments.len() - 1;
38
39 for name in names {
40 self.names.insert(name, index);
41 }
42 }
43
44 /// Parses the `Tokens` provided by a `Scanner` and matches them with registered `Argument`s.
45 pub fn parse<S: Scanner>(&mut self, scanner: S) -> Result {
46 let mut expecting_value = None;
47 let mut add_to_free_variables = false;
48 let mut free_variables = Vec::new();
49
50 for token in scanner {
51 // Check if further arguments need to be parsed
52 if add_to_free_variables {
53 free_variables.push(token.into());
54 continue;
55 }
56
57 // If a `Value` is expecting a value...
58 if let Some(index) = expecting_value {
59 match token {
60 // Fail if the value is skipped
61 Token::Bound(_) => {
62 return Result::InvalidValue;
63 }
64 // Try to set the value
65 Token::Free(value) => {
66 if let Argument::Value(ref mut argument, _) = self.arguments[index] {
67 if !argument.set_value(value) || !argument.has_value() {
68 return Result::InvalidValue;
69 }
70 expecting_value = None;
71 } else {
72 panic!("Argument index was invalid!");
73 }
74 }
75 }
76 } else {
77 // Try to parse the argument or return it as a free argument
78 let free_argument = if let Token::Bound(name) = token {
79 // Try to find the argument by name
80 let arg_index = self.names.get::<str>(&name).cloned();
81 // Process the argument if possible
82 match arg_index.map(|index| {
83 (index, self.arguments.get_mut(index).expect("Invalid index"))
84 }) {
85 Some((_, &mut Argument::Flag(ref mut flag, _))) => {
86 flag.activate();
87 None
88 }
89 Some((_, &mut Argument::Command(ref mut command, _))) => {
90 if let Some(abort_reason) = command.execute() {
91 return Result::Aborted(abort_reason);
92 }
93 None
94 }
95 Some((index, &mut Argument::Value(_, _))) => {
96 expecting_value = Some(index);
97 None
98 }
99 _ => Some(name),
100 }
101 } else {
102 Some(token.into())
103 };
104
105 // Process the free argument if set
106 if let Some(free_argument) = free_argument {
107 match self.free_arguments {
108 FreeArgumentSupport::None => {
109 return Result::InvalidArgument;
110 }
111 FreeArgumentSupport::AtTheEnd => {
112 if !add_to_free_variables {
113 add_to_free_variables = true;
114 }
115 free_variables.push(free_argument);
116 }
117 FreeArgumentSupport::Everywhere => {
118 free_variables.push(free_argument);
119 }
120 }
121 }
122 }
123 }
124
125 // Check if all arguments are sufficient
126 if self.arguments.iter().any(|argument| {
127 match argument {
128 &Argument::Value(ref value, _) if !value.has_value() => true,
129 _ => false,
130 }
131 }) {
132 return Result::NotSufficient;
133 }
134
135 // Parsing was successful: Execute the callbacks.
136 for argument in self.arguments.iter_mut() {
137 argument.execute_callback();
138 }
139
140 Result::Success(free_variables)
141 }
142}
143
144impl<'a> Default for Parser<'a> {
145 fn default() -> Parser<'a> {
146 Parser {
147 free_arguments: FreeArgumentSupport::AtTheEnd,
148 names: HashMap::new(),
149 arguments: Vec::new(),
150 }
151 }
152}