diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index cc2fde3..3699f86 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -6,7 +6,7 @@ use chumsky::{ extra::{self, SimpleState}, input::{IterInput, MapExtra}, pratt::{infix, left, postfix, prefix, right}, - prelude::{Recursive, choice, just, recursive}, + prelude::{Recursive, choice, just, none_of, recursive, via_parser}, recursive::{Direct, Indirect}, select, text, }; @@ -424,13 +424,9 @@ pub enum Visibility { } #[derive(Debug, Error)] -pub enum ParseError<'a> { - #[error("End of file.")] - EOF, - #[error("Unexpected token: {0:?}")] - UnexpectedToken(Token<'a>), - #[error("Not a type.")] - NotAType, +pub enum ParseError { + #[error("expected a valid if condition expression")] + IfCondition, } #[derive(Default, Debug)] @@ -594,11 +590,16 @@ where this } - pub fn expr(&self) -> impl Parser<'src, TokenInput<'src>, PlaceOrValue, ParserExtra> + Clone { + pub fn expr( + &self, + ) -> impl Parser<'src, TokenInput<'src>, PlaceOrValue, ParserExtra> + Clone + use<'src, 'b> + { self.expr.clone() } - pub fn function(&self) -> impl Parser<'src, TokenInput<'src>, Index, ParserExtra> + Clone { + pub fn function( + &self, + ) -> impl Parser<'src, TokenInput<'src>, Index, ParserExtra> + Clone + use<'src, 'b> { self.function.clone() } @@ -627,6 +628,8 @@ where ); let constant = select! { + Token::True => (Intern::new(Value::Bool(true)), Intern::new(InnerType::Bool)), + Token::False => (Intern::new(Value::Bool(false)), Intern::new(InnerType::Bool)), Token::FloatingConstant(lexeme)| Token::DotFloatingConstant(lexeme)| Token::FloatingExpConstant(lexeme)| @@ -732,9 +735,25 @@ where }) } + fn into_value(idx: PlaceOrValue, e: &mut E) -> PlaceOrValue { + match idx { + PlaceOrValue::Place(index) => e.state().push(AstNode::PlaceToValue { expr: index }), + PlaceOrValue::Value(index) => index, + } + .as_value() + } + + fn into_place(idx: PlaceOrValue, e: &mut E) -> PlaceOrValue { + match idx { + PlaceOrValue::Value(index) => e.state().push(AstNode::ValueToPlace { expr: index }), + PlaceOrValue::Place(index) => index, + } + .as_place() + } + fn create_expr( &'_ self, - ) -> Box, PlaceOrValue, ParserExtra> + Clone + use<'src, 'b>> + ) -> impl Parser<'src, TokenInput<'src>, PlaceOrValue, ParserExtra> + Clone + use<'src, 'b> { let assignment = choice(( just(Token::Equal), @@ -780,22 +799,6 @@ where let r#as = just(Token::As).ignore_then(type_parser::()); - fn into_value(idx: PlaceOrValue, e: &mut E) -> PlaceOrValue { - match idx { - PlaceOrValue::Place(index) => e.state().push(AstNode::PlaceToValue { expr: index }), - PlaceOrValue::Value(index) => index, - } - .as_value() - } - - fn into_place(idx: PlaceOrValue, e: &mut E) -> PlaceOrValue { - match idx { - PlaceOrValue::Value(index) => e.state().push(AstNode::ValueToPlace { expr: index }), - PlaceOrValue::Place(index) => index, - } - .as_place() - } - // TODO: postfix: function call, field access, array subscript let _expr = self.expr.clone(); @@ -806,12 +809,12 @@ where just(Token::CloseSquareBracket), ) // subscript takes a value as the index - .map_with(into_value); + .map_with(Self::into_value); let arguments = _expr .clone() // arguments take values - .map_with(into_value) + .map_with(Self::into_value) .map(PlaceOrValue::index) .separated_by(just(Token::Comma)) .allow_trailing() @@ -823,7 +826,7 @@ where let assignment_expr = self.simple_expr().pratt(( postfix(100, subscript, |expr, index: PlaceOrValue, e: &mut E| { let node = AstNode::Subscript { - expr: into_value(expr, e).index(), + expr: Self::into_value(expr, e).index(), index: index.index(), }; // subscript yields a place @@ -831,7 +834,7 @@ where }), postfix(100, arguments, |callee, arguments, e: &mut E| { let node = AstNode::CallExpr { - callee: into_value(callee, e).index(), + callee: Self::into_value(callee, e).index(), arguments, }; // function call yields a value @@ -839,7 +842,7 @@ where }), postfix(100, field, |expr, field: &str, e: &mut E| { let node = AstNode::FieldAccess { - expr: into_place(expr, e).index(), + expr: Self::into_place(expr, e).index(), field: field.to_string(), }; // field access yields a place @@ -847,15 +850,15 @@ where }), postfix(99, r#as, |expr, ty, e: &mut E| { let node = AstNode::ExplicitCast { - expr: into_value(expr, e).index(), + expr: Self::into_value(expr, e).index(), ty, }; e.state().push(node).as_value() }), prefix(95, prefixes, |op, expr, e: &mut E| { let node = match op { - Token::Bang => AstNode::Not(into_value(expr, e).index()), - Token::Minus => AstNode::Negate(into_value(expr, e).index()), + Token::Bang => AstNode::Not(Self::into_value(expr, e).index()), + Token::Minus => AstNode::Negate(Self::into_value(expr, e).index()), Token::Star => { return e .state() @@ -863,15 +866,15 @@ where .as_place(); } Token::Ampersand => AstNode::AddressOf { - expr: into_place(expr, e).index(), + expr: Self::into_place(expr, e).index(), }, _ => unreachable!(), }; e.state().push(node).as_value() }), infix(left(90), multiplicative, |left, op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = match op { Token::Star => AstNode::Multiply { left, right }, Token::Slash => AstNode::Divide { left, right }, @@ -881,8 +884,8 @@ where e.state().push(node).as_value() }), infix(left(80), additive, |left, op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = match op { Token::Plus => AstNode::Add { left, right }, Token::Minus => AstNode::Subtract { left, right }, @@ -891,8 +894,8 @@ where e.state().push(node).as_value() }), infix(left(70), shift, |left, op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = match op { Token::LessLess => AstNode::ShiftLeft { left, right }, Token::GreaterGreater => AstNode::ShiftRight { left, right }, @@ -901,8 +904,8 @@ where e.state().push(node).as_value() }), infix(left(60), relational, |left, op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = match op { Token::Less => AstNode::Less { left, right }, Token::LessEqual => AstNode::LessEq { left, right }, @@ -913,8 +916,8 @@ where e.state().push(node).as_value() }), infix(left(50), equality, |left, op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = match op { Token::EqualEqual => AstNode::Eq { left, right }, Token::BangEqual => AstNode::NotEq { left, right }, @@ -923,42 +926,42 @@ where e.state().push(node).as_value() }), infix(left(40), and, |left, _op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = AstNode::BitAnd { left, right }; e.state().push(node).as_value() }), infix(left(30), xor, |left, _op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = AstNode::BitXor { left, right }; e.state().push(node).as_value() }), infix(left(20), or, |left, _op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = AstNode::BitOr { left, right }; e.state().push(node).as_value() }), infix(left(10), logical_and, |left, _op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = AstNode::LogicalAnd { left, right }; e.state().push(node).as_value() }), infix(left(5), logical_or, |left, _op, right, e: &mut E| { - let left = into_value(left, e).index(); - let right = into_value(right, e).index(); + let left = Self::into_value(left, e).index(); + let right = Self::into_value(right, e).index(); let node = AstNode::LogicalOr { left, right }; e.state().push(node).as_value() }), infix(right(1), assignment, |left, op, right, e: &mut E| { - let dest = into_place(left, e).index(); - let right = into_value(right, e).index(); + let dest = Self::into_place(left, e).index(); + let right = Self::into_value(right, e).index(); let node = if op == Token::Equal { AstNode::Assignment { dest, expr: right } } else { - let left = into_value(left, e).index(); + let left = Self::into_value(left, e).index(); let right = match op { Token::PlusEqual => e.state().push(AstNode::Add { left, right }), Token::MinusEqual => e.state().push(AstNode::Subtract { left, right }), @@ -980,17 +983,43 @@ where }), )); - let else_expr = just(Token::Else).ignore_then(_expr.clone()); + let expr = choice((self.if_else_expr(), assignment_expr)).labelled("expression"); - let if_expr = just(Token::If) + expr + } + + fn if_else_expr( + &self, + ) -> impl Parser<'src, TokenInput<'src>, PlaceOrValue, ParserExtra> + Clone + use<'src, 'b> + { + let else_expr = just(Token::Else).ignore_then(self.expr()); + + just(Token::If) .ignore_then( - _expr - .clone() - .map_with(into_value) + self.expr() + .map_with(Self::into_value) .map(PlaceOrValue::index) - .delimited_by(just(Token::OpenParens), just(Token::CloseParens)), + .delimited_by(just(Token::OpenParens), just(Token::CloseParens)) + .recover_with(via_parser({ + // parenthised recovery: + recursive(|recovery| { + just(Token::OpenParens) + .ignored() + .ignore_then(choice(( + none_of([Token::CloseParens, Token::OpenParens]).map(|_| ()), + recovery.map(|_| ()), + ))) + .repeated() + .then_ignore(just(Token::CloseParens)) + }) + .map_with(|_, e: &mut E| { + e.state().push(AstNode::Error { + err: Box::new(ParseError::IfCondition), + }) + }) + })), ) - .then(_expr.clone()) + .then(self.expr()) .then(else_expr.or_not()) .map_with(|((condition, then), or), e: &mut E| { // TODO: determine placeness from branches @@ -1000,11 +1029,7 @@ where r#else: or.map(PlaceOrValue::index), }; e.state().push(node).as_value() - }); - - let expr = choice((if_expr, assignment_expr)).labelled("expression"); - - Box::new(expr) + }) } fn global_decl(&self) -> impl Parser<'src, TokenInput<'src>, Index, ParserExtra> + Clone { @@ -1071,7 +1096,7 @@ mod pretty; mod tests { use chumsky::{Parser, extra::SimpleState}; - use crate::{Ast, AstNode, new_token_input, pretty, type_parser}; + use crate::{Ast, AstNode, ParserCtx, ParserExtra, new_token_input, pretty, type_parser}; #[test] fn print_ast_node_size() { @@ -1131,24 +1156,51 @@ mod tests { ); } + fn print_ast<'src, T>( + tokens: crate::TokenInput<'src>, + parser: impl Parser<'src, crate::TokenInput<'src>, T, ParserExtra>, + ) { + let mut state = SimpleState(Ast::new()); + let out = parser.parse_with_state(tokens, &mut state); + let ast = state.0; + let mut pretty = pretty::PrettyPrint::new(); + pretty.print(&ast); + } + #[test] fn parse_exprs() { let ctx = crate::ParserCtx::new(); - let print_ast = |tokens| { - let mut state = SimpleState(Ast::new()); - let out = ctx.function().parse_with_state(tokens, &mut state).unwrap(); - let ast = state.0; - let mut pretty = pretty::PrettyPrint::new(); - pretty.print(&ast); - }; - print_ast(new_token_input( - r#" + print_ast(new_token_input("true"), ctx.expr()); + } + + #[test] + fn parse_fns() { + let ctx = crate::ParserCtx::new(); + + print_ast( + new_token_input( + r#" fn my_function(a: i32, b: *const u8) -> i32 { x = a + 1; x } "#, - )); + ), + ctx.function(), + ); + } + + #[test] + fn parse_if_expr() { + let ctx = crate::ParserCtx::new(); + print_ast( + new_token_input( + r#" +if (true) 1 / 2 else true +"#, + ), + ctx.if_else_expr(), + ); } } diff --git a/crates/parser/src/pretty.rs b/crates/parser/src/pretty.rs index a841442..fde018a 100644 --- a/crates/parser/src/pretty.rs +++ b/crates/parser/src/pretty.rs @@ -277,13 +277,22 @@ impl PrettyPrint { self.with_indent([*condition].into_iter(), |this, idx| { this.stuff(ast, idx); }); + + self.push_line("THEN".to_string()); if let Some(r#else) = r#else { - self.stuff(ast, *then); + self.with_indent(core::iter::once(*then), |this, idx| { + this.stuff(ast, idx); + }); *self.indents.last_mut().unwrap() = Indent::End; - self.stuff(ast, *r#else); + self.push_line("ELSE".to_string()); + self.with_indent(core::iter::once(*r#else), |this, idx| { + this.stuff(ast, idx); + }); } else { *self.indents.last_mut().unwrap() = Indent::End; - self.stuff(ast, *then); + self.with_indent(core::iter::once(*then), |this, idx| { + this.stuff(ast, idx); + }); } self.indents.pop(); }