Changeset - 9573233dccde
[Not reviewed]
0 1 0
MH - 3 years ago 2022-02-07 17:58:00
contact@maxhenger.nl
WIP: Revised AST transformation code
1 file changed with 263 insertions and 167 deletions:
0 comments (0 inline, 0 general)
src/protocol/parser/pass_rewriting.rs
Show inline comments
 
@@ -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<ExpressionId>) -> (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<ExpressionId>) -> 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<BlockStatementId>) -> (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
0 comments (0 inline, 0 general)