Changeset - 9e689afc94cb
[Not reviewed]
0 4 0
MH - 3 years ago 2022-02-11 15:40:33
contact@maxhenger.nl
Initial rewrite implementation, pending integration and typesystem refactor
4 files changed with 161 insertions and 121 deletions:
0 comments (0 inline, 0 general)
src/protocol/ast.rs
Show inline comments
 
@@ -1154,97 +1154,96 @@ impl Statement {
 
            Statement::Fork(v) => v.span,
 
            Statement::Select(v) => v.span,
 
            Statement::Return(v) => v.span,
 
            Statement::Goto(v) => v.span,
 
            Statement::New(v) => v.span,
 
            Statement::Expression(v) => v.span,
 
            Statement::EndBlock(_)
 
            | Statement::EndIf(_)
 
            | Statement::EndWhile(_)
 
            | Statement::EndSynchronous(_)
 
            | Statement::EndFork(_)
 
            | Statement::EndSelect(_) => unreachable!(),
 
        }
 
    }
 
    pub fn link_next(&mut self, next: StatementId) {
 
        match self {
 
            Statement::Block(stmt) => stmt.next = next,
 
            Statement::EndBlock(stmt) => stmt.next = next,
 
            Statement::Local(stmt) => match stmt {
 
                LocalStatement::Channel(stmt) => stmt.next = next,
 
                LocalStatement::Memory(stmt) => stmt.next = next,
 
            },
 
            Statement::EndIf(stmt) => stmt.next = next,
 
            Statement::EndWhile(stmt) => stmt.next = next,
 
            Statement::EndSynchronous(stmt) => stmt.next = next,
 
            Statement::EndFork(stmt) => stmt.next = next,
 
            Statement::EndSelect(stmt) => stmt.next = next,
 
            Statement::New(stmt) => stmt.next = next,
 
            Statement::Expression(stmt) => stmt.next = next,
 
            Statement::Return(_)
 
            | Statement::Break(_)
 
            | Statement::Continue(_)
 
            | Statement::Synchronous(_)
 
            | Statement::Fork(_)
 
            | Statement::Select(_)
 
            | Statement::Goto(_)
 
            | Statement::While(_)
 
            | Statement::Labeled(_)
 
            | Statement::If(_) => unreachable!(),
 
        }
 
    }
 

	
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct BlockStatement {
 
    pub this: BlockStatementId,
 
    // Phase 1: parser
 
    pub is_implicit: bool,
 
    pub span: InputSpan, // of the complete block
 
    pub statements: Vec<StatementId>,
 
    pub end_block: EndBlockStatementId,
 
    // Phase 2: linker
 
    pub scope: ScopeId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndBlockStatement {
 
    pub this: EndBlockStatementId,
 
    // Parser
 
    pub start_block: BlockStatementId,
 
    // Validation/Linking
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub enum LocalStatement {
 
    Memory(MemoryStatement),
 
    Channel(ChannelStatement),
 
}
 

	
 
impl LocalStatement {
 
    pub fn this(&self) -> LocalStatementId {
 
        match self {
 
            LocalStatement::Memory(stmt) => stmt.this.upcast(),
 
            LocalStatement::Channel(stmt) => stmt.this.upcast(),
 
        }
 
    }
 
    pub fn as_memory(&self) -> &MemoryStatement {
 
        match self {
 
            LocalStatement::Memory(result) => result,
 
            _ => panic!("Unable to cast `LocalStatement` to `MemoryStatement`"),
 
        }
 
    }
 
    pub fn as_channel(&self) -> &ChannelStatement {
 
        match self {
 
            LocalStatement::Channel(result) => result,
 
            _ => panic!("Unable to cast `LocalStatement` to `ChannelStatement`"),
 
        }
 
    }
 
    pub fn span(&self) -> InputSpan {
 
        match self {
 
            LocalStatement::Channel(v) => v.span,
 
            LocalStatement::Memory(v) => v.span,
 
        }
 
    }
 
@@ -1349,96 +1348,98 @@ pub struct ContinueStatement {
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "continue" keyword
 
    pub label: Option<Identifier>,
 
    // Phase 2: linker
 
    pub target: WhileStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SynchronousStatement {
 
    pub this: SynchronousStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "sync" keyword
 
    pub scope: ScopeId,
 
    pub body: StatementId,
 
    pub end_sync: EndSynchronousStatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndSynchronousStatement {
 
    pub this: EndSynchronousStatementId,
 
    pub start_sync: SynchronousStatementId,
 
    // Phase 2: linker
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ForkStatement {
 
    pub this: ForkStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "fork" keyword
 
    pub left_body: StatementId,
 
    pub right_body: Option<StatementId>,
 
    pub end_fork: EndForkStatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndForkStatement {
 
    pub this: EndForkStatementId,
 
    pub start_fork: ForkStatementId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectStatement {
 
    pub this: SelectStatementId,
 
    pub span: InputSpan, // of the "select" keyword
 
    pub cases: Vec<SelectCase>,
 
    pub end_select: EndSelectStatementId,
 
    pub relative_pos_in_parent: i32,
 
    pub next: StatementId, // note: the select statement will be transformed into other AST elements, this `next` jumps to those replacement statements
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct SelectCase {
 
    // The guard statement of a `select` is either a MemoryStatement or an
 
    // ExpressionStatement. Nothing else is allowed by the initial parsing
 
    pub guard: StatementId,
 
    pub body: StatementId,
 
    pub scope: ScopeId,
 
    // Phase 2: Validation and Linking
 
    pub involved_ports: Vec<(CallExpressionId, ExpressionId)>, // call to `get` and its port argument
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct EndSelectStatement {
 
    pub this: EndSelectStatementId,
 
    pub start_select: SelectStatementId,
 
    pub next: StatementId,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct ReturnStatement {
 
    pub this: ReturnStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "return" keyword
 
    pub expressions: Vec<ExpressionId>,
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct GotoStatement {
 
    pub this: GotoStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "goto" keyword
 
    pub label: Identifier,
 
    // Phase 2: linker
 
    pub target: LabeledStatementId, // invalid if not yet set
 
}
 

	
 
#[derive(Debug, Clone)]
 
pub struct NewStatement {
 
    pub this: NewStatementId,
 
    // Phase 1: parser
 
    pub span: InputSpan, // of the "new" keyword
 
    pub expression: CallExpressionId,
 
    // Phase 2: linker
 
    pub next: StatementId,
 
}
 

	
src/protocol/parser/pass_definitions.rs
Show inline comments
 
@@ -393,97 +393,96 @@ impl PassDefinitions {
 
                // expression. This is a bit ugly.
 
                if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                    consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                    return Ok(memory_stmt_id.upcast().upcast());
 
                } else {
 
                    let id = self.consume_expression_statement(module, iter, ctx)?;
 
                    return Ok(id.upcast());
 
                }
 
            }
 
        } else if next == TokenKind::OpenParen {
 
            // Same as above: memory statement or normal expression
 
            if let Some(memory_stmt_id) = self.maybe_consume_memory_statement_without_semicolon(module, iter, ctx)? {
 
                consume_token(&module.source, iter, TokenKind::SemiColon)?;
 
                return Ok(memory_stmt_id.upcast().upcast());
 
            } else {
 
                let id = self.consume_expression_statement(module, iter, ctx)?;
 
                return Ok(id.upcast());
 
            }
 
        } else {
 
            let id = self.consume_expression_statement(module, iter, ctx)?;
 
            return Ok(id.upcast());
 
        }
 
    }
 

	
 
    fn consume_block_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<BlockStatementId, ParseError> {
 
        let open_curly_span = consume_token(&module.source, iter, TokenKind::OpenCurly)?;
 

	
 
        let mut stmt_section = self.statements.start_section();
 
        let mut next = iter.next();
 
        while next != Some(TokenKind::CloseCurly) {
 
            if next.is_none() {
 
                return Err(ParseError::new_error_str_at_pos(
 
                    &module.source, iter.last_valid_pos(), "expected a statement or '}'"
 
                ));
 
            }
 
            let stmt_id = self.consume_statement(module, iter, ctx)?;
 
            stmt_section.push(stmt_id);
 
            next = iter.next();
 
        }
 

	
 
        let statements = stmt_section.into_vec();
 
        let mut block_span = consume_token(&module.source, iter, TokenKind::CloseCurly)?;
 
        block_span.begin = open_curly_span.begin;
 

	
 
        let block_id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
            this,
 
            is_implicit: false,
 
            span: block_span,
 
            statements,
 
            end_block: EndBlockStatementId::new_invalid(),
 
            scope: ScopeId::new_invalid(),
 
            next: StatementId::new_invalid(),
 
        });
 
        let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Block(block_id)));
 

	
 
        let end_block_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
            this, start_block: block_id, next: StatementId::new_invalid()
 
        });
 

	
 
        let block_stmt = &mut ctx.heap[block_id];
 
        block_stmt.end_block = end_block_id;
 
        block_stmt.scope = scope_id;
 

	
 
        Ok(block_id)
 
    }
 

	
 
    fn consume_if_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<IfStatementId, ParseError> {
 
        let if_span = consume_exact_ident(&module.source, iter, KW_STMT_IF)?;
 
        consume_token(&module.source, iter, TokenKind::OpenParen)?;
 
        let test = self.consume_expression(module, iter, ctx)?;
 
        consume_token(&module.source, iter, TokenKind::CloseParen)?;
 

	
 
        // Consume bodies of if-statement
 
        let true_body = IfStatementCase{
 
            body: self.consume_statement(module, iter, ctx)?,
 
            scope: ScopeId::new_invalid(),
 
        };
 

	
 
        let false_body = if has_ident(&module.source, iter, KW_STMT_ELSE) {
 
            iter.consume();
 
            let false_body = IfStatementCase{
 
                body: self.consume_statement(module, iter, ctx)?,
 
                scope: ScopeId::new_invalid(),
 
            };
 

	
 
            let false_body_scope_id = false_body.scope;
 
            Some(false_body)
 
        } else {
 
            None
 
        };
 

	
 
        // Construct AST elements
 
        let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{
 
@@ -638,97 +637,100 @@ impl PassDefinitions {
 

	
 
    fn consume_select_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<SelectStatementId, ParseError> {
 
        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_statement(module, iter, ctx)?;
 
            cases.push(SelectCase{
 
                guard,
 
                body: block,
 
                scope: ScopeId::new_invalid(),
 
                involved_ports: Vec::with_capacity(1)
 
            });
 

	
 
            next = iter.next();
 
        }
 

	
 
        consume_token(&module.source, iter, TokenKind::CloseCurly)?;
 

	
 
        let num_cases = cases.len();
 
        let select_stmt_id = ctx.heap.alloc_select_statement(|this| SelectStatement{
 
            this,
 
            span: select_span,
 
            cases,
 
            end_select: EndSelectStatementId::new_invalid(),
 
            relative_pos_in_parent: -1,
 
            next: StatementId::new_invalid(),
 
        });
 

	
 
        let end_select_stmt_id = ctx.heap.alloc_end_select_statement(|this| EndSelectStatement{
 
            this,
 
            start_select: select_stmt_id,
 
            next: StatementId::new_invalid(),
 
        });
 

	
 
        let select_stmt = &mut ctx.heap[select_stmt_id];
 
        select_stmt.end_select = end_select_stmt_id;
 

	
 
        for case_index in 0..num_cases {
 
            let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::SelectCase(select_stmt_id, case_index as u32)));
 
            let select_stmt = &mut ctx.heap[select_stmt_id];
 
            let select_case = &mut select_stmt.cases[case_index];
 
            select_case.scope = scope_id;
 
        }
 

	
 
        return Ok(select_stmt_id)
 
    }
 

	
 
    fn consume_return_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<ReturnStatementId, ParseError> {
 
        let return_span = consume_exact_ident(&module.source, iter, KW_STMT_RETURN)?;
 
        let mut scoped_section = self.expressions.start_section();
 

	
 
        consume_comma_separated_until(
 
            TokenKind::SemiColon, &module.source, iter, ctx,
 
            |_source, iter, ctx| self.consume_expression(module, iter, ctx),
 
            &mut scoped_section, "an expression", None
 
        )?;
 
        let expressions = scoped_section.into_vec();
 

	
 
        if expressions.is_empty() {
 
            return Err(ParseError::new_error_str_at_span(&module.source, return_span, "expected at least one return value"));
 
        } else if expressions.len() > 1 {
 
            return Err(ParseError::new_error_str_at_span(&module.source, return_span, "multiple return values are not (yet) supported"))
 
        }
 

	
 
        Ok(ctx.heap.alloc_return_statement(|this| ReturnStatement{
 
            this,
 
            span: return_span,
 
            expressions
 
        }))
 
    }
 

	
 
    fn consume_goto_statement(
 
        &mut self, module: &Module, iter: &mut TokenIter, ctx: &mut PassCtx
 
    ) -> Result<GotoStatementId, ParseError> {
src/protocol/parser/pass_rewriting.rs
Show inline comments
 
use crate::collections::*;
 
use crate::protocol::*;
 

	
 
use super::visitor::*;
 

	
 
pub(crate) struct PassRewriting {
 
    current_scope: BlockStatementId,
 
    current_scope: ScopeId,
 
    statement_buffer: ScopedBuffer<StatementId>,
 
    call_expr_buffer: ScopedBuffer<CallExpressionId>,
 
    expression_buffer: ScopedBuffer<ExpressionId>,
 
    scope_buffer: ScopedBuffer<ScopeId>,
 
}
 

	
 
impl PassRewriting {
 
    pub(crate) fn new() -> Self {
 
        Self{
 
            current_scope: BlockStatementId::new_invalid(),
 
            statement_buffer: ScopedBuffer::with_capacity(16),
 
            call_expr_buffer: ScopedBuffer::with_capacity(16),
 
            expression_buffer: ScopedBuffer::with_capacity(16),
 
            current_scope: ScopeId::new_invalid(),
 
            statement_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            call_expr_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            expression_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
            scope_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_SMALL),
 
        }
 
    }
 
}
 

	
 
impl Visitor for PassRewriting {
 
    // --- Visiting procedures
 

	
 
    fn visit_component_definition(&mut self, ctx: &mut Ctx, id: ComponentDefinitionId) -> VisitorResult {
 
        let def = &ctx.heap[id];
 
        let body_id = def.body;
 
        self.current_scope = def.scope;
 
        return self.visit_block_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_function_definition(&mut self, ctx: &mut Ctx, id: FunctionDefinitionId) -> VisitorResult {
 
        let def = &ctx.heap[id];
 
        let body_id = def.body;
 
        self.current_scope = def.scope;
 
        return self.visit_block_stmt(ctx, body_id);
 
    }
 

	
 
    // --- Visiting statements (that are not the select statement)
 

	
 
    fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult {
 
        let block_stmt = &ctx.heap[id];
 
        let stmt_section = self.statement_buffer.start_section_initialized(&block_stmt.statements);
 

	
 
        self.current_scope = id;
 
        self.current_scope = block_stmt.scope;
 
        for stmt_idx in 0..stmt_section.len() {
 
            self.visit_stmt(ctx, stmt_section[stmt_idx])?;
 
        }
 

	
 
        stmt_section.forget();
 
        return Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let labeled_stmt = &ctx.heap[id];
 
        let body_id = labeled_stmt.body;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult {
 
        let if_stmt = &ctx.heap[id];
 
        let true_body_id = if_stmt.true_case;
 
        let false_body_id = if_stmt.false_case;
 
        let true_case = if_stmt.true_case;
 
        let false_case = if_stmt.false_case;
 

	
 
        self.visit_stmt(ctx, true_body_id.body)?;
 
        if let Some(false_body_id) = false_body_id {
 
            self.visit_stmt(ctx, false_body_id.body)?;
 
        self.current_scope = true_case.scope;
 
        self.visit_stmt(ctx, true_case.body)?;
 
        if let Some(false_case) = false_case {
 
            self.current_scope = false_case.scope;
 
            self.visit_stmt(ctx, false_case.body)?;
 
        }
 

	
 
        return Ok(())
 
    }
 

	
 
    fn visit_while_stmt(&mut self, ctx: &mut Ctx, id: WhileStatementId) -> VisitorResult {
 
        let while_stmt = &ctx.heap[id];
 
        let body_id = while_stmt.body;
 
        self.current_scope = while_stmt.scope;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    fn visit_synchronous_stmt(&mut self, ctx: &mut Ctx, id: SynchronousStatementId) -> VisitorResult {
 
        let sync_stmt = &ctx.heap[id];
 
        let body_id = sync_stmt.body;
 
        self.current_scope = sync_stmt.scope;
 
        return self.visit_stmt(ctx, body_id);
 
    }
 

	
 
    // --- Visiting the select statement
 

	
 
    fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult {
 
        // Utility for the last stage of rewriting process. Note that caller
 
        // still needs to point the end of the if-statement to the end of the
 
        // replacement statement of the select statement.
 
        fn transform_select_case_code(ctx: &mut Ctx, select_id: SelectStatementId, case_index: usize, select_var_id: VariableId) -> (IfStatementId, EndIfStatementId) {
 
            // Retrieve statement IDs associated with case
 
            let case = &ctx.heap[select_id].cases[case_index];
 
            let case_guard_id = case.guard;
 
            let case_body_id = case.body;
 
            let case_scope_id = case.scope;
 

	
 
            // Create the if-statement for the result of the select statement
 
            let compare_expr_id = create_ast_equality_comparison_expr(ctx, select_var_id, case_index as u64);
 
            let true_case = IfStatementCase{
 
                body: case_guard_id, // which is linked up to the body
 
                scope: case_scope_id,
 
            };
 
            let (if_stmt_id, end_if_stmt_id) = create_ast_if_stmt(ctx, compare_expr_id.upcast(), true_case, None);
 

	
 
            // Link up body statement to end-if
 
            set_ast_statement_next(ctx, case_body_id, end_if_stmt_id.upcast());
 

	
 
            return (if_stmt_id, end_if_stmt_id)
 
        }
 

	
 
        // We're going to transform the select statement by a block statement
 
        // containing builtin runtime-calls. And to do so we create temporary
 
        // variables and move some other statements around.
 
        // Precreate the block that will end up containing all of the
 
        // transformed statements. Also precreate the scope associated with it
 
        let (outer_block_id, outer_end_block_id, outer_scope_id) =
 
            create_ast_block_stmt(ctx, Vec::new());
 

	
 
        // The "select" and the "end select" statement will act like trampolines
 
        // that jump to the replacement block. So set the child/parent
 
        // relationship already.
 
        // --- for the statements
 
        let select_stmt = &mut ctx.heap[id];
 
        select_stmt.next = outer_block_id.upcast();
 
        let end_select_stmt_id = select_stmt.end_select;
 
        let select_stmt_relative_pos = select_stmt.relative_pos_in_parent;
 

	
 
        let outer_end_block_stmt = &mut ctx.heap[outer_end_block_id];
 
        outer_end_block_stmt.next = end_select_stmt_id.upcast();
 

	
 
        // --- for the scopes
 
        link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, self.current_scope, outer_scope_id, select_stmt_relative_pos);
 

	
 
        // Create statements that will create temporary variables for all of the
 
        // ports passed to the "get" calls in the select case guards.
 
        let select_stmt = &ctx.heap[id];
 
        let total_num_cases = select_stmt.cases.len();
 
        let mut total_num_ports = 0;
 
        let end_select_stmt_id = select_stmt.end_select;
 
        let end_select = &ctx.heap[end_select_stmt_id];
 
        let stmt_id_after_select_stmt = end_select.next;
 

	
 
        // Put heap IDs into temporary buffers to handle borrowing rules
 
        let mut call_id_section = self.call_expr_buffer.start_section();
 
        let mut expr_id_section = self.expression_buffer.start_section();
 

	
 
        for case in select_stmt.cases.iter() {
 
            total_num_ports += case.involved_ports.len();
 
            for (call_id, expr_id) in case.involved_ports.iter().copied() {
 
                call_id_section.push(call_id);
 
                expr_id_section.push(expr_id);
 
            }
 
        }
 

	
 
        // Transform all of the call expressions by takings its argument (the
 
        // port from which we `get`) and turning it into a temporary variable.
 
        let mut transformed_stmts = Vec::with_capacity(total_num_ports); // TODO: Recompute this preallocated length, put assert at the end
 
        let mut locals = Vec::with_capacity(total_num_ports);
 

	
 
        for port_var_idx in 0..call_id_section.len() {
 
            let get_call_expr_id = call_id_section[port_var_idx];
 
            let port_expr_id = expr_id_section[port_var_idx];
 

	
 
            // Move the port expression such that it gets assigned to a temporary variable
 
            let variable_id = create_ast_variable(ctx);
 
            let variable_id = create_ast_variable(ctx, outer_scope_id);
 
            let variable_decl_stmt_id = create_ast_variable_declaration_stmt(ctx, variable_id, port_expr_id);
 

	
 
            // Replace the original port expression in the call with a reference
 
            // to the replacement variable
 
            let variable_expr_id = create_ast_variable_expr(ctx, variable_id);
 
            let call_expr = &mut ctx.heap[get_call_expr_id];
 
            call_expr.arguments[0] = variable_expr_id.upcast();
 

	
 
            transformed_stmts.push(variable_decl_stmt_id.upcast().upcast());
 
            locals.push(variable_id);
 
        }
 

	
 
        // Insert runtime calls that facilitate the semantics of the select
 
        // block.
 

	
 
        // Create the call that indicates the start of the select block
 
        {
 
            let num_cases_expression_id = create_ast_literal_integer_expr(ctx, total_num_cases as u64);
 
            let num_ports_expression_id = create_ast_literal_integer_expr(ctx, total_num_ports as u64);
 
            let arguments = vec![
 
                num_cases_expression_id.upcast(),
 
                num_ports_expression_id.upcast()
 
            ];
 

	
 
            let call_expression_id = create_ast_call_expr(ctx, Method::SelectStart, &mut self.expression_buffer, arguments);
 
            let call_statement_id = create_ast_expression_stmt(ctx, call_expression_id.upcast());
 

	
 
            transformed_stmts.push(call_statement_id.upcast());
 
        }
 

	
 
        // Create calls for each select case that will register the ports that
 
        // we are waiting on at the runtime.
 
        {
 
            let mut total_port_index = 0;
 
            for case_index in 0..total_num_cases {
 
                let case = &ctx.heap[id].cases[case_index];
 
                let case_num_ports = case.involved_ports.len();
 

	
 
                for case_port_index in 0..case_num_ports {
 
                    // Arguments to runtime call
 
                    let port_variable_id = locals[total_port_index]; // so far this variable contains the temporary variables for the port expressions
 
                    let case_index_expr_id = create_ast_literal_integer_expr(ctx, case_index as u64);
 
                    let port_index_expr_id = create_ast_literal_integer_expr(ctx, case_port_index as u64);
 
                    let port_variable_expr_id = create_ast_variable_expr(ctx, port_variable_id);
 
                    let runtime_call_arguments = vec![
 
                        case_index_expr_id.upcast(),
 
                        port_index_expr_id.upcast(),
 
                        port_variable_expr_id.upcast()
 
                    ];
 

	
 
                    // Create runtime call, then store it
 
                    let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectRegisterCasePort, &mut self.expression_buffer, runtime_call_arguments);
 
                    let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id.upcast());
 

	
 
                    transformed_stmts.push(runtime_call_stmt_id.upcast());
 

	
 
                    total_port_index += 1;
 
                }
 
            }
 
        }
 

	
 
        // Create the variable that will hold the result of a completed select
 
        // block. Then create the runtime call that will produce this result
 
        let select_variable_id = create_ast_variable(ctx);
 
        let select_variable_id = create_ast_variable(ctx, outer_scope_id);
 
        locals.push(select_variable_id);
 

	
 
        {
 
            let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectWait, &mut self.expression_buffer, Vec::new());
 
            let variable_stmt_id = create_ast_variable_declaration_stmt(ctx, select_variable_id, runtime_call_expr_id.upcast());
 
            transformed_stmts.push(variable_stmt_id.upcast().upcast());
 
        }
 

	
 
        call_id_section.forget();
 
        expr_id_section.forget();
 

	
 
        // Precreate the block statement that will be the replacement of the
 
        // select statement. Do not set its members yet.
 
        let replacement_stmt_id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
            this,
 
            is_implicit: true,
 
            span: InputSpan::new(),
 
            statements: Vec::new(),
 
            end_block: EndBlockStatementId::new_invalid(),
 
            scope: ScopeId::new_invalid(),
 
            next: StatementId::new_invalid(),
 
        });
 
        let end_block_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
            this,
 
            start_block: replacement_stmt_id,
 
            next: stmt_id_after_select_stmt,
 
        });
 

	
 
        // Now we transform each of the select block case's guard and code into
 
        // a chained if-else statement.
 
        if total_num_cases > 0 {
 
            let (if_stmt_id, end_if_stmt_id) = transform_select_case_code(ctx, id, 0, select_variable_id);
 
            let first_end_if_stmt = &mut ctx.heap[end_if_stmt_id];
 
            first_end_if_stmt.next = outer_end_block_id.upcast();
 

	
 
            let mut last_if_stmt_id = if_stmt_id;
 
            let mut last_end_if_stmt_id = end_if_stmt_id;
 
            transformed_stmts.push(last_if_stmt_id.upcast());
 

	
 
            for case_index in 1..total_num_cases {
 
                let (if_stmt_id, end_if_stmt_id) = transform_select_case_code(ctx, id, case_index, select_variable_id);
 
                let last_if_stmt = &mut ctx.heap[last_if_stmt_id];
 
                // last_if_stmt.false_case = Some(if_stmt_id.upcast());
 

	
 
                // TODO:
 
                //  1. Change scoping such that it is a separate datastructure with separate IDs
 
                //  2. Change statements that contain "implicit scopes" to explicitly point to the appropriate scopes
 
                //  3. Continue here setting the true-body and false-body.
 
                //  4. Figure out how we're going to link everything up again
 
            }
 
        }
 

	
 
        // let block = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
        //     this,
 
        //     is_implicit: true,
 
        //     span: stmt.span,
 
        //     statements: vec![],
 
        //     end_block: EndBlockStatementId(),
 
        //     scope_node: ScopeNode {},
 
        //     first_unique_id_in_scope: 0,
 
        //     next_unique_id_in_scope: 0,
 
        //     locals,
 
        //     labels: vec![],
 
        //     next: ()
 
        // });
 
                let false_case_scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(last_if_stmt_id, false)));
 
                set_ast_if_statement_false_body(ctx, last_if_stmt_id, last_end_if_stmt_id, IfStatementCase{ body: if_stmt_id.upcast(), scope: false_case_scope_id });
 

	
 
                let end_if_stmt = &mut ctx.heap[end_if_stmt_id];
 
                end_if_stmt.next = last_end_if_stmt_id.upcast();
 

	
 
                last_if_stmt_id = if_stmt_id;
 
                last_end_if_stmt_id = end_if_stmt_id;
 
            }
 
        }
 

	
 
        // Final steps: set the statements of the replacement block statement,
 
        // and link all of those statements together
 
        let mut last_stmt_id = transformed_stmts[0];
 
        for stmt_id in transformed_stmts.iter().skip(1).copied() {
 
            set_ast_statement_next(ctx, last_stmt_id, stmt_id);
 
            last_stmt_id = stmt_id;
 
        }
 

	
 
        let outer_block_stmt = &mut ctx.heap[outer_block_id];
 
        outer_block_stmt.statements = transformed_stmts;
 

	
 
        return Ok(())
 
    }
 
}
 

	
 
