1. Expression Node Mapping#

ASTx BinaryOp → Python ast.BinOp#

ASTx Structure:

BinaryOp(
    lhs: Expr,
    rhs: Expr,
    op: BinaryOpKind
)

Python AST Equivalent:

ast.BinOp(
    left: ast.expr,
    op: operator,
    right: ast.expr
)

Conversion Logic:

# Operator mapping table
OP_MAP = {
    BinaryOpKind.add: ast.Add(),
    BinaryOpKind.sub: ast.Sub(),
    BinaryOpKind.mul: ast.Mult()
}

@dispatch
def visit(self, node: BinaryOp) -> ast.BinOp:
    return ast.BinOp(
        left=self.visit(node.lhs),
        op=OP_MAP[node.op],
        right=self.visit(node.rhs)
    )

2. Statement Node Mapping#

ASTx DeleteStmt → Python ast.Delete#

ASTx Structure:

DeleteStmt(
    value: Iterable[Identifier]
)

Python AST Equivalent:

ast.Delete(
    targets: list[ast.expr]
)

Conversion Logic:

@dispatch
def visit(self, node: DeleteStmt) -> ast.Delete:
    return ast.Delete(
        targets=[ast.Name(id=ident.value, ctx=ast.Del()) 
                for ident in node.value]
    )

Special Handling:

  • Convert ASTx Identifiers to Python Name nodes with Del context
  • Handle nested deletion targets (e.g., del x)

3. Declaration Mapping#

ASTx VariableDeclaration → Python ast.Assign#

ASTx Structure:

VariableDeclaration(
    name: str,
    type_: DataType,
    value: Expr
)

Python AST Equivalent:

ast.Assign(
    targets: list[ast.Name],
    value: ast.expr
)

Conversion Logic:

@dispatch
def visit(self, node: VariableDeclaration) -> ast.Assign:
    return ast.Assign(
        targets=[ast.Name(id=node.name, ctx=ast.Store())],
        value=self.visit(node.value)
    )

Edge Cases:

  • Support for multiple assignment targets

4. Control Flow Mapping#

ASTx IfStmt → Python ast.If#

ASTx Structure:

IfStmt(
    condition: Expr,
    then_block: list[Statement],
    else_block: list[Statement]
)

Python AST Equivalent:

ast.If(
    test: ast.expr,
    body: list[ast.stmt],
    orelse: list[ast.stmt]
)

Conversion Logic:

@dispatch
def visit(self, node: IfStmt) -> ast.If:
    return ast.If(
        test=self.visit(node.condition),
        body=[self.visit(stmt) for stmt in node.then_block],
        orelse=[self.visit(stmt) for stmt in node.else_block]
    )

Special Handling:

  • Empty else block conversion
  • Nested if-elif-else structures

5. Function Definition Mapping#

ASTx FunctionDef → Python ast.FunctionDef#

ASTx Structure:

FunctionDef(
    name: str,
    args: Arguments,
    return_type: DataType,
    body: list[Statement]
)

Python AST Equivalent:

ast.FunctionDef(
    name: str,
    args: ast.arguments,
    body: list[ast.stmt],
    decorator_list: list[ast.expr]
)

Conversion Logic:

@dispatch
def visit(self, node: FunctionDef) -> ast.FunctionDef:
    return ast.FunctionDef(
        name=node.name,
        args=self.visit(node.args),
        body=[self.visit(stmt) for stmt in node.body],
        decorator_list=[],
        returns=ast.Name(id=node.return_type.__name__)
    )

Special Handling:

  • Argument type annotations
  • Return type declarations
  • Decorator support

6. Context Management Strategy#

ASTx Context:

Variable(
    name: str,
    type_: DataType
)

Python Context Handling:

class ContextManager:
    def __init__(self):
        self.scope_stack = [{}]
    
    def add_variable(self, name: str, node: ast.AST):
        self.scope_stack[-1][name] = node
        
    def get_variable(self, name: str) -> ast.expr:
        for scope in reversed(self.scope_stack):
            if name in scope:
                return scope[name]
        raise NameError(f"Variable {name} not defined")

Validation Strategy#

  • Round Trip Validation
def test_binary_op_roundtrip():
    # ASTx → Python AST → Python code
    astx_node = BinaryOp(LiteralInt32(2), LiteralInt32(3), BinaryOpKind.add)
    transpiler = ASTxPythonASTTranspiler()
    py_ast = transpiler.visit(astx_node)
    
    # Generate Python code
    code = ast.unparse(py_ast)
    assert code == "(2 + 3)"