diff --git a/src/protocol/parser/pass_rewriting.rs b/src/protocol/parser/pass_rewriting.rs new file mode 100644 index 0000000000000000000000000000000000000000..ccbfca20870bad54d815d7c84983fd2c025bb478 --- /dev/null +++ b/src/protocol/parser/pass_rewriting.rs @@ -0,0 +1,683 @@ +use crate::collections::*; +use crate::protocol::*; + +use super::visitor::*; + +pub(crate) struct PassRewriting { + current_scope: ScopeId, + current_procedure_id: ProcedureDefinitionId, + definition_buffer: ScopedBuffer, + statement_buffer: ScopedBuffer, + call_expr_buffer: ScopedBuffer, + expression_buffer: ScopedBuffer, + scope_buffer: ScopedBuffer, +} + +impl PassRewriting { + pub(crate) fn new() -> Self { + Self{ + current_scope: ScopeId::new_invalid(), + current_procedure_id: ProcedureDefinitionId::new_invalid(), + definition_buffer: ScopedBuffer::with_capacity(BUFFER_INIT_CAP_LARGE), + 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 { + fn visit_module(&mut self, ctx: &mut Ctx) -> VisitorResult { + let module = ctx.module(); + debug_assert_eq!(module.phase, ModuleCompilationPhase::Typed); + + let root_id = module.root_id; + let root = &ctx.heap[root_id]; + let definition_section = self.definition_buffer.start_section_initialized(&root.definitions); + for definition_index in 0..definition_section.len() { + let definition_id = definition_section[definition_index]; + self.visit_definition(ctx, definition_id)?; + } + + definition_section.forget(); + ctx.module_mut().phase = ModuleCompilationPhase::Rewritten; + return Ok(()) + } + + // --- Visiting procedures + + fn visit_procedure_definition(&mut self, ctx: &mut Ctx, id: ProcedureDefinitionId) -> VisitorResult { + let definition = &ctx.heap[id]; + let body_id = definition.body; + self.current_scope = definition.scope; + self.current_procedure_id = id; + 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 = 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_case = if_stmt.true_case; + let false_case = if_stmt.false_case; + + 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, containing_procedure_id: ProcedureDefinitionId, + select_id: SelectStatementId, case_index: usize, + select_var_id: VariableId, select_var_type_id: TypeIdReference + ) -> (IfStatementId, EndIfStatementId, ScopeId) { + // 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, containing_procedure_id, select_var_id, select_var_type_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, case_scope_id); + } + + // 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_new_child_to_existing_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]; + + // 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]; + let port_type_index = ctx.heap[port_expr_id].type_index(); + let port_type_ref = TypeIdReference::IndirectSameAsExpr(port_type_index); + + // Move the port expression such that it gets assigned to a temporary variable + let variable_id = create_ast_variable(ctx, outer_scope_id); + let variable_decl_stmt_id = create_ast_variable_declaration_stmt(ctx, self.current_procedure_id, variable_id, port_type_ref, 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, self.current_procedure_id, variable_id, port_type_ref); + 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, port_type_ref)); + } + + // 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, self.current_procedure_id, total_num_cases as u64, ctx.arch.uint32_type_id); + let num_ports_expression_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, total_num_ports as u64, ctx.arch.uint32_type_id); + let arguments = vec![ + num_cases_expression_id.upcast(), + num_ports_expression_id.upcast() + ]; + + let call_expression_id = create_ast_call_expr(ctx, self.current_procedure_id, 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, port_variable_type) = 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, self.current_procedure_id, case_index as u64, ctx.arch.uint32_type_id); + let port_index_expr_id = create_ast_literal_integer_expr(ctx, self.current_procedure_id, case_port_index as u64, ctx.arch.uint32_type_id); + let port_variable_expr_id = create_ast_variable_expr(ctx, self.current_procedure_id, port_variable_id, port_variable_type); + 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, self.current_procedure_id, 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, outer_scope_id); + let select_variable_type = TypeIdReference::DirectTypeId(ctx.arch.uint32_type_id); + locals.push((select_variable_id, select_variable_type)); + + { + let runtime_call_expr_id = create_ast_call_expr(ctx, self.current_procedure_id, Method::SelectWait, &mut self.expression_buffer, Vec::new()); + let variable_stmt_id = create_ast_variable_declaration_stmt(ctx, self.current_procedure_id, select_variable_id, select_variable_type, runtime_call_expr_id.upcast()); + transformed_stmts.push(variable_stmt_id.upcast().upcast()); + } + + call_id_section.forget(); + expr_id_section.forget(); + + // Now we transform each of the select block case's guard and code into + // a chained if-else statement. + let mut relative_pos = transformed_stmts.len() as i32; + if total_num_cases > 0 { + let (if_stmt_id, end_if_stmt_id, scope_id) = transform_select_case_code(ctx, self.current_procedure_id, id, 0, select_variable_id, select_variable_type); + link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, outer_scope_id, scope_id, relative_pos); + 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; + let mut last_parent_scope_id = outer_scope_id; + let mut last_relative_pos = transformed_stmts.len() as i32 + 1; + transformed_stmts.push(last_if_stmt_id.upcast()); + + for case_index in 1..total_num_cases { + let (if_stmt_id, end_if_stmt_id, scope_id) = transform_select_case_code(ctx, self.current_procedure_id, id, case_index, select_variable_id, select_variable_type); + let false_case_scope_id = ctx.heap.alloc_scope(|this| Scope::new(this, ScopeAssociation::If(last_if_stmt_id, false))); + link_existing_child_to_new_parent_scope(ctx, &mut self.scope_buffer, false_case_scope_id, scope_id, 0); + link_new_child_to_existing_parent_scope(ctx, &mut self.scope_buffer, last_parent_scope_id, false_case_scope_id, last_relative_pos); + 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; + last_parent_scope_id = false_case_scope_id; + last_relative_pos = 0; + } + } + + // Final steps: set the statements of the replacement block statement, + // link all of those statements together, and update the scopes. + let first_stmt_id = transformed_stmts[0]; + 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; + } + + if total_num_cases == 0 { + // If we don't have any cases, then we didn't connect the statements + // up to the end of the outer block, so do that here + set_ast_statement_next(ctx, last_stmt_id, outer_end_block_id.upcast()); + } + + let outer_block_stmt = &mut ctx.heap[outer_block_id]; + outer_block_stmt.next = first_stmt_id; + outer_block_stmt.statements = transformed_stmts; + + return Ok(()) + } +} + +// ----------------------------------------------------------------------------- +// Utilities to create compiler-generated AST nodes +// ----------------------------------------------------------------------------- + +#[derive(Clone, Copy)] +enum TypeIdReference { + DirectTypeId(TypeId), + IndirectSameAsExpr(i32), // by type index +} + +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, containing_procedure_id: ProcedureDefinitionId, variable_id: VariableId, variable_type_id: TypeIdReference) -> VariableExpressionId { + let variable_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, variable_type_id); + 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, + type_index: variable_type_index, + }); +} + +fn create_ast_call_expr(ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, method: Method, buffer: &mut ScopedBuffer, arguments: Vec) -> CallExpressionId { + let call_type_id = match method { + Method::SelectStart => ctx.arch.void_type_id, + Method::SelectRegisterCasePort => ctx.arch.void_type_id, + Method::SelectWait => ctx.arch.uint32_type_id, // TODO: Not pretty, this. Pretty error prone + _ => unreachable!(), // if this goes of, add the appropriate method here. + }; + + let expression_ids = buffer.start_section_initialized(&arguments); + let call_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(call_type_id)); + let call_expression_id = ctx.heap.alloc_call_expression(|this| CallExpression{ + func_span: InputSpan::new(), + this, + full_span: InputSpan::new(), + parser_type: ParserType{ + elements: Vec::new(), + full_span: InputSpan::new(), + }, + method, + arguments, + procedure: ProcedureDefinitionId::new_invalid(), + parent: ExpressionParent::None, + type_index: call_type_index, + }); + + 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, containing_procedure_id: ProcedureDefinitionId, unsigned_value: u64, type_id: TypeId) -> LiteralExpressionId { + let literal_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(type_id)); + return ctx.heap.alloc_literal_expression(|this| LiteralExpression{ + this, + span: InputSpan::new(), + value: Literal::Integer(LiteralInteger{ + unsigned_value, + negated: false, + }), + parent: ExpressionParent::None, + type_index: literal_type_index, + }); +} + +fn create_ast_equality_comparison_expr( + ctx: &mut Ctx, containing_procedure_id: ProcedureDefinitionId, + variable_id: VariableId, variable_type: TypeIdReference, value: u64 +) -> BinaryExpressionId { + let var_expr_id = create_ast_variable_expr(ctx, containing_procedure_id, variable_id, variable_type); + let int_expr_id = create_ast_literal_integer_expr(ctx, containing_procedure_id, value, ctx.arch.uint32_type_id); + let cmp_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(ctx.arch.bool_type_id)); + let cmp_expr_id = ctx.heap.alloc_binary_expression(|this| BinaryExpression{ + this, + operator_span: InputSpan::new(), + full_span: InputSpan::new(), + left: var_expr_id.upcast(), + operation: BinaryOperator::Equality, + right: int_expr_id.upcast(), + parent: ExpressionParent::None, + type_index: cmp_type_index, + }); + + let var_expr = &mut ctx.heap[var_expr_id]; + var_expr.parent = ExpressionParent::Expression(cmp_expr_id.upcast(), 0); + let int_expr = &mut ctx.heap[int_expr_id]; + int_expr.parent = ExpressionParent::Expression(cmp_expr_id.upcast(), 1); + + return cmp_expr_id; +} + +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, containing_procedure_id: ProcedureDefinitionId, + variable_id: VariableId, variable_type: TypeIdReference, 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, containing_procedure_id, variable_id, variable_type); + let void_type_index = add_new_procedure_expression_type(ctx, containing_procedure_id, TypeIdReference::DirectTypeId(ctx.arch.void_type_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, + type_index: void_type_index, + }); + + // 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) -> (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) -> (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); +} + +/// 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" + 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(false_case); + + // Point end of false body to the end of the if statement + 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, 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, 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, parent_scope_id: ScopeId, child_scope_id: ScopeId, relative_pos_hint: i32) { + let parent_scope = &ctx.heap[parent_scope_id]; + + let existing_scope_ids = scope_buffer.start_section_initialized(&parent_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); +} + +fn add_new_procedure_expression_type(ctx: &mut Ctx, procedure_id: ProcedureDefinitionId, type_id: TypeIdReference) -> i32 { + let procedure = &mut ctx.heap[procedure_id]; + let type_index = procedure.monomorphs[0].expr_info.len(); + + match type_id { + TypeIdReference::DirectTypeId(type_id) => { + for monomorph in procedure.monomorphs.iter_mut() { + debug_assert_eq!(monomorph.expr_info.len(), type_index); + monomorph.expr_info.push(ExpressionInfo{ + type_id, + variant: ExpressionInfoVariant::Generic + }); + } + }, + TypeIdReference::IndirectSameAsExpr(source_type_index) => { + for monomorph in procedure.monomorphs.iter_mut() { + debug_assert_eq!(monomorph.expr_info.len(), type_index); + let copied_expr_info = monomorph.expr_info[source_type_index as usize]; + monomorph.expr_info.push(copied_expr_info) + } + } + } + + return type_index as i32; +} \ No newline at end of file