logic_parser/parsing/
parser.rs1use crate::errors::ParserError;
2use crate::lexing::token::{Token, TokenKind};
3use ParserError::{UnexpectedToken, UnexpectedEOF};
4
5use super::node::ASTNode;
6
7pub type Result<T> = std::result::Result<T, ParserError>;
8
9#[derive(Debug)]
10pub struct Parser<'a> {
11 tokens: &'a Vec<Token>,
12 pos: usize
13}
14
15impl Parser<'_> {
16 pub fn new(tokens: &Vec<Token>) -> Parser {
17 Parser { tokens, pos: 0 }
18 }
19
20 pub fn parse(&mut self) -> Result<ASTNode> {
28 let ast = self.parse_expression()?;
29 if let Some(t) = self.consume() {
31 return Err(UnexpectedToken(format!("'{t}'", t=t.kind), t.span))
32 }
33 Ok(ast)
34 }
35
36 fn parse_expression(&mut self) -> Result<ASTNode> {
37 let l_term = self.parse_term()?;
38
39 match self.peek().cloned() {
40 Some(TokenKind::Implies) => {
41 self.consume();
42 Ok(ASTNode::Implies {
43 left: Box::new(l_term),
44 right: Box::new(self.parse_expression()?)
45 })
46 },
47 Some(TokenKind::IfAndOnlyIf) => {
48 self.consume();
49 Ok(ASTNode::IfAndOnlyIf {
50 left: Box::new(l_term),
51 right: Box::new(self.parse_expression()?)
52 })
53 },
54 Some(_) => Ok(l_term),
55 None => Ok(l_term)
56 }
57 }
58
59 fn parse_term(&mut self) -> Result<ASTNode> {
60 let l_term = self.parse_proposition()?;
61
62 match self.peek().cloned() {
63 Some(TokenKind::And) => {
64 self.consume();
65 Ok(ASTNode::And {
66 left: Box::new(l_term),
67 right: Box::new(self.parse_term()?)
68 })
69 },
70 Some(TokenKind::Or) => {
71 self.consume();
72 Ok(ASTNode::Or {
73 left: Box::new(l_term),
74 right: Box::new(self.parse_term()?)
75 })
76 },
77 Some(_) => Ok(l_term),
78 None => Ok(l_term)
79 }
80 }
81
82 fn parse_proposition(&mut self) -> Result<ASTNode> {
83 let next_token = match self.consume().cloned() {
84 Some(t) => t,
85 None => {
86 let last_span = self.tokens.last().map(|t| t.span).unwrap_or((0, 0).into());
88 return Err(
89 UnexpectedEOF("Expected [~] (true | false | variable | (...))".into(), last_span)
90 )
91 },
92 };
93
94 match next_token.kind {
95 TokenKind::Identifier(name) => {
96 Ok(ASTNode::Identifier { name: name.to_owned() })
97 },
98 TokenKind::Literal(boolean) => {
99 Ok(ASTNode::Literal { value: boolean })
100 },
101 TokenKind::Not => {
102 let prop = self.parse_proposition()?;
103 Ok(ASTNode::Not{ operand: Box::new(prop) })
104 },
105 TokenKind::OpenParen => {
106 let expr = self.parse_expression()?;
107 if let Some(TokenKind::CloseParen) = self.peek() {
108 self.consume();
109 Ok(expr)
110 }
111 else {
112 Err(UnexpectedToken("R_PAREN expected".into(), next_token.span))
113 }
114 },
115 TokenKind::CloseParen => {
116 Err(UnexpectedToken("R_PAREN".into(), next_token.span))
117 },
118 other @ (TokenKind::And | TokenKind::Or | TokenKind::Implies | TokenKind::IfAndOnlyIf) => {
119 Err(UnexpectedToken(format!("'{other}'"), next_token.span))
120 }
121 }
122 }
123
124 fn consume(&mut self) -> Option<&Token> {
125 let token = self.tokens.get(self.pos);
126 if token.is_some() {
127 self.pos += 1;
128 }
129 token
130 }
131
132 fn peek(&self) -> Option<&TokenKind> {
133 self.tokens.get(self.pos).map(|t| &t.kind)
134 }
135}
136
137#[cfg(test)]
138mod test {
139 use super::*;
140 use crate::lexing::Lexer;
141 use std::error::Error;
142 use std::result::Result;
143
144 #[test]
145 fn complex_json_rendered_properly() -> Result<(), Box<dyn Error>> {
146 use assert_json::assert_json;
147 let tokens = Lexer::new().tokenize("((p || q)) => (q & ~(r))")?;
148 let ast = Parser::new(&tokens).parse()?;
149 let result = ast.as_json();
150
151 assert_json!(result.as_str(), {
152 "type": "operator.implies",
153 "left": {
154 "type": "operator.or",
155 "left": {
156 "type": "identifier",
157 "name": "p"
158 },
159 "right": {
160 "type": "identifier",
161 "name": "q"
162 }
163 },
164 "right": {
165 "type": "operator.and",
166 "left": {
167 "type": "identifier",
168 "name": "q"
169 },
170 "right": {
171 "type": "operator.not",
172 "operand": {
173 "type": "identifier",
174 "name": "r"
175 }
176 }
177 }
178 });
179 Ok(())
180 }
181
182 #[test]
183 fn multiple_negation_works() -> Result<(), Box<dyn Error>> {
184 use assert_json::assert_json;
185 let tokens = Lexer::new().tokenize("~~~negate_me")?;
186 let ast = Parser::new(&tokens).parse()?;
187 let result = ast.as_json();
188
189 assert_json!(result.as_str(), {
190 "type": "operator.not",
191 "operand": {
192 "type": "operator.not",
193 "operand": {
194 "type": "operator.not",
195 "operand": {
196 "type": "identifier",
197 "name": "negate_me"
198 }
199 }
200 }
201 });
202 Ok(())
203 }
204
205 #[test]
206 fn iff_and_implies_work_together() -> Result<(), Box<dyn Error>> {
207 use assert_json::assert_json;
208 let tokens = Lexer::new().tokenize("(a => b) <=> c")?;
209 let ast = Parser::new(&tokens).parse()?;
210 let result = ast.as_json();
211
212 assert_json!(result.as_str(), {
213 "type": "operator.iff",
214 "left": {
215 "type": "operator.implies",
216 "left": {
217 "type": "identifier",
218 "name": "a"
219 },
220 "right": {
221 "type": "identifier",
222 "name": "b"
223 }
224 },
225 "right": {
226 "type": "identifier",
227 "name": "c"
228 }
229 });
230 Ok(())
231 }
232
233 #[test]
234 fn alternative_syntax_work() -> Result<(), Box<dyn Error>> {
235 use assert_json::assert_json;
236 let tokens = Lexer::new().tokenize("(a & b) && ((b | c) || b)")?;
237 let ast = Parser::new(&tokens).parse()?;
238 let result = ast.as_json();
239
240 assert_json!(result.as_str(), {
241 "type": "operator.and",
242 "left": {
243 "type": "operator.and",
244 "left": {
245 "type": "identifier",
246 "name": "a"
247 },
248 "right": {
249 "type": "identifier",
250 "name": "b"
251 }
252 },
253 "right": {
254 "type": "operator.or",
255 "left": {
256 "type": "operator.or",
257 "left": {
258 "type": "identifier",
259 "name": "b"
260 },
261 "right": {
262 "type": "identifier",
263 "name": "c"
264 }
265 },
266 "right": {
267 "type": "identifier",
268 "name": "b"
269 }
270 }
271 });
272 Ok(())
273 }
274
275 #[test]
276 fn unmatched_paren_left_results_on_error() {
277 let tokens = Lexer::new().tokenize("((a => b) <=> c").unwrap();
278 let parse_error = Parser::new(&tokens).parse().unwrap_err();
279
280 match parse_error {
281 ParserError::UnexpectedToken(_, span) => {
282 assert_eq!(span, (0, 1).into())
283 },
284 _ => unreachable!()
285 }
286 }
287
288 #[test]
289 fn unmatched_paren_right_results_on_error() {
290 let tokens = Lexer::new().tokenize("(a => b)) <=> c").unwrap();
291
292 let parse_error = Parser::new(&tokens).parse().unwrap_err();
293 match parse_error {
294 ParserError::UnexpectedToken(_, span) => {
295 assert_eq!(span, (8, 9).into())
296 },
297 _ => unreachable!()
298 }
299 }
300
301 #[test]
302 fn parsing_custom_expressions() {
303 let query = "(tag:pink || tag:anime) && (mime:image/* || mime:video/*)";
304 let mut lexer = Lexer::with_alphabets(
305 |c| c.is_alphanumeric() || c == '_' || c == ':' || c == '*' || c == '/',
306 |c| c.is_alphabetic(),
307 );
308
309 let tokens = lexer.tokenize(query).unwrap();
310
311 let mut parser = Parser::new(&tokens);
312 parser.parse().unwrap();
313 }
314}