From 5e5c4f2c47ec867d698c001cfe8d097be5744a57 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Tue, 24 Sep 2024 16:22:31 +0200 Subject: Parse abstractions Got tricked with or()'s function eagerness: Even when the left hand side succeeds, the argument was evaluated which consumed a token in the parser which prevented proper parsing. --- rust/src/parser.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) (limited to 'rust/src/parser.rs') diff --git a/rust/src/parser.rs b/rust/src/parser.rs index 3ec0ab2..11948c1 100644 --- a/rust/src/parser.rs +++ b/rust/src/parser.rs @@ -4,6 +4,7 @@ use crate::ast::*; enum Token { LParen, RParen, + Lambda, Word(String), } @@ -28,6 +29,18 @@ impl Parser { fn next(&mut self) { self.index += 1; } + + fn backtrack(&mut self) { + self.index -= 1; + } + + fn expect_symbol(&mut self) -> Result { + if let Token::Word(s) = self.tokens.get(self.index).ok_or("Expected a symbol")? { + Ok(s.clone()) + } else { + Err("Expected a symbol".to_string()) + } + } } pub fn parse(arg: &str) -> Value { @@ -39,7 +52,30 @@ pub fn parse(arg: &str) -> Value { } fn parse_expression(parser: &mut Parser) -> Result { - parse_application(parser).or(parse_value(parser)) + parse_abstraction(parser) + .or_else(|_| parse_application(parser)) + .or_else(|_| parse_value(parser)) +} + +fn parse_abstraction(parser: &mut Parser) -> Result { + parser.expect(Token::LParen)?; + parser.next(); + parser.expect(Token::Lambda).map_err(|e| { + parser.backtrack(); + e.to_string() + })?; + parser.next(); + let var = parse_variable(parser)?; + let body = parse_expression(parser)?; + parser.expect(Token::RParen)?; + parser.next(); + Ok(Value::Lam(var, Box::new(body))) +} + +fn parse_variable(parser: &mut Parser) -> Result { + let var = parser.expect_symbol()?; + parser.next(); + Ok(var) } fn parse_application(parser: &mut Parser) -> Result { @@ -48,14 +84,15 @@ fn parse_application(parser: &mut Parser) -> Result { let left = parse_expression(parser)?; let right = parse_expression(parser)?; parser.expect(Token::RParen)?; + parser.next(); Ok(Value::App(Box::new(left), Box::new(right))) } fn parse_value(parser: &mut Parser) -> Result { let token = parser.tokens.get(parser.index).ok_or("Expected a value")?; let val = parse_number(token) - .or(parse_bool(token)) - .or(parse_symbol(token))?; + .or_else(|_| parse_bool(token)) + .or_else(|_| parse_symbol(token))?; parser.next(); Ok(val) } @@ -97,7 +134,7 @@ fn terminate(result: &mut Vec, word: &mut String) { fn parse_symbol(token: &Token) -> Result { match token { Token::Word(s) => Ok(Value::Sym(s.clone())), - _ => Err("Expected a symbol".to_string()), + _ => Err(format!("Expected a symbol, got {:?}", token)), } } @@ -193,6 +230,20 @@ mod tests { ); } + #[test] + fn parse_abstraction() { + assert_eq!( + Lam( + "x".to_string(), + Box::new(App( + Box::new(Sym("x".to_string())), + Box::new(Sym("x".to_string())) + )) + ), + parse("(lam x (x x))") + ); + } + impl Arbitrary for Value { type Parameters = (); type Strategy = BoxedStrategy; -- cgit v1.2.3