diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 0db1abc..914e103 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -240,7 +240,7 @@ pub enum AstNode { VarDecl { mutable: bool, name: String, - var_type: Type, + var_type: Option, }, Assignment { dest: Index, @@ -414,6 +414,14 @@ impl PlaceOrValue { PlaceOrValue::Value(_) => PlaceOrValue::Value(index), } } + + pub fn eq_placeness(&self, other: &Self) -> bool { + match (self, other) { + (PlaceOrValue::Place(_), PlaceOrValue::Place(_)) + | (PlaceOrValue::Value(_), PlaceOrValue::Value(_)) => true, + _ => false, + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] @@ -603,9 +611,48 @@ where self.function.clone() } - fn stmt(&self) -> impl Parser<'src, TokenInput<'src>, Index, ParserExtra> + Clone { + fn var_decl( + &self, + ) -> impl Parser<'src, TokenInput<'src>, Index, ParserExtra> + Clone + use<'src, 'b> { + let ident = select! {Token::Ident(ident) => ident}; + just(Token::Let) + .ignored() + .then(select! {Token::Mutable => ()}.or_not()) + .then(ident) + .then( + just(Token::Colon) + .ignore_then(type_parser::()) + .or_not(), + ) + .then( + just(Token::Equal) + .ignore_then(self.expr().map_with(Self::into_value)) + .or_not(), + ) + .then_ignore(just(Token::Semi)) + .map_with(|((((_, mutable), ident), var_type), val), e: &mut E| { + let decl = e.state().push(AstNode::VarDecl { + mutable: mutable.is_some(), + name: ident.to_string(), + var_type, + }); + if let Some(expr) = val { + e.state().push(AstNode::Assignment { + dest: decl, + expr: expr.index(), + }) + } else { + decl + } + }) + } + + fn stmt( + &self, + ) -> impl Parser<'src, TokenInput<'src>, Index, ParserExtra> + Clone + use<'src, 'b> { choice(( self.function.clone(), + self.var_decl(), self.expr .clone() .then_ignore(just(Token::Semi)) @@ -718,10 +765,7 @@ where &self, ) -> impl Parser<'src, TokenInput<'src>, PlaceOrValue, ParserExtra> + Clone + use<'src, 'b> { - self.expr - .clone() - .then_ignore(just(Token::Semi)) - .map(PlaceOrValue::index) + self.stmt() .repeated() .collect::>() .then(self.expr.clone().or_not()) @@ -1022,13 +1066,20 @@ where .then(self.expr()) .then(else_expr.or_not()) .map_with(|((condition, then), or), e: &mut E| { - // TODO: determine placeness from branches + use PlaceOrValue::*; + let (then, or) = match (then, or) { + (Place(then), Some(Place(or))) => (Place(then), Some(Place(or))), + _ => ( + Self::into_value(then, e), + or.map(|or| Self::into_value(or, e)), + ), + }; let node = AstNode::If { condition, then: then.index(), r#else: or.map(PlaceOrValue::index), }; - e.state().push(node).as_value() + then.with_index(e.state().push(node)) }) } @@ -1095,7 +1146,7 @@ mod symbols; #[cfg(test)] mod tests { - use chumsky::{Parser, extra::SimpleState}; + use chumsky::{ParseResult, Parser, extra::SimpleState}; use crate::{Ast, AstNode, ParserCtx, ParserExtra, new_token_input, pretty, type_parser}; @@ -1163,6 +1214,10 @@ mod tests { ) { let mut state = SimpleState(Ast::new()); let out = parser.parse_with_state(tokens, &mut state); + for e in out.errors() { + eprintln!("Error: {}", e); + panic!("errors occured while parsing.") + } let ast = state.0; let mut pretty = pretty::PrettyPrint::new(); pretty.print(&ast); @@ -1184,6 +1239,7 @@ mod tests { r#" /// docs! fn my_function(a: i32, b: *const u8) -> i32 { + let mut x: i32; x = a + 1; x } @@ -1193,6 +1249,40 @@ fn my_function(a: i32, b: *const u8) -> i32 { ); } + #[test] + fn parse_var_decl() { + let ctx = crate::ParserCtx::new(); + print_ast( + new_token_input( + r#" +let mut x: i32 = 10; +"#, + ), + ctx.var_decl(), + ); + } + + #[test] + fn parse_stmts() { + let ctx = crate::ParserCtx::new(); + print_ast( + new_token_input( + r#" +let mut x: i32 = 10; +"#, + ), + ctx.stmt(), + ); + print_ast( + new_token_input( + r#" +(if (true) {x} else {y}) = x + x; +"#, + ), + ctx.stmt(), + ); + } + #[test] fn parse_if_expr() { let ctx = crate::ParserCtx::new(); diff --git a/crates/parser/src/pretty.rs b/crates/parser/src/pretty.rs index fde018a..4fad0c0 100644 --- a/crates/parser/src/pretty.rs +++ b/crates/parser/src/pretty.rs @@ -164,7 +164,15 @@ impl PrettyPrint { mutable, name, var_type, - } => todo!(), + } => { + self.push_line(format!( + "{} [{} {}: {}]", + node_name(node), + name, + mutable.then_some(" mut").unwrap_or(""), + var_type.map_or("?".to_string(), |ty| format!("{ty}")) + )); + } AstNode::Assignment { dest, expr } => { self.push_line(format!("{}", node_name(node),)); self.indents.push(Indent::Vertical); @@ -174,7 +182,7 @@ impl PrettyPrint { }); *self.indents.last_mut().unwrap() = Indent::End; self.push_line("EXPR".to_string()); - self.with_indent(core::iter::once(*dest), |this, idx| { + self.with_indent(core::iter::once(*expr), |this, idx| { this.stuff(ast, idx); }); self.indents.pop(); @@ -182,8 +190,13 @@ impl PrettyPrint { AstNode::GlobalDecl { name, var_type, - expr: value, - } => todo!(), + expr, + } => { + self.push_line(format!("{} [{}: {}]", node_name(node), name, var_type)); + self.with_indent(core::iter::once(*expr), |this, idx| { + this.stuff(ast, idx); + }); + } AstNode::StructDecl { name, fields } => todo!(), AstNode::FieldDecl { name, field_type } => todo!(), AstNode::FieldAccess { expr, field } => todo!(), @@ -350,8 +363,8 @@ fn node_name(node: &AstNode) -> &'static str { AstNode::ExplicitCast { .. } => "AS", AstNode::Deref { .. } => "DEREF", AstNode::AddressOf { .. } => "ADDR_OF", - AstNode::PlaceToValue { .. } => "INTO_PLACE", - AstNode::ValueToPlace { .. } => "INTO_VALUE", + AstNode::PlaceToValue { .. } => "INTO_VALUE", + AstNode::ValueToPlace { .. } => "INTO_PLACE", AstNode::CallExpr { .. } => "CALL", AstNode::Argument { .. } => "ARG", AstNode::Not(_) => "NOT",