diff --git a/schala-lang/src/parsing/combinator.rs b/schala-lang/src/parsing/combinator.rs index 0834c36..f708d8e 100644 --- a/schala-lang/src/parsing/combinator.rs +++ b/schala-lang/src/parsing/combinator.rs @@ -176,10 +176,82 @@ fn prefix_expr(input: Span) -> ParseResult { )(input) } -fn extended_expr(input: Span) -> ParseResult { - context("extended-expr", primary_expr)(input) +#[derive(Debug)] +enum ExtendedPart<'a> { + Index(Vec), + Call(Vec), + Accessor(&'a str), } +fn extended_expr(input: Span) -> ParseResult { + let (s, (primary, parts)) = context("extended-expr", + pair(primary_expr, many0(extended_expr_part)))(input)?; + + let mut expression = Expression::new(fresh_id(&s), primary); + for part in parts.into_iter() { + let kind = match part { + ExtendedPart::Index(indexers) => { + ExpressionKind::Index { indexee: Box::new(expression), indexers } + }, + ExtendedPart::Call(arguments) => { + ExpressionKind::Call { f: Box::new(expression), arguments } + } + ExtendedPart::Accessor(name) => { + let name = rc_string(name); + ExpressionKind::Access { name, expr: Box::new(expression) } + }, + }; + expression = Expression::new(fresh_id(&s), kind); + } + + Ok((s, expression.kind)) +} + +fn extended_expr_part(input: Span) -> ParseResult { + fn index_part(input: Span) -> ParseResult> { + delimited( + tok(char('[')), + separated_list1(tok(char(',')), expression), + tok(char(']')), + )(input) + } + + fn call_part(input: Span) -> ParseResult> { + delimited( + tok(char('(')), + separated_list0(tok(char(',')), invocation_argument), + tok(char(')')), + )(input) + } + + fn access_part(input: Span) -> ParseResult<&str> { + preceded( + tok(char('.')), + map(identifier, |item| *item.fragment()) + )(input) + } + + alt(( + map(index_part, ExtendedPart::Index), + map(call_part, ExtendedPart::Call), + map(access_part, ExtendedPart::Accessor) + ))(input) +} + + //TODO this shouldn't be an expression b/c type annotations disallowed here +fn invocation_argument(input: Span) -> ParseResult { + alt(( + map(tok(char('_')), |_| InvocationArgument::Ignored), + map(tuple(( + tok(identifier), + tok(char('=')), + expression, + )), |(name, _, expr)| InvocationArgument::Keyword { name: rc_string(name.fragment()), expr }), + map(expression, InvocationArgument::Positional), + ))(input) +} + + fn primary_expr(input: Span) -> ParseResult { context("primary-expr", alt(( list_expr, paren_expr, diff --git a/schala-lang/src/parsing/test.rs b/schala-lang/src/parsing/test.rs index 0f7fcb8..fa4f9be 100644 --- a/schala-lang/src/parsing/test.rs +++ b/schala-lang/src/parsing/test.rs @@ -222,8 +222,8 @@ fn prefix_exps() { assert_expr_comb!("-0.2", prefixop("-", expr(FloatLiteral(0.2)))); assert_expr_comb!("!3", prefixop("!", expr(NatLiteral(3)))); assert_expr_comb!("!t", prefixop("!", expr(Value(qn!(t))))); - assert_expr!("a <- -b", binop("<-", expr(Value(qn!(a))), prefixop("-", expr(Value(qn!(b)))))); - assert_expr!("a <--b", binop("<--", expr(Value(qn!(a))), expr(Value(qn!(b))))); + assert_expr_comb!("a <- -b", binop("<-", expr(Value(qn!(a))), prefixop("-", expr(Value(qn!(b)))))); + assert_expr_comb!("a <--b", binop("<--", expr(Value(qn!(a))), expr(Value(qn!(b))))); } #[test] @@ -239,15 +239,15 @@ fn operators() { fn accessors() { use ExpressionKind::*; - assert_expr!("a.b", expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })); - assert_expr!( + assert_expr_comb!("a.b", expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })); + assert_expr_comb!( "a.b.c", expr(Access { name: rc("c"), expr: bx(expr(Access { name: rc("b"), expr: bx(expr(Value(qn!(a)))) })) }) ); - assert_expr!( + assert_expr_comb!( "a.b.c(3)", expr(Call { f: bx(expr(Access { @@ -257,7 +257,7 @@ fn accessors() { arguments: vec![InvocationArgument::Positional(expr(NatLiteral(3)))], }) ); - assert_expr!( + assert_expr_comb!( "a.b().c", expr(Access { name: rc("c"), @@ -296,7 +296,7 @@ fn identifiers() { assert_expr_comb!("alpha::beta::gamma", expr(Value(qn!(alpha, beta, gamma)))); assert_expr_comb!("a + b", binop("+", expr(Value(qn!(a))), expr(Value(qn!(b))))); assert_expr_comb!("None", expr(Value(qn!(None)))); - assert_expr!( + assert_expr_comb!( "thing::item::call()", expr(Call { f: bx(expr(Value(qn!(thing, item, call)))), arguments: vec![] }) ); @@ -324,14 +324,14 @@ fn named_struct() { #[test] fn index() { use ExpressionKind::*; - assert_expr!( + assert_expr_comb!( "armok[b,c]", expr(Index { indexee: bx(expr(Value(qn!(armok)))), indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] }) ); - assert_expr!( + assert_expr_comb!( "a[b,c][1]", expr(Index { indexee: bx(expr(Index { @@ -341,7 +341,7 @@ fn index() { indexers: vec![expr(NatLiteral(1))] }) ); - assert_expr!( + assert_expr_comb!( "perspicacity()[a]", expr(Index { indexee: bx(expr(Call { f: bx(expr(Value(qn!(perspicacity)))), arguments: vec![] })), @@ -353,7 +353,7 @@ fn index() { 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_expr_comb!("a()[b]()[d]", d); assert_fail_expr!("a[]", "Empty index expressions are not allowed"); }