arxlang / astx

https://astx.arxlang.org/
Other
3 stars 4 forks source link

`MemberAccessExpr` - Accesses a member of a structure or class (e.g., `obj.field`). #97

Open xmnlab opened 2 months ago

xmnlab commented 2 months ago

from gpt:

Understanding the Existing Structure

Based on the modules you've provided and the previous classes we've created, here's a summary of key points relevant to creating the MemberAccessExpr class:


Designing the MemberAccessExpr Class

We need to design a MemberAccessExpr class that:


1. Updating ASTKind Enum

First, we need to add a new kind for the MemberAccessExpr.

In astx/base.py, add:

# astx/base.py

@public
class ASTKind(Enum):
    """The expression kind class used for downcasting."""

    # ... existing kinds ...

    # Expressions
    ForRangeExprKind = -800
    ForCounterExprKind = -801
    WhileExprKind = -802
    MemberAccessExprKind = -803  # Add this line

    # ... rest of the code ...

**2. Defining MemberAccessExpr

We'll define the MemberAccessExpr in astx/expressions.py, alongside other expressions.

In astx/expressions.py, add the following:

# astx/expressions.py

from __future__ import annotations
from typing import Optional

from public import public

from astx.base import (
    NO_SOURCE_LOCATION,
    ASTKind,
    ASTNodes,
    Expr,
    SourceLocation,
)
from astx.variables import Variable
from astx.types import ReprStruct

@public
class MemberAccessExpr(Expr):
    """AST class for member access expressions."""

    value: Expr
    attr: str

    def __init__(
        self,
        value: Expr,
        attr: str,
        loc: SourceLocation = NO_SOURCE_LOCATION,
        parent: Optional[ASTNodes] = None,
    ) -> None:
        """
        Initialize the MemberAccessExpr instance.

        Parameters:
        - value: The expression representing the object whose member is being accessed.
        - attr: The name of the member (attribute or method) being accessed.
        - loc: The source location of the expression.
        - parent: The parent AST node.
        """
        super().__init__(loc=loc, parent=parent)
        self.value = value
        self.attr = attr
        self.kind = ASTKind.MemberAccessExprKind

    def __str__(self) -> str:
        """Return a string representation of the object."""
        return f"MemberAccessExpr({self.value}.{self.attr})"

    def get_struct(self, simplified: bool = False) -> ReprStruct:
        """Return the AST structure of the object."""
        key = "MemberAccessExpr"
        value = {
            "object": self.value.get_struct(simplified),
            "attribute": self.attr,
        }
        return self._prepare_struct(key, value, simplified)

Explanation:


3. Updating astx/__init__.py

Add the MemberAccessExpr class to your package's __init__.py:

# astx/__init__.py

from astx.expressions import (
    ForRangeExpr,
    ForCounterExpr,
    WhileExpr,
    MemberAccessExpr,  # Add this line
)

__all__ = [
    # ... existing exports ...
    "ForRangeExpr",
    "ForCounterExpr",
    "WhileExpr",
    "MemberAccessExpr",  # Add this line
]

4. Ensuring Integration with Existing Structures


Example Usage

1. Simple Member Access

from astx.expressions import MemberAccessExpr
from astx.variables import Variable
from astx.base import SourceLocation

# Suppose we have an object 'person' and we want to access 'name' attribute
person_var = Variable(name="person", loc=SourceLocation(line=1, col=0))

# Create MemberAccessExpr for 'person.name'
member_access = MemberAccessExpr(
    value=person_var,
    attr="name",
    loc=SourceLocation(line=1, col=7)
)

# Print the string representation
print(member_access)

Output:

MemberAccessExpr(Variable[person].name)

2. Chained Member Access

# Suppose we want to access 'address.city' of 'person'
# First, create 'person.address' MemberAccessExpr
address_access = MemberAccessExpr(
    value=person_var,
    attr="address",
    loc=SourceLocation(line=1, col=7)
)

# Then, create 'person.address.city' MemberAccessExpr
city_access = MemberAccessExpr(
    value=address_access,
    attr="city",
    loc=SourceLocation(line=1, col=15)
)

# Print the string representation
print(city_access)

Output:

MemberAccessExpr(MemberAccessExpr(Variable[person].address).city)

3. Using in an Assignment

from astx.variables import VariableAssignment
from astx.datatypes import LiteralString
from astx.modifiers import MutabilityKind
from astx.base import SourceLocation

# Assign a new value to 'person.name'
assignment = VariableAssignment(
    name="person.name",  # Note: The name here is a string; in practice, we'd need to handle this appropriately
    value=LiteralString(value="Alice"),
    loc=SourceLocation(line=2, col=0)
)

