diff --git a/src/protocol/parser/pass_definitions.rs b/src/protocol/parser/pass_definitions.rs index c6b2f994762f6f61637e05f7c3fd29e8198a074b..66ed638cc16da38f731d12225efd5d7c0a7da217 100644 --- a/src/protocol/parser/pass_definitions.rs +++ b/src/protocol/parser/pass_definitions.rs @@ -367,7 +367,6 @@ impl PassDefinitions { scope_node: ScopeNode::new_invalid(), first_unique_id_in_scope: -1, next_unique_id_in_scope: -1, - relative_pos_in_parent: 0, locals: Vec::new(), labels: Vec::new(), next: StatementId::new_invalid(), @@ -456,6 +455,19 @@ impl PassDefinitions { let fork_stmt = &mut ctx.heap[id]; fork_stmt.end_fork = end_fork; + } else if ident == KW_STMT_SELECT { + let id = self.consume_select_statement(module, iter, ctx)?; + section.push(id.upcast()); + + let end_select = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{ + this, + start_select: id, + next: StatementId::new_invalid(), + }); + section.push(end_select.upcast()); + + let select_stmt = &mut ctx.heap[id]; + select_stmt.end_select = end_select; } else if ident == KW_STMT_RETURN { let id = self.consume_return_statement(module, iter, ctx)?; section.push(id.upcast()); @@ -474,9 +486,9 @@ impl PassDefinitions { // Two fallback possibilities: the first one is a memory // declaration, the other one is to parse it as a normal // expression. This is a bit ugly. - if let Some((memory_stmt_id, assignment_stmt_id)) = self.maybe_consume_memory_statement(module, iter, ctx)? { + if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? { + consume_token(&module.source, iter, TokenKind::SemiColon)?; 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()); @@ -484,9 +496,9 @@ impl PassDefinitions { } } 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)? { + if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? { + consume_token(&module.source, iter, TokenKind::SemiColon)?; 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()); @@ -534,7 +546,6 @@ impl PassDefinitions { scope_node: ScopeNode::new_invalid(), first_unique_id_in_scope: -1, next_unique_id_in_scope: -1, - relative_pos_in_parent: 0, locals: Vec::new(), labels: Vec::new(), next: StatementId::new_invalid(), @@ -611,7 +622,7 @@ impl PassDefinitions { this, span: break_span, label, - target: None, + target: EndWhileStatementId::new_invalid(), })) } @@ -630,7 +641,7 @@ impl PassDefinitions { this, span: continue_span, label, - target: None + target: WhileStatementId::new_invalid(), })) } @@ -671,6 +682,53 @@ impl PassDefinitions { })) } + fn consume_select_statement( + &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx + ) -> Result { + let select_span = consume_exact_ident(&module.source, iter, KW_STMT_SELECT)?; + consume_token(&module.source, iter, TokenKind::OpenCurly)?; + + let mut cases = Vec::new(); + let mut next = iter.next(); + + while Some(TokenKind::CloseCurly) != next { + let guard = match self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? { + Some(guard_mem_stmt) => guard_mem_stmt.upcast().upcast(), + None => { + let start_pos = iter.last_valid_pos(); + let expr = self.consume_expression(module, iter, ctx)?; + let end_pos = iter.last_valid_pos(); + + let guard_expr_stmt = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{ + this, + span: InputSpan::from_positions(start_pos, end_pos), + expression: expr, + next: StatementId::new_invalid(), + }); + + guard_expr_stmt.upcast() + }, + }; + consume_token(&module.source, iter, TokenKind::ArrowRight)?; + let block = self.consume_block_or_wrapped_statement(module, iter, ctx)?; + cases.push(SelectCase{ + guard, block, + involved_ports: Vec::with_capacity(1) + }); + + next = iter.next(); + } + + consume_token(&module.source, iter, TokenKind::CloseCurly)?; + + Ok(ctx.heap.alloc_select_statement(|this| SelectStatement{ + this, + span: select_span, + cases, + end_select: EndSelectStatementId::new_invalid(), + })) + } + fn consume_return_statement( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx ) -> Result { @@ -707,7 +765,7 @@ impl PassDefinitions { this, span: goto_span, label, - target: None + target: LabeledStatementId::new_invalid(), })) } @@ -866,9 +924,14 @@ impl PassDefinitions { Ok(()) } - fn maybe_consume_memory_statement( + /// Attempts to consume a memory statement (a statement along the lines of + /// `type var_name = initial_expr`). Will return `Ok(None)` if it didn't + /// seem like there was a memory statement, `Ok(Some(...))` if there was + /// one, and `Err(...)` if its reasonable to assume that there was a memory + /// statement, but we failed to parse it. + fn maybe_consume_memory_statement_without_semicolon( &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx - ) -> Result, ParseError> { + ) -> Result, ParseError> { // This is a bit ugly. It would be nicer if we could somehow // consume the expression with a type hint if we do get a valid // type, but we don't get an identifier following it @@ -888,12 +951,10 @@ impl PassDefinitions { let memory_span = InputSpan::from_positions(parser_type.full_span.begin, identifier.span.end); let assign_span = consume_token(&module.source, iter, TokenKind::Equal)?; - let initial_expr_begin_pos = iter.last_valid_pos(); let initial_expr_id = self.consume_expression(module, iter, ctx)?; let initial_expr_end_pos = iter.last_valid_pos(); - consume_token(&module.source, iter, TokenKind::SemiColon)?; - // Allocate the memory statement with the variable + // Create the AST variable let local_id = ctx.heap.alloc_variable(|this| Variable{ this, kind: VariableKind::Local, @@ -902,18 +963,13 @@ impl PassDefinitions { relative_pos_in_block: 0, unique_id_in_scope: -1, }); - let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ - this, - span: memory_span, - variable: local_id, - next: StatementId::new_invalid() - }); - // Allocate the initial assignment + // Create the initial assignment expression + // Note: we set the initial variable declaration here let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{ this, identifier, - declaration: None, + declaration: Some(local_id), used_as_binding_target: false, parent: ExpressionParent::None, unique_id_in_definition: -1, @@ -928,14 +984,17 @@ impl PassDefinitions { parent: ExpressionParent::None, unique_id_in_definition: -1, }); - let assignment_stmt_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{ + + // Put both together in the memory statement + let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ this, - span: InputSpan::from_positions(initial_expr_begin_pos, initial_expr_end_pos), - expression: assignment_expr_id.upcast(), - next: StatementId::new_invalid(), + span: memory_span, + variable: local_id, + initial_expr: assignment_expr_id, + next: StatementId::new_invalid() }); - return Ok(Some((memory_stmt_id, assignment_stmt_id))) + return Ok(Some(memory_stmt_id)); } }