diff --git a/src/syn/parser.rs b/src/syn/parser.rs index f642c4b..ac5203a 100644 --- a/src/syn/parser.rs +++ b/src/syn/parser.rs @@ -9,6 +9,7 @@ const BASE_EXPR_START: &[TokenKind] = &[ TokenKind::LParen, TokenKind::LBracket, TokenKind::LBrace, + TokenKind::ObjBrace, ]; const UN_EXPR_START: &[TokenKind] = &[TokenKind::Plus, TokenKind::Minus, TokenKind::Bang]; @@ -61,6 +62,15 @@ impl<'t> Parser<'t> { } } +//////////////////////////////////////////////////////////////////////////////// +// Statement parsing +//////////////////////////////////////////////////////////////////////////////// +impl<'t> Parser<'t> { + pub fn next_stmt(&mut self) -> Result { + todo!() + } +} + //////////////////////////////////////////////////////////////////////////////// // Expression parsing //////////////////////////////////////////////////////////////////////////////// @@ -165,7 +175,17 @@ impl<'t> Parser<'t> { } .into() } - TokenKind::LBrace => todo!(), + TokenKind::LBrace => todo!("TODO body expressions"), + TokenKind::ObjBrace => { + let prev_skip = self.set_skip_newlines(true)?; + let object = self.next_obj_list()?; + let end_token = self.expect_token_kind(TokenKind::RBrace, "end of object (right curly brace)")?; + let span = token.span().union(end_token.span()); + Expr::Base(BaseExpr { + kind: BaseExprKind::Object(object), + span, + }) + } TokenKind::LParen => { let prev_skip = self.set_skip_newlines(true)?; let first = self.next_expr()?; @@ -175,7 +195,7 @@ impl<'t> Parser<'t> { self.set_skip_newlines(prev_skip)?; let end_token = self.expect_token_kind(TokenKind::RParen, "end of tuple")?; - let span = first.span().union(end_token.span()); + let span = token.span().union(end_token.span()); list.insert(0, first); Expr::Base( BaseExpr { @@ -212,6 +232,28 @@ impl<'t> Parser<'t> { Ok(list_items) } + /// Parses the tail of a `BaseExprKind::Object` expression. + /// + /// This function expects that the initial object brace `%{` has already been eaten. + fn next_obj_list(&mut self) -> Result> { + let mut object = Vec::new(); + + // Parses this pattern: + // ( key EQ value ( COMMA key = value )* COMMA? )? + while !self.is_token_kind(TokenKind::RBrace) { + let key = self.next_expr()?; + self.expect_token_kind(TokenKind::Eq, "equals sign for object item")?; + let value = self.next_expr()?; + object.push((key, value)); + + // match a comma, otherwise exit the loop + if self.match_token_kind(TokenKind::Comma)?.is_none() { + break; + } + } + Ok(object) + } + //////////////////////////////////////////////////////////////////////////////// // Token matching functions //////////////////////////////////////////////////////////////////////////////// @@ -220,13 +262,19 @@ impl<'t> Parser<'t> { let mut next_token = self.lexer.next_token()?; if self.is_skip_newlines() { - while next_token.map(|t| t.kind() == TokenKind::Newline).unwrap_or(false) { + while next_token + .map(|t| t.kind() == TokenKind::Newline) + .unwrap_or(false) + { next_token = self.lexer.next_token()?; } if self.is_token_kind(TokenKind::Newline) { self.curr_token = next_token; - while next_token.map(|t| t.kind() == TokenKind::Newline).unwrap_or(false) { + while next_token + .map(|t| t.kind() == TokenKind::Newline) + .unwrap_or(false) + { next_token = self.lexer.next_token()?; } } @@ -485,9 +533,7 @@ mod test { test_parser!( Parser::try_from("(:sym,)").unwrap(), next_expr, - base_expr(BaseExprKind::Tuple(vec![ - base_expr(BaseExprKind::Sym), - ])) + base_expr(BaseExprKind::Tuple(vec![base_expr(BaseExprKind::Sym),])) ); test_parser!( @@ -497,6 +543,26 @@ mod test { ); } + #[test] + fn test_object_expr() { + test_parser!( + Parser::try_from( + r#"%{ + :sym = value, + key = "value", + "lit" = 1, + }"# + ) + .unwrap(), + next_expr, + base_expr(BaseExprKind::Object(vec![ + (base_expr(BaseExprKind::Sym), base_expr(BaseExprKind::Ident)), + (base_expr(BaseExprKind::Ident), base_expr(BaseExprKind::Str)), + (base_expr(BaseExprKind::Str), base_expr(BaseExprKind::Num)), + ])) + ); + } + #[test] fn test_bin_expr() { test_parser!( @@ -613,26 +679,30 @@ mod test { #[test] fn test_multiline_exprs() { test_parser!( - Parser::try_from(r"[ + Parser::try_from( + r"[ 1, x, ['value', :value], :sym - ]").unwrap(), + ]" + ) + .unwrap(), next_expr, base_expr(BaseExprKind::List(vec![ base_expr(BaseExprKind::Num), base_expr(BaseExprKind::Ident), base_expr(BaseExprKind::List(vec![ - base_expr(BaseExprKind::Str), - base_expr(BaseExprKind::Sym), + base_expr(BaseExprKind::Str), + base_expr(BaseExprKind::Sym), ])), base_expr(BaseExprKind::Sym), ])) ); test_parser!( - Parser::try_from(r"( + Parser::try_from( + r"( 1, x, [ @@ -640,26 +710,31 @@ mod test { 'value', ], :sym - )").unwrap(), + )" + ) + .unwrap(), next_expr, base_expr(BaseExprKind::Tuple(vec![ base_expr(BaseExprKind::Num), base_expr(BaseExprKind::Ident), base_expr(BaseExprKind::List(vec![ - base_expr(BaseExprKind::Sym), - base_expr(BaseExprKind::Str), + base_expr(BaseExprKind::Sym), + base_expr(BaseExprKind::Str), ])), base_expr(BaseExprKind::Sym), ])) ); test_parser!( - Parser::try_from(r"( + Parser::try_from( + r"( + 1 + 2 + 3 + 4 - )").unwrap(), + )" + ) + .unwrap(), next_expr, bin_expr( un_expr(UnOp::Plus, base_expr(BaseExprKind::Num)), @@ -677,29 +752,30 @@ mod test { ); test_parser!( - Parser::try_from(r"( + Parser::try_from( + r"( + 1 + 2 + 3 + 4 , - )").unwrap(), + )" + ) + .unwrap(), next_expr, - base_expr(BaseExprKind::Tuple(vec![ + base_expr(BaseExprKind::Tuple(vec![bin_expr( + un_expr(UnOp::Plus, base_expr(BaseExprKind::Num)), + BinOp::Plus, bin_expr( - un_expr(UnOp::Plus, base_expr(BaseExprKind::Num)), + base_expr(BaseExprKind::Num), BinOp::Plus, bin_expr( base_expr(BaseExprKind::Num), BinOp::Plus, - bin_expr( - base_expr(BaseExprKind::Num), - BinOp::Plus, - base_expr(BaseExprKind::Num) - ) + base_expr(BaseExprKind::Num) ) ) - ])) + )])) ); } }