diff --git a/src/protocol/parser/pass_rewriting.rs b/src/protocol/parser/pass_rewriting.rs index 6ac59652dae8dac2a38cb1390559e3302017ea57..517de373802e3d79c4936c3afa27b312898e606b 100644 --- a/src/protocol/parser/pass_rewriting.rs +++ b/src/protocol/parser/pass_rewriting.rs @@ -1,14 +1,3 @@ -// TODO: File contains a lot of manual AST element construction. Wherein we have -// (for the first time in this compiler) a lot of fields that have no real -// meaning (e.g. the InputSpan of a AST-transformation). What are we going to -// do with this to make the code and datastructures more easily grokable? -// We could do an intermediate AST structure. But considering how close this -// phase of compilation is to bytecode generation, that might be a lot of busy- -// work with few results. Alternatively we may put the AST elements inside -// a special substructure. We could also force ourselves (and put the -// appropriate comments in the code) to not use certain fields anymore after -// a particular stage of compilation. - use crate::collections::*; use crate::protocol::*; @@ -96,12 +85,38 @@ impl Visitor for PassRewriting { // --- Visiting the select statement fn visit_select_stmt(&mut self, ctx: &mut Ctx, id: SelectStatementId) -> VisitorResult { + // Utility for the last stage of rewriting process + 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.block; + + // 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 (if_stmt_id, end_if_stmt_id) = create_ast_if_stmt(ctx, compare_expr_id.upcast(), case_body_id, None); + + // Modify body of case to link up to the surrounding statements + // correctly + let case_body = &mut ctx.heap[case_body_id]; + let case_end_body_id = case_body.end_block; + case_body.statements.insert(0, case_guard_id); + + let case_end_body = &mut ctx.heap[case_end_body_id]; + case_end_body.next = 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. let select_stmt = &ctx.heap[id]; let mut 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(); @@ -178,7 +193,7 @@ impl Visitor for PassRewriting { // Create runtime call, then store it let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectRegisterCasePort, runtime_call_arguments); - let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id); + let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id.upcast()); transformed_stmts.push(runtime_call_stmt_id.upcast()); @@ -201,11 +216,46 @@ impl Visitor for PassRewriting { 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_node: ScopeNode::new_invalid(), + first_unique_id_in_scope: -1, + next_unique_id_in_scope: -1, + locals: Vec::new(), + labels: Vec::new(), + 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 cmp_expr_id = create_ast_equality_comparison_expr(ctx, select_variable_id, 0); - let mut (if_stmt_id, end_if_stmt_id) = create_ast_if_statement(ctx, cmp_expr_id.upcast(), ) + let (if_stmt_id, end_if_stmt_id) = transform_select_case_code(ctx, id, 0, select_variable_id); + 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_body = 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{ @@ -438,7 +488,7 @@ fn create_ast_variable_declaration_stmt(ctx: &mut Ctx, variable_id: VariableId, return memory_stmt_id; } -fn create_ast_if_statement(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_body: BlockStatementId, false_body: Option) -> (IfStatementId, EndIfStatementId) { +fn create_ast_if_stmt(ctx: &mut Ctx, condition_expression_id: ExpressionId, true_body: BlockStatementId, false_body: Option) -> (IfStatementId, EndIfStatementId) { // Create if statement and the end-if statement let if_stmt_id = ctx.heap.alloc_if_statement(|this| IfStatement{ this,