# Alternatively, use the MemberAccessExpr in the assignment
assignment = VariableAssignment(
    name=member_access,  # Use the MemberAccessExpr as the target
    value=LiteralString(value="Alice"),
    loc=SourceLocation(line=2, col=0)
)

# Print the string representation
print(assignment)

Output:

VariableAssignment[MemberAccessExpr(Variable[person].name)]

Integration with Existing Structures

Modifying VariableAssignment in astx/variables.py:

# astx/variables.py

@public
class VariableAssignment(StatementType):
    """AST class for variable assignment."""

    target: Union[str, Expr]  # Modify this line to accept Expr
    value: Expr

    def __init__(
        self,
        name: Union[str, Expr],
        value: Expr,
        loc: SourceLocation = NO_SOURCE_LOCATION,
        parent: Optional[ASTNodes] = None,
    ) -> None:
        """Initialize the VariableAssignment instance."""
        super().__init__(loc=loc, parent=parent)
        self.target = name  # Change to 'target' for clarity
        self.value = value
        self.kind = ASTKind.VariableAssignmentKind

    def __str__(self) -> str:
        """Return a string that represents the object."""
        target_str = str(self.target) if isinstance(self.target, Expr) else self.target
        return f"VariableAssignment[{target_str}]"

    def get_struct(self, simplified: bool = False) -> ReprStruct:
        """Return the AST structure of the object."""
        key = str(self)
        value = self.value.get_struct(simplified)
        return self._prepare_struct(key, value, simplified)

Testing and Validation


Documentation


Conclusion

By creating the MemberAccessExpr class, we've extended your astx module to support member access expressions, similar to Python's AST Attribute node. This allows you to represent expressions like object.member within your AST, enabling more accurate and flexible code representations.


Full Code for astx/expressions.py with MemberAccessExpr

Here's the complete code for astx/expressions.py, including the MemberAccessExpr:

# astx/expressions.py

from __future__ import annotations
from typing import Optional, Union

from public import public

from astx.base import (
    NO_SOURCE_LOCATION,
    ASTKind,
    ASTNodes,
    Expr,
    SourceLocation,
)
from astx.variables import (
    InlineVariableDeclaration,
    VariableAssignment,
    Variable,
)
from astx.types import ReprStruct
from astx.datatypes import LiteralInt32, DataTypeOps
from astx.operators import BinaryOp
from astx.blocks import Block

@public
class ForRangeExpr(Expr):
    # ... existing code for ForRangeExpr ...

@public
class ForCounterExpr(Expr):
    # ... existing code for ForCounterExpr ...

@public
class WhileExpr(Expr):
    # ... existing code for WhileExpr ...

@public
class MemberAccessExpr(Expr):
    """AST class for member access expressions."""

    value: Expr
    attr: str

    def __init__(
        self,
        value: Expr,
        attr: str,
        loc: SourceLocation = NO_SOURCE_LOCATION,
        parent: Optional[ASTNodes] = None,
    ) -> None:
        """
        Initialize the MemberAccessExpr instance.

        Parameters:
        - value: The expression representing the object whose member is being accessed.
        - attr: The name of the member (attribute or method) being accessed.
        - loc: The source location of the expression.
        - parent: The parent AST node.
        """
        super().__init__(loc=loc, parent=parent)
        self.value = value
        self.attr = attr
        self.kind = ASTKind.MemberAccessExprKind

    def __str__(self) -> str:
        """Return a string representation of the object."""
        return f"MemberAccessExpr({self.value}.{self.attr})"

    def get_struct(self, simplified: bool = False) -> ReprStruct:
        """Return the AST structure of the object."""
        key = "MemberAccessExpr"
        value = {
            "object": self.value.get_struct(simplified),
            "attribute": self.attr,
        }
        return self._prepare_struct(key, value, simplified)

Final Notes

xmnlab commented 2 months ago

In python this is called Attribute, so maybe MemberAccessExpr could be derived into AttributeExpr and MethodExpr .. it is just an idea

example from Python AST

>>> ast.dump(ast.parse("a.cool"))
"Module(body=[Expr(value=Attribute(value=Name(id='a', ctx=Load()), attr='cool', ctx=Load()))], type_ignores=[])"
>>> ast.dump(ast.parse("a.cool()"))
"Module(body=[Expr(value=Call(func=Attribute(value=Name(id='a', ctx=Load()), attr='cool', ctx=Load()), args=[], keywords=[]))], type_ignores=[])"