From 2118e017eec8230cf348fdd34d179f220aa5e03f Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Wed, 4 Sep 2024 22:19:52 +1000 Subject: [PATCH] use `Fn` trait for parser implementations --- src/stage/parse/parser.rs | 101 +++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/src/stage/parse/parser.rs b/src/stage/parse/parser.rs index 626aae8..361d342 100644 --- a/src/stage/parse/parser.rs +++ b/src/stage/parse/parser.rs @@ -5,16 +5,21 @@ use crate::compiler::Compiler; use super::{Lexer, ParseError, Token}; /// Function capable of parsing an infix expression out of the provided lexer. -pub type InfixParser = fn( - parser: &Parser, - compiler: &mut Compiler, - lexer: &mut Lexer, - left: T, -) -> Result; +pub trait InfixParser: + Fn(&Parser, &mut Compiler, &mut Lexer, T) -> Result +{ +} +impl InfixParser for F where + F: Fn(&Parser, &mut Compiler, &mut Lexer, T) -> Result +{ +} /// Function capable of parsing a prefix expression out of the provided lexer. -pub type PrefixParser = - fn(parser: &Parser, compiler: &mut Compiler, lexer: &mut Lexer) -> Result; +pub trait PrefixParser: Fn(&Parser, &mut Compiler, &mut Lexer) -> Result {} +impl PrefixParser for F where + F: Fn(&Parser, &mut Compiler, &mut Lexer) -> Result +{ +} /// Function to test whether a token is a match to parse. pub type TokenTest = fn(token: &Token) -> bool; @@ -22,66 +27,78 @@ pub type TokenTest = fn(token: &Token) -> bool; /// Composable parser, allowing for components of the parser to be dynamically registered. pub struct ParserRules { /// Infix parse function to run when a given token is presented during infix parsing. - infix: HashMap>, + infix: HashMap>>, /// Dynamic tests to run on a token during infix parsing. These will be run after the infix map is checked. - infix_tests: Vec<(TokenTest, InfixParser)>, + infix_tests: Vec<(TokenTest, Box>)>, /// Prefix parse function to run when a given token is presented durign prefix parsing. - prefix: HashMap>, + prefix: HashMap>>, /// Dynamic tests to run on a token during prefix parsing. These will be run after the prefix map is checked. - prefix_tests: Vec<(TokenTest, PrefixParser)>, + prefix_tests: Vec<(TokenTest, Box>)>, /// Fallback parser to use if no other parsers match. - fallback: Option>, + fallback: Option>>, } impl ParserRules { /// Register a new prefix parser against a token. Will return `false` if the token has already /// been registered. - pub fn register_prefix(&mut self, token: Token, parser: PrefixParser) -> bool { + pub fn register_prefix( + &mut self, + token: Token, + parser: impl PrefixParser + 'static, + ) -> bool { // If the token has already been registered, bail if self.prefix.contains_key(&token) { return false; } - self.prefix.insert(token, parser); + self.prefix.insert(token, Box::new(parser)); true } /// Register a test for a prefix parser. - pub fn register_prefix_test(&mut self, token_test: TokenTest, parser: PrefixParser) { - self.prefix_tests.push((token_test, parser)); + pub fn register_prefix_test( + &mut self, + token_test: TokenTest, + parser: impl PrefixParser + 'static, + ) { + self.prefix_tests.push((token_test, Box::new(parser))); } /// Register a new infix parser against a token. Will return `false` if the token has already /// been registered. - pub fn register_infix(&mut self, token: Token, parser: InfixParser) -> bool { + pub fn register_infix(&mut self, token: Token, parser: impl InfixParser + 'static) -> bool { // If the token has already been registered, bail if self.infix.contains_key(&token) { return false; } - self.infix.insert(token, parser); + self.infix.insert(token, Box::new(parser)); true } /// Register a test for an infix parser. - pub fn register_infix_test(&mut self, token_test: TokenTest, parser: InfixParser) { - self.infix_tests.push((token_test, parser)); + pub fn register_infix_test( + &mut self, + token_test: TokenTest, + parser: impl InfixParser + 'static, + ) { + self.infix_tests.push((token_test, Box::new(parser))); } /// Register a fallback parser if no other parsers match the token. Will return `false` if /// there is already a fallback registered. - pub fn register_fallback(&mut self, parser: PrefixParser) -> bool { + pub fn register_fallback(&mut self, parser: impl PrefixParser + 'static) -> bool { if self.fallback.is_some() { return false; } - self.fallback = Some(parser); + self.fallback = Some(Box::new(parser)); true } @@ -100,8 +117,9 @@ impl ParserRules { while let Some(token) = lexer .peek_token() .filter(|t| precedence < P::from((*t).clone())) + .cloned() { - let Some(infix_parser) = self.get_infix_parser(token) else { + let Some(infix_parser) = self.get_infix_parser(&token) else { // Can't find an infix parser for the next token, likely finished this expression return Ok(left); }; @@ -119,24 +137,26 @@ impl ParserRules { compiler: &mut Compiler, lexer: &mut Lexer, ) -> Result { - let token = lexer.peek_token().ok_or(ParseError::UnexpectedEOF)?; + let token = lexer.peek_token().ok_or(ParseError::UnexpectedEOF)?.clone(); let prefix_parser = self - .get_prefix_parser(token) - .or(self.fallback.as_ref()) + .get_prefix_parser(&token) + .or(self.fallback.as_deref()) .ok_or_else(|| ParseError::UnexpectedToken(token.clone()))?; prefix_parser(parser, compiler, lexer) } /// Attempt to find a prefix parser for a given token. - fn get_prefix_parser<'a>(&'a self, token: &'a Token) -> Option<&'a PrefixParser> { + fn get_prefix_parser<'a>(&'a self, token: &'a Token) -> Option<&'a dyn PrefixParser> { self.get_parser(&self.prefix, &self.prefix_tests, token) + .map(|b| b.as_ref()) } /// Attempt to find an infix parser for a given token. - fn get_infix_parser<'a>(&'a self, token: &'a Token) -> Option<&'a InfixParser> { - self.get_parser(&self.infix, &self.infix_tests, token) + fn get_infix_parser<'a>(&'a self, token: &'a Token) -> Option<&'a dyn InfixParser> { + self.get_parser::>>(&self.infix, &self.infix_tests, token) + .map(|b| b.as_ref()) } /// Attempt to find a parser from a lookup and set of tests for a given token. @@ -244,7 +264,11 @@ impl Parser { /// Register a new prefix parser against a token. Will return `false` if the token has already /// been registered. - pub fn register_prefix(&mut self, token: Token, parser: PrefixParser) -> bool { + pub fn register_prefix( + &mut self, + token: Token, + parser: impl PrefixParser + 'static, + ) -> bool { self.get_mut::().register_prefix(token, parser) } @@ -252,14 +276,18 @@ impl Parser { pub fn register_prefix_test( &mut self, token_test: TokenTest, - parser: PrefixParser, + parser: impl PrefixParser + 'static, ) { self.get_mut::().register_prefix_test(token_test, parser); } /// Register a new infix parser against a token. Will return `false` if the token has already /// been registered. - pub fn register_infix(&mut self, token: Token, parser: InfixParser) -> bool { + pub fn register_infix( + &mut self, + token: Token, + parser: impl InfixParser + 'static, + ) -> bool { self.get_mut::().register_infix(token, parser) } @@ -267,14 +295,17 @@ impl Parser { pub fn register_infix_test( &mut self, token_test: TokenTest, - parser: InfixParser, + parser: impl InfixParser + 'static, ) { self.get_mut::().register_infix_test(token_test, parser); } /// Register a fallback parser if no other parsers match the token. Will return `false` if /// there is already a fallback registered. - pub fn register_fallback(&mut self, parser: PrefixParser) -> bool { + pub fn register_fallback( + &mut self, + parser: impl PrefixParser + 'static, + ) -> bool { self.get_mut::().register_fallback(parser) }