impl PassRewriting {
 
    fn create_runtime_call_statement(&self, ctx: &mut Ctx, method: Method, arguments: Vec<ExpressionId>) -> (CallExpressionId, ExpressionStatementId) {
 
        let call_expr_id = ctx.heap.alloc_call_expression(|this| CallExpression{
 
            this,
 
            func_span: InputSpan::new(),
 
            full_span: InputSpan::new(),
 
            parser_type: ParserType{
 
                elements: Vec::new(),
 
                full_span: InputSpan::new(),
 
            },
 
            method,
 
            arguments,
 
            definition: DefinitionId::new_invalid(),
 
            parent: ExpressionParent::None,
 
            unique_id_in_definition: -1,
 
        });
 
        let call_stmt_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
            this,
 
            span: InputSpan::new(),
 
            expression: call_expr_id.upcast(),
 
            next: StatementId::new_invalid(),
 
        });
 

	
 
        let call_expr = &mut ctx.heap[call_expr_id];
 
        call_expr.parent = ExpressionParent::ExpressionStmt(call_stmt_id);
 

	
 
        return (call_expr_id, call_stmt_id);
 
    }
 

	
 
    fn create_runtime_select_wait_variable_and_statement(&self, ctx: &mut Ctx) -> (VariableId, MemoryStatementId) {
 
        let variable_id = create_ast_variable(ctx);
 
        let variable_expr_id = create_ast_variable_expr(ctx, variable_id);
 
        let runtime_call_expr_id = ctx.heap.alloc_call_expression(|this| CallExpression{
 
            this,
 
            func_span: InputSpan::new(),
 
            full_span: InputSpan::new(),
 
            parser_type: ParserType{
 
                elements: Vec::new(),
 
                full_span: InputSpan::new(),
 
            },
 
            method: Method::SelectWait,
 
            arguments: Vec::new(),
 
            definition: DefinitionId::new_invalid(),
 
            parent: ExpressionParent::None,
 
            unique_id_in_definition: -1
 
        });
 
        let initial_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
            this,
 
            operator_span: InputSpan::new(),
 
            full_span: InputSpan::new(),
 
            left: variable_expr_id.upcast(),
 
            operation: AssignmentOperator::Set,
 
            right: runtime_call_expr_id.upcast(),
 
            parent: ExpressionParent::None,
 
            unique_id_in_definition: -1
 
        });
 

	
 
        let variable_statement_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{
 
            this,
 
            span: InputSpan::new(),
 
            variable: variable_id,
 
            initial_expr: initial_expr_id,
 
            next: StatementId::new_invalid()
 
        });
 

	
 
        let variable_expr = &mut ctx.heap[variable_expr_id];
 
        variable_expr.parent = ExpressionParent::Expression(initial_expr_id.upcast(), 0);
 
        let runtime_call_expr = &mut ctx.heap[runtime_call_expr_id];
 
        runtime_call_expr.parent = ExpressionParent::Expression(initial_expr_id.upcast(), 1);
 
        let initial_expr = &mut ctx.heap[initial_expr_id];
 
        initial_expr.parent = ExpressionParent::Memory(variable_statement_id);
 

	
 
        return (variable_id, variable_statement_id);
 
    }
 
}
 

	
 
