diff --git a/src/protocol/parser/pass_rewriting.rs b/src/protocol/parser/pass_rewriting.rs index d678d2cd3a2fb12c52158cd6ef94c8d899cc387d..6ac59652dae8dac2a38cb1390559e3302017ea57 100644 --- a/src/protocol/parser/pass_rewriting.rs +++ b/src/protocol/parser/pass_rewriting.rs @@ -121,24 +121,93 @@ impl Visitor for PassRewriting { let mut locals = Vec::with_capacity(total_num_ports); for port_var_idx in 0..call_id_section.len() { - let call_id = call_id_section[port_var_idx]; - let expr_id = expr_id_section[port_var_idx]; - let (replacement_variable_id, variable_stmt_id) = self.modify_get_call_insert_variable(ctx, call_id, expr_id); - transformed_stmts.push(variable_stmt_id.upcast().upcast()); - locals.push(replacement_variable_id); + 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_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); } - // Our transformed statements now contain all of the temporary port - // calculations. We'll now insert the appropriate runtime calls to - // notify the runtime that we're going to wait on these ports. - let (_, select_start_stmt_id) = self.create_runtime_select_start_call_statement(ctx, total_num_cases, total_num_ports); - transformed_stmts.push(select_start_stmt_id.upcast()); + // Insert runtime calls that facilitate the semantics of the select + // block. - // TODO: Call the runtime function for eeach of the substituted port variables to register all ports for the select statement + // 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, 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, runtime_call_arguments); + let runtime_call_stmt_id = create_ast_expression_stmt(ctx, runtime_call_expr_id); + + 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); + locals.push(select_variable_id); + + { + let runtime_call_expr_id = create_ast_call_expr(ctx, Method::SelectWait, 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(); + // 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 block = ctx.heap.alloc_block_statement(|this| BlockStatement{ // this, // is_implicit: true, @@ -158,83 +227,6 @@ impl Visitor for PassRewriting { } impl PassRewriting { - fn modify_get_call_insert_variable(&self, ctx: &mut Ctx, call_expr_id: CallExpressionId, port_expr_id: ExpressionId) -> (VariableId, MemoryStatementId) { - // Retrieve original expression which we're going to transplant into - // its own variable - let port_expr = &ctx.heap[port_expr_id]; - let port_expr_span = port_expr.full_span(); - let port_expr_unique_id = port_expr.get_unique_id_in_definition(); - - // Create the entries in the heap - let variable_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{ - this, - identifier: Identifier::new_empty(port_expr_span), - declaration: None, - used_as_binding_target: false, - parent: ExpressionParent::None, - unique_id_in_definition: port_expr_unique_id, - }); - let initial_expr_id = ctx.heap.alloc_assignment_expression(|this| AssignmentExpression{ - this, - operator_span: port_expr_span, - full_span: port_expr_span, - left: variable_expr_id.upcast(), - operation: AssignmentOperator::Set, - right: port_expr_id, - parent: ExpressionParent::None, - unique_id_in_definition: -1, - }); - let variable_id = ctx.heap.alloc_variable(|this| Variable{ - this, - kind: VariableKind::Local, - parser_type: ParserType{ elements: vec![ParserTypeElement{ - element_span: port_expr_span, - variant: ParserTypeVariant::Inferred, - }], - full_span: port_expr_span - }, - identifier: Identifier::new_empty(port_expr_span), - relative_pos_in_block: -1, - unique_id_in_scope: -1, - }); - let variable_decl_stmt = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ - this, - span: port_expr_span, - variable: variable_id, - initial_expr: initial_expr_id, - next: StatementId::new_invalid(), - }); - - // Modify all entries that required access other heap entries - let variable_expr = &mut ctx.heap[variable_expr_id]; - variable_expr.declaration = Some(variable_id); - variable_expr.parent = ExpressionParent::Expression(initial_expr_id.upcast(), 1); - - let initial_expr = &mut ctx.heap[initial_expr_id]; - initial_expr.parent = ExpressionParent::Memory(variable_decl_stmt); - - // Modify the parent of the expression that we just transplanted - let port_expr = &mut ctx.heap[port_expr_id]; - *port_expr.parent_mut() = ExpressionParent::Expression(initial_expr_id.upcast(), 1); - - // Modify the call expression (that should contain the port expression - // as the first argument) to point to the new variable - let call_arg_expr_id = ctx.heap.alloc_variable_expression(|this| VariableExpression{ - this, - identifier: Identifier::new_empty(port_expr_span), - declaration: Some(variable_id), - used_as_binding_target: false, - parent: ExpressionParent::Expression(call_expr_id.upcast(), 0), - unique_id_in_definition: port_expr_unique_id, - }); - let call_expr = &mut ctx.heap[call_expr_id]; - debug_assert_eq!(call_expr.method, Method::Get); - debug_assert!(call_expr.arguments.len() == 1 && call_expr.arguments[0] == port_expr_id); - call_expr.arguments[0] = call_arg_expr_id.upcast(); - - return (variable_id, variable_decl_stmt); - } - fn create_runtime_call_statement(&self, ctx: &mut Ctx, method: Method, arguments: Vec) -> (CallExpressionId, ExpressionStatementId) { let call_expr_id = ctx.heap.alloc_call_expression(|this| CallExpression{ this, @@ -263,64 +255,9 @@ impl PassRewriting { return (call_expr_id, call_stmt_id); } - fn create_runtime_select_start_call_statement(&self, ctx: &mut Ctx, num_cases: usize, num_ports_total: usize) -> (CallExpressionId, ExpressionStatementId) { - let num_cases_expr_id = self.create_literal_integer(ctx, num_cases as u64); - let num_ports_expr_id = self.create_literal_integer(ctx, num_ports_total as u64); - let arguments = vec![ - num_cases_expr_id.upcast(), - num_ports_expr_id.upcast() - ]; - - let (call_expr_id, call_stmt_id) = self.create_runtime_call_statement(ctx, Method::SelectStart, arguments); - - let num_cases_expr = &mut ctx.heap[num_cases_expr_id]; - num_cases_expr.parent = ExpressionParent::Expression(call_expr_id.upcast(), 0); - let num_ports_expr = &mut ctx.heap[num_ports_expr_id]; - num_ports_expr.parent = ExpressionParent::Expression(num_ports_expr_id.upcast(), 1); - - return (call_expr_id, call_stmt_id); - } - - fn create_runtime_select_register_port_call_statement(&self, ctx: &mut Ctx, case_index: usize, port_index: usize, original_port_expr_id: ExpressionId, port_variable_id: VariableId) -> (CallExpressionId, ExpressionStatementId) { - let original_port_expr = &ctx.heap[original_port_expr_id]; - let original_port_span = original_port_expr.full_span(); - let original_port_unique_id = original_port_expr.get_unique_id_in_definition(); - - let case_index_expr_id = self.create_literal_integer(ctx, case_index as u64); - let port_index_expr_id = self.create_literal_integer(ctx, port_index as u64); - let port_var_expr_id = self.create_variable_expr(ctx, port_variable_id); - - let arguments = vec![ - case_index_expr_id.upcast(), - port_index_expr_id.upcast(), - port_var_expr_id.upcast() - ]; - - let (call_expr_id, call_stmt_id) = self.create_runtime_call_statement(ctx, Method::SelectRegisterCasePort, arguments); - - let case_index_expr = &mut ctx.heap[case_index_expr_id]; - case_index_expr.parent = ExpressionParent::Expression(call_expr_id.upcast(), 0); - let port_index_expr = &mut ctx.heap[port_index_expr_id]; - port_index_expr.parent = ExpressionParent::Expression(call_expr_id.upcast(), 1); - let port_var_expr = &mut ctx.heap[port_var_expr_id]; - port_var_expr.parent = ExpressionParent::Expression(call_expr_id.upcast(), 2); - - return (call_expr_id, call_stmt_id); - } - fn create_runtime_select_wait_variable_and_statement(&self, ctx: &mut Ctx) -> (VariableId, MemoryStatementId) { - 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_block: -1, - unique_id_in_scope: -1 - }); - let variable_expr_id = self.create_variable_expr(ctx, variable_id); + 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(), @@ -363,30 +300,189 @@ impl PassRewriting { return (variable_id, variable_statement_id); } +} - /// Creates an integer literal. The caller still needs to set its expression - /// parent afterwards. - fn create_literal_integer(&self, ctx: &mut Ctx, value: u64) -> LiteralExpressionId { - return ctx.heap.alloc_literal_expression(|this| LiteralExpression{ - this, - span: InputSpan::new(), - value: Literal::Integer(LiteralInteger{ - unsigned_value: value, - negated: false, - }), - parent: ExpressionParent::None, - unique_id_in_definition: -1 - }); +// ----------------------------------------------------------------------------- +// Utilities to create compiler-generated AST nodes +// ----------------------------------------------------------------------------- + +fn create_ast_variable(ctx: &mut Ctx) -> VariableId { + return 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_block: -1, + unique_id_in_scope: -1, + }); +} + +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, arguments: Vec) -> CallExpressionId { + 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, argument_id) in arguments.iter().cloned().enumerate() { + let argument_expr = &mut ctx.heap[argument_id]; + *argument_expr.parent_mut() = ExpressionParent::Expression(call_expression_id.upcat(), argument_index as u32); } - fn create_variable_expr(&self, 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 - }) + 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, + unique_id_in_definition: -1 + }); +} + +fn create_ast_equality_comparison_expr(ctx: &mut Ctx, variable_id: VariableId, value: u64) -> BinaryExpressionId { + let var_expr_id = create_ast_variable_expr(ctx, variable_id); + let int_expr_id = create_ast_literal_integer_expr(ctx, value); + 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, + unique_id_in_definition: -1, + }); + + 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, 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_if_statement(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, + span: InputSpan::new(), + test: condition_expression_id, + true_body, + false_body, + 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); + + let true_body_stmt = &ctx.heap[true_body]; + let true_body_end_stmt = &mut ctx.heap[true_body_stmt.end_block]; + true_body_end_stmt.next = end_if_stmt_id.upcast(); + + if let Some(false_body) = false_body { + let false_body_stmt = &ctx.heap[false_body]; + let false_body_end_stmt = &mut ctx.heap[false_body_stmt.end_block]; + false_body_end_stmt.next = end_if_stmt_id.upcast(); } + + 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: BlockStatementId) { + // Point if-statement to "false body" + let if_stmt = &mut ctx.heap[if_statement_id]; + debug_assert!(if_stmt.false_body.is_none()); // simplifies logic, not necessary + if_stmt.false_body = Some(false_body); + + // Point end of false body to the end of the if statement + let false_body_stmt = &ctx.heap[false_body]; + let false_body_end_stmt = &mut ctx.heap[false_body_stmt.end_block]; + false_body_end_stmt.next = end_if_statement_id.upcast(); } \ No newline at end of file