diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index 0d15c82d1eab600fdb4e7ecd22714d90077f593d..ab2b18ad81aa014acecabc50db0c7b131aa6c945 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -402,8 +402,10 @@ impl PassDefinitions { let id = self.consume_if_statement(module, iter, ctx)?; section.push(id.upcast()); - let end_if = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{ - this, start_if: id, next: StatementId::new_invalid() + let end_if = ctx.heap.alloc_end_if_statement(|this| EndIfStatement { + this, + start_if: id, + next: StatementId::new_invalid() }); section.push(end_if.upcast()); @@ -413,8 +415,10 @@ impl PassDefinitions { let id = self.consume_while_statement(module, iter, ctx)?; section.push(id.upcast()); - let end_while = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement{ - this, start_while: id, next: StatementId::new_invalid() + let end_while = ctx.heap.alloc_end_while_statement(|this| EndWhileStatement { + this, + start_while: id, + next: StatementId::new_invalid() }); section.push(end_while.upcast()); @@ -443,7 +447,7 @@ impl PassDefinitions { let id = self.consume_fork_statement(module, iter, ctx)?; section.push(id.upcast()); - let end_fork = ctx.heap.alloc_end_fork_statement(|this| EndForkStatement{ + let end_fork = ctx.heap.alloc_end_fork_statement(|this| EndForkStatement { this, start_fork: id, next: StatementId::new_invalid(), @@ -478,6 +482,15 @@ impl PassDefinitions { section.push(id.upcast()); } } + } else if next == TokenKind::OpenParen { + // Same as above: memory statement or normal expression + if let Some((memory_stmt_id, assignment_stmt_id)) = self.maybe_consume_memory_statement(module, iter, ctx)? { + section.push(memory_stmt_id.upcast().upcast()); + section.push(assignment_stmt_id.upcast()); + } else { + let id = self.consume_expression_statement(module, iter, ctx)?; + section.push(id.upcast()); + } } else { let id = self.consume_expression_statement(module, iter, ctx)?; section.push(id.upcast()); @@ -1330,10 +1343,63 @@ impl PassDefinitions { let next = iter.next(); let result = if next == Some(TokenKind::OpenParen) { - // Expression between parentheses + // Something parenthesized. This can mean several things: we have + // a parenthesized expression or we have a tuple literal. They are + // ambiguous when the tuple has one member. But like the tuple type + // parsing we interpret all one-tuples as parenthesized expressions. + // + // Practically (to prevent unnecessary `consume_expression` calls) + // we distinguish the zero-tuple, the parenthesized expression, and + // the N-tuple (for N > 1). + let open_paren_pos = iter.next_start_position(); iter.consume(); - let result = self.consume_expression(module, iter, ctx)?; - consume_token(&module.source, iter, TokenKind::CloseParen)?; + let result = if Some(TokenKind::CloseParen) == iter.next() { + // Zero-tuple + let (_, close_paren_pos) = iter.next_positions(); + iter.consume(); + + let literal_id = ctx.heap.alloc_literal_expression(|this| LiteralExpression{ + this, + span: InputSpan::from_positions(open_paren_pos, close_paren_pos), + value: Literal::Tuple(Vec::new()), + parent: ExpressionParent::None, + unique_id_in_definition: -1, + }); + + literal_id.upcast() + } else { + // Start by consuming one expression, then check for a comma + let expr_id = self.consume_expression(module, iter, ctx)?; + if Some(TokenKind::Comma) == iter.next() && Some(TokenKind::CloseParen) != iter.peek() { + // Must be an N-tuple + iter.consume(); // the comma + let mut scoped_section = self.expressions.start_section(); + scoped_section.push(expr_id); + + let mut close_paren_pos = open_paren_pos; + consume_comma_separated_until( + TokenKind::CloseParen, &module.source, iter, ctx, + |_source, iter, ctx| self.consume_expression(module, iter, ctx), + &mut scoped_section, "an expression", Some(&mut close_paren_pos) + )?; + debug_assert!(scoped_section.len() > 1); // peeked token wasn't CloseParen, must be expression + + let literal_id = ctx.heap.alloc_literal_expression(|this| LiteralExpression{ + this, + span: InputSpan::from_positions(open_paren_pos, close_paren_pos), + value: Literal::Tuple(scoped_section.into_vec()), + parent: ExpressionParent::None, + unique_id_in_definition: -1, + }); + + literal_id.upcast() + } else { + // Assume we're dealing with a normal expression + consume_token(&module.source, iter, TokenKind::CloseParen)?; + + expr_id + } + }; result } else if next == Some(TokenKind::OpenCurly) { @@ -1385,6 +1451,7 @@ impl PassDefinitions { // May be a variable, a type instantiation or a function call. If we // have a single identifier that we cannot find in the type table // then we're going to assume that we're dealing with a variable. + let ident_span = iter.next_span(); let ident_text = module.source.section_at_span(ident_span); let symbol = ctx.symbols.get_symbol_by_name(SymbolScope::Module(module.root_id), ident_text);