// -----------------------------------------------------------------------------
 
// Utilities to create compiler-generated AST nodes
 
// -----------------------------------------------------------------------------
 

	
 
fn create_ast_variable(ctx: &mut Ctx) -> VariableId {
 
    return ctx.heap.alloc_variable(|this| Variable{
 
fn create_ast_variable(ctx: &mut Ctx, scope_id: ScopeId) -> VariableId {
 
    let variable_id = ctx.heap.alloc_variable(|this| Variable{
 
        this,
 
        kind: VariableKind::Local,
 
        parser_type: ParserType{
 
            elements: Vec::new(),
 
            full_span: InputSpan::new(),
 
        },
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        relative_pos_in_parent: -1,
 
        unique_id_in_scope: -1,
 
    });
 
    let scope = &mut ctx.heap[scope_id];
 
    scope.variables.push(variable_id);
 

	
 
    return variable_id;
 
}
 

	
 
fn create_ast_variable_expr(ctx: &mut Ctx, variable_id: VariableId) -> VariableExpressionId {
 
    return ctx.heap.alloc_variable_expression(|this| VariableExpression{
 
        this,
 
        identifier: Identifier::new_empty(InputSpan::new()),
 
        declaration: Some(variable_id),
 
        used_as_binding_target: false,
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1
 
    });
 
}
 

	
 
fn create_ast_call_expr(ctx: &mut Ctx, method: Method, buffer: &mut ScopedBuffer<ExpressionId>, arguments: Vec<ExpressionId>) -> CallExpressionId {
 
    let expression_ids = buffer.start_section_initialized(&arguments);
 
    let call_expression_id = ctx.heap.alloc_call_expression(|this| CallExpression{
 
        this,
 
        func_span: InputSpan::new(),
 
        full_span: InputSpan::new(),
 
        parser_type: ParserType{
 
            elements: Vec::new(),
 
            full_span: InputSpan::new(),
 
        },
 
        method,
 
        arguments,
 
        definition: DefinitionId::new_invalid(),
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1,
 
    });
 

	
 
    for argument_index in 0..expression_ids.len() {
 
        let argument_id = expression_ids[argument_index];
 
        let argument_expr = &mut ctx.heap[argument_id];
 
        *argument_expr.parent_mut() = ExpressionParent::Expression(call_expression_id.upcast(), argument_index as u32);
 
    }
 

	
 
    return call_expression_id;
 
}
 

	
 
fn create_ast_literal_integer_expr(ctx: &mut Ctx, unsigned_value: u64) -> LiteralExpressionId {
 
    return ctx.heap.alloc_literal_expression(|this| LiteralExpression{
 
        this,
 
        span: InputSpan::new(),
 
        value: Literal::Integer(LiteralInteger{
 
            unsigned_value,
 
            negated: false,
 
        }),
 
        parent: ExpressionParent::None,
 
@@ -442,145 +410,217 @@ fn create_ast_equality_comparison_expr(ctx: &mut Ctx, variable_id: VariableId, v
 
fn create_ast_expression_stmt(ctx: &mut Ctx, expression_id: ExpressionId) -> ExpressionStatementId {
 
    let statement_id = ctx.heap.alloc_expression_statement(|this| ExpressionStatement{
 
        this,
 
        span: InputSpan::new(),
 
        expression: expression_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    let expression = &mut ctx.heap[expression_id];
 
    *expression.parent_mut() = ExpressionParent::ExpressionStmt(statement_id);
 

	
 
    return statement_id;
 
}
 

	
 
fn create_ast_variable_declaration_stmt(ctx: &mut Ctx, variable_id: VariableId, initial_value_expr_id: ExpressionId) -> MemoryStatementId {
 
    // Create the assignment expression, assigning the initial value to the variable
 
    let variable_expr_id = create_ast_variable_expr(ctx, variable_id);
 
    let assignment_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{
 
        this,
 
        operator_span: InputSpan::new(),
 
        full_span: InputSpan::new(),
 
        left: variable_expr_id.upcast(),
 
        operation: AssignmentOperator::Set,
 
        right: initial_value_expr_id,
 
        parent: ExpressionParent::None,
 
        unique_id_in_definition: -1,
 
    });
 

	
 
    // Create the memory statement
 
    let memory_stmt_id = ctx.heap.alloc_memory_statement(|this| MemoryStatement{
 
        this,
 
        span: InputSpan::new(),
 
        variable: variable_id,
 
        initial_expr: assignment_expr_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    // Set all parents which we can access
 
    let variable_expr = &mut ctx.heap[variable_expr_id];
 
    variable_expr.parent = ExpressionParent::Expression(assignment_expr_id.upcast(), 0);
 
    let value_expr = &mut ctx.heap[initial_value_expr_id];
 
    *value_expr.parent_mut() = ExpressionParent::Expression(assignment_expr_id.upcast(), 1);
 
    let assignment_expr = &mut ctx.heap[assignment_expr_id];
 
    assignment_expr.parent = ExpressionParent::Memory(memory_stmt_id);
 

	
 
    return memory_stmt_id;
 
}
 

	
 
fn create_ast_block_stmt(ctx: &mut Ctx, statements: Vec<StatementId>) -> (BlockStatementId, EndBlockStatementId, ScopeId) {
 
    let block_stmt_id = ctx.heap.alloc_block_statement(|this| BlockStatement{
 
        this,
 
        span: InputSpan::new(),
 
        statements,
 
        end_block: EndBlockStatementId::new_invalid(),
 
        scope: ScopeId::new_invalid(),
 
        next: StatementId::new_invalid(),
 
    });
 
    let end_block_stmt_id = ctx.heap.alloc_end_block_statement(|this| EndBlockStatement{
 
        this,
 
        start_block: block_stmt_id,
 
        next: StatementId::new_invalid(),
 
    });
 
    let scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::Block(block_stmt_id)));
 

	
 
    let block_stmt = &mut ctx.heap[block_stmt_id];
 
    block_stmt.end_block = end_block_stmt_id;
 
    block_stmt.scope = scope_id;
 

	
 
    return (block_stmt_id, end_block_stmt_id, scope_id);
 
}
 

	
 
fn create_ast_if_stmt(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_case: IfStatementCase, false_case: Option<IfStatementCase>) -> (IfStatementId, EndIfStatementId) {
 
    // Create if statement and the end-if statement
 
    let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{
 
        this,
 
        span: InputSpan::new(),
 
        test: condition_expression_id,
 
        true_case,
 
        false_case,
 
        end_if: EndIfStatementId::new_invalid()
 
    });
 

	
 
    let end_if_stmt_id = ctx.heap.alloc_end_if_statement(|this| EndIfStatement{
 
        this,
 
        start_if: if_stmt_id,
 
        next: StatementId::new_invalid(),
 
    });
 

	
 
    // Link the statements up as much as we can
 
    let if_stmt = &mut ctx.heap[if_stmt_id];
 
    if_stmt.end_if = end_if_stmt_id;
 

	
 
    let condition_expr = &mut ctx.heap[condition_expression_id];
 
    *condition_expr.parent_mut() = ExpressionParent::If(if_stmt_id);
 

	
 

	
 

	
 
    return (if_stmt_id, end_if_stmt_id);
 
}
 

	
 
fn set_ast_if_statement_false_body(ctx: &mut Ctx, if_statement_id: IfStatementId, end_if_statement_id: EndIfStatementId, false_body_id: StatementId) {
 
/// Sets the false body for a given
 
fn set_ast_if_statement_false_body(ctx: &mut Ctx, if_statement_id: IfStatementId, end_if_statement_id: EndIfStatementId, false_case: IfStatementCase) {
 
    // Point if-statement to "false body"
 
    todo!("set scopes");
 
    let if_stmt = &mut ctx.heap[if_statement_id];
 
    debug_assert!(if_stmt.false_case.is_none()); // simplifies logic, not necessary
 
    if_stmt.false_case = Some(IfStatementCase{
 
        body: false_body_id,
 
        scope: ScopeId::new_invalid(),
 
    });
 
    if_stmt.false_case = Some(false_case);
 

	
 
    // Point end of false body to the end of the if statement
 
    set_ast_statement_next(ctx, false_body_id, end_if_statement_id.upcast());
 
    set_ast_statement_next(ctx, false_case.body, end_if_statement_id.upcast());
 
}
 

	
 
/// Sets the specified AST statement's control flow such that it will be
 
/// followed by the target statement. This may seem obvious, but may imply that
 
/// a statement associated with, but different from, the source statement is
 
/// modified.
 
fn set_ast_statement_next(ctx: &mut Ctx, source_stmt_id: StatementId, target_stmt_id: StatementId) {
 
    let source_stmt = &mut ctx.heap[source_stmt_id];
 
    match source_stmt {
 
        Statement::Block(stmt) => {
 
            let end_id = stmt.end_block;
 
            ctx.heap[end_id].next = target_stmt_id
 
        },
 
        Statement::EndBlock(stmt) => stmt.next = target_stmt_id,
 
        Statement::Local(stmt) => {
 
            match stmt {
 
                LocalStatement::Memory(stmt) => stmt.next = target_stmt_id,
 
                LocalStatement::Channel(stmt) => stmt.next = target_stmt_id,
 
            }
 
        },
 
        Statement::Labeled(stmt) => {
 
            let body_id = stmt.body;
 
            set_ast_statement_next(ctx, body_id, target_stmt_id);
 
        },
 
        Statement::If(stmt) => {
 
            let end_id = stmt.end_if;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndIf(stmt) => stmt.next = target_stmt_id,
 
        Statement::While(stmt) => {
 
            let end_id = stmt.end_while;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndWhile(stmt) => stmt.next = target_stmt_id,
 

	
 
        Statement::Break(_stmt) => {},
 
        Statement::Continue(_stmt) => {},
 
        Statement::Synchronous(stmt) => {
 
            let end_id = stmt.end_sync;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndSynchronous(stmt) => {
 
            stmt.next = target_stmt_id;
 
        },
 
        Statement::Fork(_) | Statement::EndFork(_) => {
 
            todo!("remove fork from language");
 
        },
 
        Statement::Select(stmt) => {
 
            let end_id = stmt.end_select;
 
            ctx.heap[end_id].next = target_stmt_id;
 
        },
 
        Statement::EndSelect(stmt) => stmt.next = target_stmt_id,
 
        Statement::Return(_stmt) => {},
 
        Statement::Goto(_stmt) => {},
 
        Statement::New(stmt) => stmt.next = target_stmt_id,
 
        Statement::Expression(stmt) => stmt.next = target_stmt_id,
 
    }
 
}
 

	
 
/// Links a new scope to an existing scope as its child.
 
fn link_new_child_to_existing_parent_scope(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer<ScopeId>, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) {
 
    let child_scope = &mut ctx.heap[child_scope_id];
 
    debug_assert!(child_scope.parent.is_none());
 

	
 
    child_scope.parent = Some(parent_scope_id);
 
    child_scope.relative_pos_in_parent = relative_pos_hint;
 

	
 
    add_child_scope_to_parent(ctx, scope_buffer, parent_scope_id, child_scope_id, relative_pos_hint);
 
}
 

	
 
/// Relinks an existing scope to a new scope as its child. Will also break the
 
/// link of the child scope's old parent.
 
fn link_existing_child_to_new_parent_scope(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer<ScopeId>, new_parent_scope_id: ScopeId, child_scope_id: ScopeId, new_relative_pos_in_parent: i32) {
 
    let child_scope = &mut ctx.heap[child_scope_id];
 
    let old_parent_scope_id = child_scope.parent.unwrap();
 
    child_scope.parent = Some(new_parent_scope_id);
 
    child_scope.relative_pos_in_parent = new_relative_pos_in_parent;
 

	
 
    // Remove from old parent
 
    let old_parent = &mut ctx.heap[old_parent_scope_id];
 
    let scope_index = old_parent.nested.iter()
 
        .position(|v| *v == child_scope_id)
 
        .unwrap();
 
    old_parent.nested.remove(scope_index);
 

	
 
    // Add to new parent
 
    add_child_scope_to_parent(ctx, scope_buffer, new_parent_scope_id, child_scope_id, new_relative_pos_in_parent);
 
}
 

	
 
/// Will add a child scope to a parent scope using the relative position hint.
 
fn add_child_scope_to_parent(ctx: &mut Ctx, scope_buffer: &mut ScopedBuffer<ScopeId>, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) {
 
    let child_scope = &ctx.heap[child_scope_id];
 

	
 
    let existing_scope_ids = scope_buffer.start_section_initialized(&child_scope.nested);
 
    let mut insert_pos = existing_scope_ids.len();
 
    for index in 0..existing_scope_ids.len() {
 
        let existing_scope_id = existing_scope_ids[index];
 
        let existing_scope = &ctx.heap[existing_scope_id];
 
        if relative_pos_hint <= existing_scope.relative_pos_in_parent {
 
            insert_pos = index;
 
            break;
 
        }
 
    }
 
    existing_scope_ids.forget();
 

	
 
    let parent_scope = &mut ctx.heap[parent_scope_id];
 
    parent_scope.nested.insert(insert_pos, child_scope_id);
 
}
src/protocol/parser/pass_validation_linking.rs
Show inline comments
 
@@ -270,99 +270,96 @@ impl Visitor for PassValidationLinking {
 

	
 
        // Visit statements in function body
 
        self.visit_block_stmt(ctx, body_id)?;
 
        self.pop_scope(old_scope);
 

	
 
        // Assign total number of expressions and assign an in-block unique ID
 
        // to each of the locals in the procedure.
 
        let definition = &mut ctx.heap[id];
 
        let definition_scope = definition.scope;
 
        definition.num_expressions_in_body = self.next_expr_index;
 
        self.visit_scope_and_assign_local_ids(ctx, definition_scope, 0);
 
        self.resolve_pending_control_flow_targets(ctx)?;
 

	
 
        Ok(())
 
    }
 

	
 
    //--------------------------------------------------------------------------
 
    // Statement visitors
 
    //--------------------------------------------------------------------------
 

	
 
    fn visit_block_stmt(&mut self, ctx: &mut Ctx, id: BlockStatementId) -> VisitorResult {
 
        // Get end of block
 
        let block_stmt = &ctx.heap[id];
 
        let end_block_id = block_stmt.end_block;
 
        let scope_id = block_stmt.scope;
 

	
 
        // Traverse statements in block
 
        let statement_section = self.statement_buffer.start_section_initialized(&block_stmt.statements);
 
        let old_scope = self.push_scope(ctx, false, scope_id);
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast());
 

	
 
        for stmt_idx in 0..statement_section.len() {
 
            self.relative_pos_in_parent = stmt_idx as i32;
 
            self.visit_stmt(ctx, statement_section[stmt_idx])?;
 
        }
 

	
 
        statement_section.forget();
 
        assign_and_replace_next_stmt!(self, ctx, end_block_id.upcast());
 

	
 
        self.pop_scope(old_scope);
 
        Ok(())
 
    }
 

	
 
    fn visit_local_memory_stmt(&mut self, ctx: &mut Ctx, id: MemoryStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let expr_id = stmt.initial_expr;
 
        let variable_id = stmt.variable;
 

	
 
        if ctx.heap[variable_id].identifier.value.as_str() == "counter" {
 
            let a = 1;
 
        }
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_parent, variable_id)?;
 

	
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast().upcast());
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
        self.expr_parent = ExpressionParent::Memory(id);
 
        self.visit_assignment_expr(ctx, expr_id)?;
 
        self.expr_parent = ExpressionParent::None;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_local_channel_stmt(&mut self, ctx: &mut Ctx, id: ChannelStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let from_id = stmt.from;
 
        let to_id = stmt.to;
 

	
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_parent, from_id)?;
 
        self.checked_add_local(ctx, self.cur_scope, self.relative_pos_in_parent, to_id)?;
 

	
 
        assign_and_replace_next_stmt!(self, ctx, id.upcast().upcast());
 
        Ok(())
 
    }
 

	
 
    fn visit_labeled_stmt(&mut self, ctx: &mut Ctx, id: LabeledStatementId) -> VisitorResult {
 
        let stmt = &ctx.heap[id];
 
        let body_id = stmt.body;
 

	
 
        self.checked_add_label(ctx, self.relative_pos_in_parent, self.in_sync, id)?;
 

	
 
        self.visit_stmt(ctx, body_id)?;
 
        Ok(())
 
    }
 

	
 
    fn visit_if_stmt(&mut self, ctx: &mut Ctx, id: IfStatementId) -> VisitorResult {
 
        let if_stmt = &ctx.heap[id];
 
        let end_if_id = if_stmt.end_if;
 
        let test_expr_id = if_stmt.test;
 
        let true_case = if_stmt.true_case;
 
        let false_case = if_stmt.false_case;
 

	
 
        // Visit test expression
 
        debug_assert_eq!(self.expr_parent, ExpressionParent::None);
 
        debug_assert!(self.in_test_expr.is_invalid());
 

	
 
        self.in_test_expr = id.upcast();
 
        self.expr_parent = ExpressionParent::If(id);
 
        self.visit_expr(ctx, test_expr_id)?;
 
        self.in_test_expr = StatementId::new_invalid();
 
@@ -478,96 +475,100 @@ impl Visitor for PassValidationLinking {
 

	
 
        // Visit block statement. Note that we explicitly push the scope here
 
        // (and the `visit_block_stmt` will also push, but without effect) to
 
        // ensure the scope contains the sync ID.
 
        let sync_body = ctx.heap[id].body;
 
        debug_assert!(self.in_sync.is_invalid());
 
        self.in_sync = id;
 
        let old_scope = self.push_scope(ctx, false, scope_id);
 
        self.visit_stmt(ctx, sync_body)?;
 
        self.pop_scope(old_scope);
 
        assign_and_replace_next_stmt!(self, ctx, end_sync_id.upcast());
 

	
 
        self.in_sync = SynchronousStatementId::new_invalid();
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_fork_stmt(&mut self, ctx: &mut Ctx, id: ForkStatementId) -> VisitorResult {
 
        let fork_stmt = &ctx.heap[id];
 
        let end_fork_id = fork_stmt.end_fork;
 
        let left_body_id = fork_stmt.left_body;
 
        let right_body_id = fork_stmt.right_body;
 

	
 
        // Fork statements may only occur inside sync blocks
 
        if self.in_sync.is_invalid() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, fork_stmt.span,
 
                "Forking may only occur inside sync blocks"
 
            ));
 
        }
 

	
 
        // Visit the respective bodies. Like the if statement, a fork statement
 
        // does not have a single static subsequent statement. It forks and then
 
        // each fork has a different next statement.
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 
        self.visit_stmt(ctx, left_body_id)?;
 
        assign_then_erase_next_stmt!(self, ctx, end_fork_id.upcast());
 

	
 
        if let Some(right_body_id) = right_body_id {
 
            self.visit_stmt(ctx, right_body_id)?;
 
            assign_then_erase_next_stmt!(self, ctx, end_fork_id.upcast());
 
        }
 

	
 
        self.prev_stmt = end_fork_id.upcast();
 
        Ok(())
 
    }
 

	
 
    fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult {
 
        let select_stmt = &mut ctx.heap[id];
 
        select_stmt.relative_pos_in_parent = self.relative_pos_in_parent;
 
        self.relative_pos_in_parent += 1;
 

	
 
        let select_stmt = &ctx.heap[id];
 
        let end_select_id = select_stmt.end_select;
 

	
 
        // Select statements may only occur inside sync blocks
 
        if self.in_sync.is_invalid() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, select_stmt.span,
 
                "select statements may only occur inside sync blocks"
 
            ));
 
        }
 

	
 
        if !self.def_type.is_primitive() {
 
            return Err(ParseError::new_error_str_at_span(
 
                &ctx.module().source, select_stmt.span,
 
                "select statements may only be used in primitive components"
 
            ));
 
        }
 

	
 
        // Visit the various arms in the select block
 
        let mut case_stmt_ids = self.statement_buffer.start_section();
 
        let mut case_scope_ids = self.scope_buffer.start_section();
 
        let num_cases = select_stmt.cases.len();
 
        for case in &select_stmt.cases {
 
            // We add them in pairs, so the subsequent for-loop retrieves in pairs
 
            case_stmt_ids.push(case.guard);
 
            case_stmt_ids.push(case.body);
 
            case_scope_ids.push(case.scope);
 
        }
 

	
 
        assign_then_erase_next_stmt!(self, ctx, id.upcast());
 

	
 
        for index in 0..num_cases {
 
            let base_index = 2 * index;
 
            let guard_id     = case_stmt_ids[base_index];
 
            let case_body_id = case_stmt_ids[base_index + 1];
 
            let case_scope_id = case_scope_ids[index];
 

	
 
            // The guard statement ends up belonging to the block statement
 
            // following the arm. The reason we parse it separately is to
 
            // extract all of the "get" calls.
 
            let old_scope = self.push_scope(ctx, false, case_scope_id);
 

	
 
            // Visit the guard of this arm
 
            debug_assert!(self.in_select_guard.is_invalid());
 
            self.in_select_guard = id;
 
            self.in_select_arm = index as u32;
 
            self.visit_stmt(ctx, guard_id)?;
 
            self.in_select_guard = SelectStatementId::new_invalid();
 
@@ -1310,100 +1311,96 @@ impl Visitor for PassValidationLinking {
 
            DefinedTypeVariant::Function(definition) => definition.arguments.len(),
 
            DefinedTypeVariant::Component(definition) => definition.arguments.len(),
 
            v => unreachable!("encountered {} type in call expression", v.type_class()),
 
        };
 

	
 
        let num_provided_args = call_expr.arguments.len();
 
        if num_provided_args != num_expected_args {
 
            let argument_text = if num_expected_args == 1 { "argument" } else { "arguments" };
 
            let call_span = call_expr.full_span;
 
            return Err(ParseError::new_error_at_span(
 
                &ctx.module().source, call_span, format!(
 
                    "expected {} {}, but {} were provided",
 
                    num_expected_args, argument_text, num_provided_args
 
                )
 
            ));
 
        }
 

	
 
        // Recurse into all of the arguments and set the expression's parent
 
        let upcast_id = id.upcast();
 

	
 
        let section = self.expression_buffer.start_section_initialized(&call_expr.arguments);
 
        let old_expr_parent = self.expr_parent;
 
        call_expr.parent = old_expr_parent;
 
        call_expr.unique_id_in_definition = self.next_expr_index;
 
        self.next_expr_index += 1;
 

	
 
        for arg_expr_idx in 0..section.len() {
 
            let arg_expr_id = section[arg_expr_idx];
 
            self.expr_parent = ExpressionParent::Expression(upcast_id, arg_expr_idx as u32);
 
            self.visit_expr(ctx, arg_expr_id)?;
 
        }
 

	
 
        section.forget();
 
        self.expr_parent = old_expr_parent;
 

	
 
        Ok(())
 
    }
 

	
 
    fn visit_variable_expr(&mut self, ctx: &mut Ctx, id: VariableExpressionId) -> VisitorResult {
 
        let var_expr = &ctx.heap[id];
 

	
 
        // Check if declaration was already resolved (this occurs for the
 
        // variable expr that is on the LHS of the assignment expr that is
 
        // associated with a variable declaration)
 
        let mut variable_id = var_expr.declaration;
 
        let mut is_binding_target = false;
 

	
 
        // Otherwise try to find it
 
        if var_expr.identifier.value.as_str() == "new_value" {
 
            let a = 0; // TODO: REMOVE!
 
        }
 

	
 
        if variable_id.is_none() {
 
            variable_id = self.find_variable(ctx, self.relative_pos_in_parent, &var_expr.identifier);
 
        }
 

	
 
        // Otherwise try to see if is a variable introduced by a binding expr
 
        let variable_id = if let Some(variable_id) = variable_id {
 
            variable_id
 
        } else {
 
            if self.in_binding_expr.is_invalid() || !self.in_binding_expr_lhs {
 
                return Err(ParseError::new_error_str_at_span(
 
                    &ctx.module().source, var_expr.identifier.span, "unresolved variable"
 
                ));
 
            }
 

	
 
            // This is a binding variable, but it may only appear in very
 
            // specific locations.
 
            let is_valid_binding = match self.expr_parent {
 
                ExpressionParent::Expression(expr_id, idx) => {
 
                    match &ctx.heap[expr_id] {
 
                        Expression::Binding(_binding_expr) => {
 
                            // Nested binding is disallowed, and because of
 
                            // the check above we know we're directly at the
 
                            // LHS of the binding expression
 
                            debug_assert_eq!(_binding_expr.this, self.in_binding_expr);
 
                            debug_assert_eq!(idx, 0);
 
                            true
 
                        }
 
                        Expression::Literal(lit_expr) => {
 
                            // Only struct, unions, tuples and arrays can
 
                            // have subexpressions, so we're always fine
 
                            if cfg!(debug_assertions) {
 
                                match lit_expr.value {
 
                                    Literal::Struct(_) | Literal::Union(_) | Literal::Array(_) | Literal::Tuple(_) => {},
 
                                    _ => unreachable!(),
 
                                }
 
                            }
 

	
 
                            true
 
                        },
 
                        _ => false,
 
                    }
 
                },
 
                _ => {
 
                    false
 
                }
 
            };
 

	
 
            if !is_valid_binding {
0 comments (0 inline, 0 general)