bob_the/
parser.rs

1use crate::lexer::Token;
2
3#[derive(Debug, PartialEq)]
4pub enum Statement {
5    Read(String, String),
6    Write(String, String),
7    Print(String),
8    Append(String, String, String),
9}
10
11pub fn parse(tokens: Vec<Token>) -> Result<Vec<Statement>, String> {
12    let mut statements = Vec::new();
13    let mut tokens_iter = tokens.into_iter().peekable();
14
15    while let Some(token) = tokens_iter.next() {
16        let statement = match token {
17            Token::Read => {
18                let src = get_identifier(&mut tokens_iter)?;
19                tokens_iter.next(); // Consume '->'
20                let dest = get_identifier(&mut tokens_iter)?;
21                tokens_iter.next(); // Consume EOL
22                Statement::Read(src, dest)
23            }
24            Token::Write => {
25                let path = get_identifier(&mut tokens_iter)?;
26                let content = match tokens_iter.next() {
27                    Some(Token::StringLiteral(s)) => s,
28                    Some(Token::Identifier(s)) => s,
29                    Some(anything) => {
30                        return Err(format!("Expected a string literal, got {:?}", anything))
31                    }
32                    _ => return Err("Expected a string literal.".to_string()),
33                };
34                tokens_iter.next(); // Consume EOL
35                Statement::Write(path, content)
36            }
37            Token::Print => {
38                let content = match tokens_iter.next() {
39                    Some(Token::StringLiteral(s)) => s,
40                    Some(Token::Identifier(s)) => s,
41                    Some(anything) => {
42                        return Err(format!("Expected a string literal, got {:?}", anything))
43                    }
44                    _ => return Err("Expected a string literal.".to_string()),
45                };
46                tokens_iter.next(); // Consume EOL
47                Statement::Print(content)
48            }
49            Token::Append => {
50                let src1 = get_identifier(&mut tokens_iter)?;
51                let src2 = match tokens_iter.next() {
52                    Some(Token::StringLiteral(s)) => s,
53                    Some(Token::Identifier(s)) => s,
54                    Some(anything) => {
55                        return Err(format!("Expected a string literal, got {:?}", anything))
56                    }
57                    _ => return Err("Expected a string literal.".to_string()),
58                };
59                tokens_iter.next(); // Consume '->'
60                let dest = get_identifier(&mut tokens_iter)?;
61                tokens_iter.next(); // Consume EOL
62                Statement::Append(src1, src2, dest)
63            }
64            Token::Eol => continue,
65            _ => return Err("Unexpected token.".to_string()),
66        };
67        statements.push(statement);
68    }
69    Ok(statements)
70}
71
72fn get_identifier<I>(tokens_iter: &mut I) -> Result<String, String>
73where
74    I: Iterator<Item = Token>,
75{
76    match tokens_iter.next() {
77        Some(Token::Identifier(s)) => Ok(s),
78        _ => Err("Expected an identifier.".to_string()),
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_parser_basic() {
88        use Token::*;
89
90        let tokens = vec![
91            Read,
92            Identifier("input.txt".to_string()),
93            Arrow,
94            Identifier("content".to_string()),
95            Eol,
96            Write,
97            Identifier("output.txt".to_string()),
98            StringLiteral("Hello, World!".to_string()),
99            Eol,
100            Print,
101            StringLiteral("Hello, World!".to_string()),
102            Eol,
103            Append,
104            Identifier("var1".to_string()),
105            StringLiteral("var2".to_string()),
106            Arrow,
107            Identifier("result".to_string()),
108            Eol,
109        ];
110
111        let ast = parse(tokens).unwrap();
112        let expected = vec![
113            Statement::Read("input.txt".to_string(), "content".to_string()),
114            Statement::Write("output.txt".to_string(), "Hello, World!".to_string()),
115            Statement::Print("Hello, World!".to_string()),
116            Statement::Append("var1".to_string(), "var2".to_string(), "result".to_string()),
117        ];
118        assert_eq!(ast, expected);
119    }
120}