diff --git a/src/hir/expression/array.rs b/src/hir/expression/array.rs index d3d2fc3..2978aef 100644 --- a/src/hir/expression/array.rs +++ b/src/hir/expression/array.rs @@ -12,8 +12,9 @@ ast_node! { impl Parsable for Array { fn register(parser: &mut Parser) { - assert!( - parser.register_prefix(Token::LeftSquare, |parser, compiler, lexer| { + assert!(parser.register_prefix::>( + Token::LeftSquare, + |parser, compiler, lexer| { // Parse opening square bracket let span_start = match lexer.next_spanned().unwrap() { (Token::LeftSquare, span) => span.start, @@ -30,7 +31,6 @@ impl Parsable for Array { let mut init = Vec::new(); let mut expect_item = true; let span_end = loop { - dbg!("loop"); match (lexer.peek_token().unwrap(), expect_item) { (Token::Comma, false) => { expect_item = true; @@ -58,8 +58,8 @@ impl Parsable for Array { span: span_start..span_end, ty_info: None, })) - }) - ); + } + )); } } @@ -129,7 +129,7 @@ mod test { #[case::triple("[1, 2, 3]", 3)] #[case::triple_trailing("[1, 2, 3,]", 3)] fn flat(parser: Parser, #[case] source: &str, #[case] items: usize) { - let array = parser + let array: Expression = parser .parse( &mut Compiler::default(), &mut Lexer::from(source), @@ -142,7 +142,7 @@ mod test { #[rstest] fn nested(parser: Parser) { - let array = parser + let array: Expression = parser .parse( &mut Compiler::default(), &mut Lexer::from("[[1,], [1, 2,], [1, 2, 3,],]"), diff --git a/src/hir/expression/assign.rs b/src/hir/expression/assign.rs index 2639fa8..4b76590 100644 --- a/src/hir/expression/assign.rs +++ b/src/hir/expression/assign.rs @@ -13,8 +13,9 @@ ast_node! { impl Parsable for Assign { fn register(parser: &mut Parser) { - assert!( - parser.register_infix(Token::Eq, |parser, compiler, lexer, left| { + assert!(parser.register_infix::>( + Token::Eq, + |parser, compiler, lexer, left| { let (binding, binding_span) = match left { Expression::Ident(Ident { binding, span, .. }) => (binding, span), lhs => { @@ -36,7 +37,8 @@ impl Parsable for Assign { } } - let value = parser.parse(compiler, lexer, Precedence::Lowest)?; + let value: Expression = + parser.parse(compiler, lexer, Precedence::Lowest)?; Ok(Expression::Assign(Assign { span: binding_span.start..value.span().end, @@ -44,8 +46,8 @@ impl Parsable for Assign { value: Box::new(value), ty_info: None, })) - }) - ); + } + )); } } @@ -111,7 +113,7 @@ mod test { fn success(parser: Parser, #[case] lhs: &str, #[case] rhs: &str) { let mut compiler = Compiler::default(); - let assign = parser + let assign: Expression = parser .parse( &mut compiler, &mut Lexer::from(format!("{lhs} = {rhs}").as_str()), @@ -128,7 +130,7 @@ mod test { #[rstest] fn invalid(parser: Parser) { - let result = parser.parse( + let result: Result, _> = parser.parse( &mut Compiler::default(), &mut Lexer::from("1 = otherident"), Precedence::Lowest, diff --git a/src/hir/expression/boolean.rs b/src/hir/expression/boolean.rs index b977e0c..839f19b 100644 --- a/src/hir/expression/boolean.rs +++ b/src/hir/expression/boolean.rs @@ -82,7 +82,7 @@ mod test { Boolean::::register(&mut parser); - let boolean = parser + let boolean: Expression = parser .parse( &mut Compiler::default(), &mut Lexer::from(source), diff --git a/src/hir/expression/call.rs b/src/hir/expression/call.rs index 504e6f9..9e0945d 100644 --- a/src/hir/expression/call.rs +++ b/src/hir/expression/call.rs @@ -165,7 +165,7 @@ mod test { fn success(parser: Parser, #[case] source: &str, #[case] arg_count: usize) { let mut compiler = Compiler::default(); - let call = parser + let call: Expression = parser .parse(&mut compiler, &mut Lexer::from(source), Precedence::Lowest) .unwrap(); @@ -183,7 +183,7 @@ mod test { #[case::single_comma_no_args("myfn(,)")] fn fail(parser: Parser, #[case] source: &str) { assert!(parser - .parse( + .parse::, _>( &mut Compiler::default(), &mut Lexer::from(source), Precedence::Lowest diff --git a/src/hir/expression/cast.rs b/src/hir/expression/cast.rs index ca2796a..eb58d60 100644 --- a/src/hir/expression/cast.rs +++ b/src/hir/expression/cast.rs @@ -13,28 +13,31 @@ ast_node! { impl Parsable for Cast { fn register(parser: &mut Parser) { - assert!(parser.register_infix(Token::As, |_, _, lexer, left| { - let as_span = match lexer.next_spanned().unwrap() { - (Token::As, span) => span, - (token, _) => { - return Err(ParseError::ExpectedToken { - expected: Box::new(Token::As), - found: Box::new(token), - reason: "expected to find cast expression".to_string(), - }); - } - }; - - // Parse out type from right hand side - let (target_ty, target_ty_span) = parse_ty(lexer)?; - - Ok(Expression::Cast(Cast { - value: Box::new(left), - target_ty, - span: as_span.start..target_ty_span.end, - ty_info: None, - })) - })); + assert!(parser.register_infix::>( + Token::As, + |_, _, lexer, left| { + let as_span = match lexer.next_spanned().unwrap() { + (Token::As, span) => span, + (token, _) => { + return Err(ParseError::ExpectedToken { + expected: Box::new(Token::As), + found: Box::new(token), + reason: "expected to find cast expression".to_string(), + }); + } + }; + + // Parse out type from right hand side + let (target_ty, target_ty_span) = parse_ty(lexer)?; + + Ok(Expression::Cast(Cast { + value: Box::new(left), + target_ty, + span: as_span.start..target_ty_span.end, + ty_info: None, + })) + } + )); } } @@ -115,7 +118,7 @@ mod test { #[case::missing_type("1 as")] #[case::repeated_as("1 as as")] fn fail(parser: Parser, #[case] source: &str) { - let result = parser.parse( + let result: Result, _> = parser.parse( &mut Compiler::default(), &mut Lexer::from(source), Precedence::Lowest, diff --git a/src/hir/expression/ident.rs b/src/hir/expression/ident.rs index 6b3aa21..cdc0dbb 100644 --- a/src/hir/expression/ident.rs +++ b/src/hir/expression/ident.rs @@ -14,7 +14,7 @@ ast_node! { impl Parsable for Ident { fn register(parser: &mut Parser) { - parser.register_prefix_test( + parser.register_prefix_test::>( |token| matches!(token, Token::Ident(_)), |_, compiler, lexer| { let (value, span) = match lexer.next_spanned().unwrap() { @@ -99,7 +99,7 @@ mod test_ident { let mut compiler = Compiler::default(); - let ident = parser + let ident: Expression = parser .parse( &mut compiler, &mut Lexer::from("someident"), diff --git a/src/hir/expression/index.rs b/src/hir/expression/index.rs index 384992e..716b9bf 100644 --- a/src/hir/expression/index.rs +++ b/src/hir/expression/index.rs @@ -158,7 +158,7 @@ mod test { #[case::non_ident_lhs("1[3]")] fn fail(parser: Parser, #[case] source: &str) { assert!(parser - .parse( + .parse::, _>( &mut Compiler::default(), &mut Lexer::from(source), Precedence::Lowest diff --git a/src/hir/expression/integer.rs b/src/hir/expression/integer.rs index 0e3fa21..b35abaf 100644 --- a/src/hir/expression/integer.rs +++ b/src/hir/expression/integer.rs @@ -12,7 +12,7 @@ ast_node! { impl Parsable for Integer { fn register(parser: &mut Parser) { - parser.register_prefix_test( + parser.register_prefix_test::>( |token| matches!(token, Token::Integer(_)), |_, _, lexer| { let (value, span) = match lexer.next_spanned().unwrap() { @@ -70,7 +70,7 @@ mod test_integer { Integer::::register(&mut parser); - let integer = parser + let integer: Expression = parser .parse( &mut Compiler::default(), &mut Lexer::from(value.to_string().as_str()), diff --git a/src/stage/parse/mod.rs b/src/stage/parse/mod.rs index 43becd6..3e62e79 100644 --- a/src/stage/parse/mod.rs +++ b/src/stage/parse/mod.rs @@ -51,8 +51,14 @@ pub enum ParseError { #[error("the function must have a return statement")] MissingReturn, + #[error("expected to parse a block")] + ExpectedBlock, + #[error("unexpectedly encountered end of file")] UnexpectedEOF, + + #[error("no parsers registered for type: {0}")] + NoRegisteredParsers(String), } pub fn parse(compiler: &mut Compiler, source: &str) -> Result { diff --git a/src/stage/parse/parser.rs b/src/stage/parse/parser.rs index 5dbd74e..2b3c52c 100644 --- a/src/stage/parse/parser.rs +++ b/src/stage/parse/parser.rs @@ -1,49 +1,40 @@ -use std::collections::HashMap; +use std::{any::TypeId, collections::HashMap}; -use crate::{compiler::Compiler, repr::ast::untyped::Expression}; +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( +pub type InfixParser = fn( parser: &Parser, compiler: &mut Compiler, lexer: &mut Lexer, - left: Expression, -) -> Result; + left: 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 type PrefixParser = + fn(parser: &Parser, compiler: &mut Compiler, lexer: &mut Lexer) -> Result; /// Function to test whether a token is a match to parse. pub type TokenTest = fn(token: &Token) -> bool; /// Composable parser, allowing for components of the parser to be dynamically registered. -#[derive(Default)] -pub struct Parser { +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, InfixParser)>, /// 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, PrefixParser)>, } -impl Parser { - /// Create a new instance of the parser. - pub fn new() -> Self { - Self::default() - } - +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: PrefixParser) -> bool { // If the token has already been registered, bail if self.prefix.contains_key(&token) { return false; @@ -55,13 +46,13 @@ impl Parser { } /// Register a test for a prefix parser. - pub fn register_prefix_test(&mut self, token_test: TokenTest, parser: PrefixParser) { + pub fn register_prefix_test(&mut self, token_test: TokenTest, parser: PrefixParser) { self.prefix_tests.push((token_test, parser)); } - /// Register a new infix parser against a token. Will return `fasle` if the token has already + /// 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: InfixParser) -> bool { // If the token has already been registered, bail if self.infix.contains_key(&token) { return false; @@ -73,30 +64,31 @@ impl Parser { } /// Register a test for an infix parser. - pub fn register_infix_test(&mut self, token_test: TokenTest, parser: InfixParser) { + pub fn register_infix_test(&mut self, token_test: TokenTest, parser: InfixParser) { self.infix_tests.push((token_test, parser)); } /// Parse an expression starting with the given precedence out of the lexer. pub fn parse + PartialOrd>( &self, + parser: &Parser, compiler: &mut Compiler, lexer: &mut Lexer, precedence: P, - ) -> Result { + ) -> Result { // Parse a prefix for this expression - let mut left = self.parse_prefix(compiler, lexer)?; + let mut left = self.parse_prefix(parser, compiler, lexer)?; while let Some(token) = lexer .peek_token() .filter(|t| precedence < P::from((*t).clone())) { - let Some(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); }; - left = parser(self, compiler, lexer, left)?; + left = infix_parser(parser, compiler, lexer, left)?; } Ok(left) @@ -105,25 +97,26 @@ impl Parser { /// Parse a prefix expression fn parse_prefix( &self, + parser: &Parser, compiler: &mut Compiler, lexer: &mut Lexer, - ) -> Result { + ) -> Result { let token = lexer.peek_token().unwrap(); - let parser = self + let prefix_parser = self .get_prefix_parser(token) .ok_or_else(|| ParseError::UnexpectedToken(token.clone()))?; - parser(self, compiler, lexer) + 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 PrefixParser> { self.get_parser(&self.prefix, &self.prefix_tests, token) } /// Attempt to find an infix parser for a given token. - fn get_infix_parser<'a>(&'a self, token: &'a Token) -> Option<&'a InfixParser> { + fn get_infix_parser<'a>(&'a self, token: &'a Token) -> Option<&'a InfixParser> { self.get_parser(&self.infix, &self.infix_tests, token) } @@ -143,28 +136,113 @@ impl Parser { } } +impl Default for ParserRules { + fn default() -> Self { + Self { + infix: HashMap::new(), + infix_tests: Vec::new(), + prefix: HashMap::new(), + prefix_tests: Vec::new(), + } + } +} + +#[derive(Default)] +pub struct Parser(HashMap>); + +impl Parser { + /// Create a new instance of the parser. + pub fn new() -> Self { + Self::default() + } + + /// Parse tokens from the provided lexer in order to produce values of `T`. Will produce an + /// error if no parser exists for `T`. + pub fn parse + PartialOrd>( + &self, + compiler: &mut Compiler, + lexer: &mut Lexer, + precedence: P, + ) -> Result { + self.get::() + .ok_or_else(|| ParseError::NoRegisteredParsers(std::any::type_name::().to_string()))? + .parse(self, compiler, lexer, precedence) + } + + /// 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 { + self.get_mut::().register_prefix(token, parser) + } + + /// Register a test for a prefix parser. + pub fn register_prefix_test( + &mut self, + token_test: TokenTest, + parser: PrefixParser, + ) { + 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 { + self.get_mut::().register_infix(token, parser) + } + + /// Register a test for an infix parser. + pub fn register_infix_test( + &mut self, + token_test: TokenTest, + parser: InfixParser, + ) { + self.get_mut::().register_infix_test(token_test, parser); + } + + /// Get a reference to the parser rules associated with some type. + fn get(&self) -> Option<&ParserRules> { + Some( + self.0 + .get(&TypeId::of::>())? + .downcast_ref::>() + .expect("correct type stored against type ID"), + ) + } + + /// Get a mutable reference to the parser rules associated with some type, creating it if it doesn't exist. + fn get_mut(&mut self) -> &mut ParserRules { + self.0 + .entry(TypeId::of::>()) + .or_insert_with(|| Box::new(ParserRules::::default())) + .downcast_mut::>() + .expect("correct type stored against type ID") + } +} + #[cfg(test)] mod test { use crate::{ - hir::{Boolean, Expression, Integer}, - stage::parse::Precedence, + hir::*, + stage::parse::{Precedence, UntypedAstMetadata}, }; use super::*; use rstest::*; + type Expression = crate::hir::Expression; + /// Ensure parsing fails when no parsers are registered. #[rstest] fn parse_no_match() { let parser = Parser::new(); - let result = parser.parse( + let result = parser.parse::<(), _>( &mut Compiler::default(), &mut Lexer::from("true"), Precedence::Lowest, ); - assert!(matches!(result, Err(ParseError::UnexpectedToken(_)))); + assert!(matches!(result, Err(ParseError::NoRegisteredParsers(_)))); } mod prefix { @@ -400,8 +478,6 @@ mod test { } mod infix { - use crate::hir::{Expression, Infix, InfixOperation}; - use super::*; #[fixture] @@ -462,13 +538,13 @@ mod test { /// Ensire that multiple (nested) infix expressions can be parsed from infix tokens. #[rstest] fn parse_two_infix(parser: Parser) { - let expression = dbg!(parser + let expression = parser .parse( &mut Compiler::default(), &mut Lexer::from("true + true + true"), Precedence::Lowest, ) - .unwrap()); + .unwrap(); assert!(matches!(expression, Expression::Infix(_)));