corosync_config_parser/
parser.rs

1use super::config::ConfigBlock;
2use super::error::{CodePosition, Error, ErrorType, Result};
3use super::lexer;
4use super::lexer::{Token, TokenType};
5
6macro_rules! expect_token {
7    ($state:expr) => {
8        match next($state) {
9            Some(t) => t,
10            None => return fail($state, ErrorType::UnexpectedEOF, "token")
11        }
12    };
13    ($state:expr, $ty:expr) => {
14        match next($state) {
15            Some(Token {token_type: $ty(..), ..} @ t) => t,
16            Some(t) => return fail($state, ErrorType::Unexpected(t), stringify!($ty))
17            None => return fail($state, ErrorType::UnexpectedEOF, stringify!($ty))
18        }
19    }
20}
21
22struct ParseState {
23    tokens: Box<dyn Iterator<Item = lexer::Token>>,
24    last_token: Option<Token>,
25    force_next: Option<Token>,
26    done: bool,
27}
28
29impl CodePosition for ParseState {
30    fn location(&self) -> (u32, u16) {
31        match self.last_token {
32            Some(ref t) => (t.line, t.col),
33            None => (0, 0),
34        }
35    }
36}
37
38pub fn run(tokens: Box<dyn Iterator<Item = lexer::Token>>) -> Result<ConfigBlock> {
39    let mut state = ParseState {
40        tokens: tokens,
41        last_token: None,
42        force_next: None,
43        done: false,
44    };
45
46    parse_block(&mut state, false, String::from(""), vec![])
47}
48
49fn parse_block(
50    state: &mut ParseState,
51    inner: bool,
52    name: String,
53    options: Vec<String>,
54) -> Result<ConfigBlock> {
55    let mut return_value = ConfigBlock::new(name, options, vec![]);
56    loop {
57        let token = if inner {
58            expect_token!(state)
59        } else {
60            match next(state) {
61                Some(t) => t,
62                None => return Ok(return_value),
63            }
64        };
65        match token.clone().token_type {
66            TokenType::RawLiteral(option_name) => {
67                let params = parse_params(state)?;
68                let t = expect_token!(state);
69                match t.token_type {
70                    TokenType::OpenBrace => {
71                        // Block follows
72                        return_value.add_block(parse_block(state, true, option_name, params)?);
73                    }
74                    _ => {
75                        // No block. In strict mode this will only ever execute for
76                        // TokenType::Semicolon as parse_params() will already have
77                        // returned an error for other types
78                        return_value.add_block(ConfigBlock::new(option_name, params, vec![]))
79                    }
80                }
81            }
82            TokenType::CloseBrace if inner => break,
83            TokenType::Semicolon => {}
84            _ => {
85                return fail(
86                    state,
87                    ErrorType::Unexpected(token.clone()),
88                    if inner { "option or }" } else { "option" },
89                )
90            }
91        }
92    }
93    Ok(return_value)
94}
95
96fn parse_params(state: &mut ParseState) -> Result<Vec<String>> {
97    let mut return_value = vec![];
98    loop {
99        match lookahead(state) {
100            Some(t) => match t.token_type {
101                TokenType::StringLiteral(s) => {
102                    return_value.push(s);
103                    pop(state);
104                }
105                TokenType::RawLiteral(s) => {
106                    return_value.push(s);
107                    pop(state);
108                }
109                TokenType::OpenBrace => break,
110                TokenType::Semicolon => break,
111                TokenType::LineEnd => break,
112                TokenType::Colon => {
113                    pop(state);
114                }
115                _ => {
116                    if cfg!(feature = "nonstrict") {
117                        break;
118                    } else {
119                        println!("Errored params");
120                        return fail(state, ErrorType::Unexpected(t), "; or {");
121                    }
122                }
123            },
124            None => return fail(&state, ErrorType::UnexpectedEOF, "}"),
125        }
126    }
127    Ok(return_value)
128}
129
130fn next(state: &mut ParseState) -> Option<lexer::Token> {
131    let v = match &state.force_next {
132        &Some(ref t) => Some(t.clone()),
133        &None => state.tokens.next(),
134    };
135    state.force_next = None;
136    match v.clone() {
137        Some(_) => {}
138        None => {
139            if state.done {
140                unreachable!("Tried to get another token after end of stream");
141            } else {
142                state.done = true;
143            }
144        }
145    }
146    v
147}
148
149fn pop(state: &mut ParseState) {
150    if state.force_next.is_some() {
151        state.force_next = None;
152    } else {
153        state.tokens.next();
154    }
155}
156
157fn lookahead(state: &mut ParseState) -> Option<lexer::Token> {
158    match state.force_next.clone() {
159        Some(t) => Some(t),
160        None => {
161            let t = state.tokens.next();
162            state.force_next = t.clone();
163            t
164        }
165    }
166}
167
168fn fail<T>(state: &ParseState, error_type: ErrorType, expected: &'static str) -> Result<T> {
169    Err(Error::from_state(state, error_type, Some(expected)))
170}
171
172#[cfg(test)]
173mod test {
174    use super::super::config::ConfigBlock;
175    use super::super::lexer::{Token, TokenType};
176    use super::*;
177
178    #[test]
179    fn test_it_parsing_the_most_basic_option() {
180        assert_eq!(
181            run(Box::new(
182                vec![
183                    tok(TokenType::RawLiteral(String::from("test"))),
184                    tok(TokenType::Semicolon)
185                ]
186                .into_iter()
187            )),
188            Ok(ConfigBlock::new(
189                String::new(),
190                vec![],
191                vec![ConfigBlock::new(String::from("test"), vec![], vec![])]
192            ))
193        );
194    }
195
196    #[test]
197    fn test_it_parsing_a_typical_example() {
198        assert_eq!(
199            run(Box::new(
200                vec![
201                    tok(TokenType::RawLiteral(String::from("option"))),
202                    tok(TokenType::RawLiteral(String::from("param1"))),
203                    tok(TokenType::OpenBrace),
204                    tok(TokenType::RawLiteral(String::from("inner"))),
205                    tok(TokenType::StringLiteral(String::from("value"))),
206                    tok(TokenType::Semicolon),
207                    tok(TokenType::CloseBrace),
208                ]
209                .into_iter()
210            )),
211            Ok(ConfigBlock::new(
212                String::new(),
213                vec![],
214                vec![ConfigBlock::new(
215                    String::from("option"),
216                    vec![String::from("param1")],
217                    vec![ConfigBlock::new(
218                        String::from("inner"),
219                        vec![String::from("value")],
220                        vec![]
221                    )]
222                )]
223            ))
224        );
225    }
226
227    fn tok(ty: TokenType) -> Token {
228        Token::new(0, 0, ty)
229    }
230}