diff --git a/schala-lang/language/src/parsing/mod.rs b/schala-lang/language/src/parsing/mod.rs index d60387b..31d19d5 100644 --- a/schala-lang/language/src/parsing/mod.rs +++ b/schala-lang/language/src/parsing/mod.rs @@ -69,15 +69,21 @@ //! ```text //! expression := precedence_expr type_anno? //! precedence_expr := prefix_expr -//! prefix_expr := prefix_op call_expr -//! prefix_op := "+" | "-" | "!" | "~" -//! call_expr := index_expr ( "(" invocation_list ")" )* | ε -//! invocation_list := invocation_argument ("," invocation_argument)* | ε -//! invocation_argument := expression | IDENTIFIER "=" expression | "_" -//! index_expr := primary ( "[" (expression ("," (expression)* | ε) "]" )* +//! prefix_expr := prefix_op extended_expr +//! +//! prefix_op := "+" | "-" | "!" | "~" | ε +//! +//! extended_expr := primary (ε | index | call | access)* +//! index := "[" (expression ("," expression)*) "]" +//! call := "(" invocation_list ")" +//! access := "." identifier //! primary := literal | paren_expr | if_expr | for_expr | while_expr | identifier_expr | lambda_expr | anonymous_struct | list_expr //! expr_or_block := "{" (statement delimiter)* "}" | expr +//! +//! invocation_list := invocation_argument ("," invocation_argument)* | ε +//! invocation_argument := expression | IDENTIFIER "=" expression | "_" //! ``` +//! //TODO fix expressions, add accessor_expr as a new thing //! //! ### Primary expressions //! @@ -688,20 +694,39 @@ impl Parser { ExpressionKind::PrefixExp(prefix_op, Box::new(expr)) )) }, - _ => self.call_expr() + _ => self.extended_expr() } } #[recursive_descent_method] - fn call_expr(&mut self) -> ParseResult { - let mut expr = self.index_expr()?; - //TODO look at this - while let LParen = self.token_handler.peek_kind() { - let arguments = delimited!(self, LParen, invocation_argument, Comma, RParen); - expr = Expression::new(self.id_store.fresh(), ExpressionKind::Call { f: Box::new(expr), arguments }); //TODO no type anno is incorrect - } - - Ok(expr) + fn extended_expr(&mut self) -> ParseResult { + let mut expression = self.primary()?; + loop { + //TODO need a next non whitespace + let next = self.token_handler.peek_kind(); + match next { + Period => unimplemented!(), + LSquareBracket => { + let indexers = delimited!(self, LSquareBracket, expression, Comma, RSquareBracket); + if indexers.is_empty() { + return ParseError::new_with_token("Empty index expressions are not allowed", self.token_handler.peek()); + } + expression = Expression::new(self.id_store.fresh(), ExpressionKind::Index { + indexee: Box::new(expression), + indexers, + }); + }, + LParen => { + let arguments = delimited!(self, LParen, invocation_argument, Comma, RParen); + expression = Expression::new(self.id_store.fresh(), ExpressionKind::Call { + f: Box::new(expression), + arguments, + }); + }, + _ => break, + } + } + Ok(expression) } #[recursive_descent_method] diff --git a/schala-lang/language/src/parsing/new_tests.rs b/schala-lang/language/src/parsing/new_tests.rs index 2e90ba2..8dad68f 100644 --- a/schala-lang/language/src/parsing/new_tests.rs +++ b/schala-lang/language/src/parsing/new_tests.rs @@ -120,7 +120,6 @@ macro_rules! assert_fail_expr { assert_eq!(err.msg, $failure); }; } - #[test] fn basic_literals() { use ExpressionKind::*; @@ -202,14 +201,14 @@ fn operators() { assert_expr!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1)))); } -//TODO have a test for dot accessors -/* - assert_expr!("a.b.c.d", exst!(binexp!(".", - binexp!(".", - binexp!(".", val!("a"), val!("b")), - val!("c")), - val!("d")))); -*/ +#[test] +fn accessors() { + /* + assert_expr!("a.b"); + assert_expr!("a.b.c.d()"); + assert_expr!("a.b().c.d()"); + */ +} #[test] fn tuples() { @@ -273,17 +272,19 @@ fn index() { indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] }) ); - //TODO this is a parser bug - /* - assert_expr!("perspicacity()[a]", expr(Index{ - indexee: bx(expr(Call { - f: bx(expr(Value(qn!(perspicacity)))), - arguments: vec![] - })), - indexers: vec![expr(Value(qn!(a)))] - })); - */ - //TODO parse_test("a()[b]()[d]") + assert_expr!( + "perspicacity()[a]", + expr(Index { + indexee: bx(expr(Call { f: bx(expr(Value(qn!(perspicacity)))), arguments: vec![] })), + indexers: vec![expr(Value(qn!(a)))] + }) + ); + + let a = expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] }); + let b = expr(Index { indexee: bx(a), indexers: vec![expr(Value(qn!(b)))] }); + let c = expr(Call { f: bx(b), arguments: vec![] }); + let d = expr(Index { indexee: bx(c), indexers: vec![expr(Value(qn!(d)))] }); + assert_expr!("a()[b]()[d]", d); assert_fail_expr!("a[]", "Empty index expressions are not allowed"); } diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index 7f60eb7..eee4e0d 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -1,5 +1,6 @@ #![cfg(test)] use test_case::test_case; +use pretty_assertions::assert_eq; use crate::{ symbol_table::SymbolTable,