From 70aa6dc21be6eeae497ea8f4bb85d20f49a8b272 2022-02-08 15:17:28 From: MH Date: 2022-02-08 15:17:28 Subject: [PATCH] WIP: More AST rewriting --- diff --git a/src/protocol/input_source.rs b/src/protocol/input_source.rs index fccb0372e7697ff8478444038d3fad561cb7e0f1..4940c2b17a6a2fe4468461bb360a8746ee671451 100644 --- a/src/protocol/input_source.rs +++ b/src/protocol/input_source.rs @@ -21,7 +21,8 @@ pub struct InputSpan { } impl InputSpan { - // This will only be used for builtin functions + // This must only be used if you're sure that the span will not be involved + // in creating an error message. #[inline] pub fn new() -> InputSpan { InputSpan{ begin: InputPosition{ line: 0, offset: 0 }, end: InputPosition{ line: 0, offset: 0 }} diff --git a/src/protocol/parser/pass_rewriting.rs b/src/protocol/parser/pass_rewriting.rs index bbcb177ca6141f6edcb411868c742ff7a2ec7b17..d678d2cd3a2fb12c52158cd6ef94c8d899cc387d 100644 --- a/src/protocol/parser/pass_rewriting.rs +++ b/src/protocol/parser/pass_rewriting.rs @@ -1,3 +1,14 @@ +// 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::*; @@ -112,11 +123,19 @@ impl Visitor for PassRewriting { 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_call_and_create_replacement_variable(ctx, call_id, expr_id); - transformed_stmts.push(variable_stmt_id); + 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); } + // 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()); + + // TODO: Call the runtime function for eeach of the substituted port variables to register all ports for the select statement + call_id_section.forget(); expr_id_section.forget(); @@ -139,7 +158,7 @@ impl Visitor for PassRewriting { } impl PassRewriting { - fn modify_call_and_create_replacement_variable(&self, ctx: &mut Ctx, call_expr_id: CallExpressionId, port_expr_id: ExpressionId) -> (VariableId, MemoryStatementId) { + 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]; @@ -175,8 +194,8 @@ impl PassRewriting { full_span: port_expr_span }, identifier: Identifier::new_empty(port_expr_span), - relative_pos_in_block: 0, - unique_id_in_scope: 0, + relative_pos_in_block: -1, + unique_id_in_scope: -1, }); let variable_decl_stmt = ctx.heap.alloc_memory_statement(|this| MemoryStatement{ this, @@ -209,9 +228,165 @@ impl PassRewriting { 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, + 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_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 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); + } + + /// 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 + }); + } + + 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 + }) + } } \ No newline at end of file