vex-ast 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. vex_ast/__init__.py +65 -0
  2. vex_ast/ast/__init__.py +75 -0
  3. vex_ast/ast/core.py +71 -0
  4. vex_ast/ast/expressions.py +233 -0
  5. vex_ast/ast/interfaces.py +192 -0
  6. vex_ast/ast/literals.py +80 -0
  7. vex_ast/ast/navigator.py +213 -0
  8. vex_ast/ast/operators.py +136 -0
  9. vex_ast/ast/statements.py +351 -0
  10. vex_ast/ast/validators.py +114 -0
  11. vex_ast/ast/vex_nodes.py +241 -0
  12. vex_ast/parser/__init__.py +0 -0
  13. vex_ast/parser/factory.py +179 -0
  14. vex_ast/parser/interfaces.py +35 -0
  15. vex_ast/parser/python_parser.py +725 -0
  16. vex_ast/parser/strategies.py +0 -0
  17. vex_ast/registry/__init__.py +51 -0
  18. vex_ast/registry/api.py +155 -0
  19. vex_ast/registry/categories.py +136 -0
  20. vex_ast/registry/language_map.py +78 -0
  21. vex_ast/registry/registry.py +153 -0
  22. vex_ast/registry/signature.py +143 -0
  23. vex_ast/registry/simulation_behavior.py +9 -0
  24. vex_ast/registry/validation.py +44 -0
  25. vex_ast/serialization/__init__.py +37 -0
  26. vex_ast/serialization/json_deserializer.py +264 -0
  27. vex_ast/serialization/json_serializer.py +148 -0
  28. vex_ast/serialization/schema.py +471 -0
  29. vex_ast/utils/__init__.py +0 -0
  30. vex_ast/utils/errors.py +112 -0
  31. vex_ast/utils/source_location.py +39 -0
  32. vex_ast/utils/type_definitions.py +0 -0
  33. vex_ast/visitors/__init__.py +0 -0
  34. vex_ast/visitors/analyzer.py +103 -0
  35. vex_ast/visitors/base.py +130 -0
  36. vex_ast/visitors/printer.py +145 -0
  37. vex_ast/visitors/transformer.py +0 -0
  38. vex_ast-0.1.0.dist-info/METADATA +176 -0
  39. vex_ast-0.1.0.dist-info/RECORD +41 -0
  40. vex_ast-0.1.0.dist-info/WHEEL +5 -0
  41. vex_ast-0.1.0.dist-info/top_level.txt +1 -0
vex_ast/__init__.py ADDED
@@ -0,0 +1,65 @@
1
+ """
2
+ VEX AST Generator Package.
3
+
4
+ Provides tools for parsing VEX V5 code and generating an Abstract Syntax Tree (AST).
5
+ """
6
+
7
+ from .ast.core import Program
8
+ from .ast.navigator import AstNavigator
9
+ from .parser.python_parser import parse_string, parse_file
10
+ from .visitors.printer import PrintVisitor
11
+ from .visitors.analyzer import NodeCounter, VariableCollector
12
+ from .utils.errors import ErrorHandler, ErrorType, VexSyntaxError
13
+ from .registry import registry_api, initialize as initialize_registry
14
+ from .serialization.json_serializer import serialize_ast_to_dict, serialize_ast_to_json
15
+ from .serialization.json_deserializer import deserialize_ast_from_dict, deserialize_ast_from_json
16
+ from .serialization.schema import generate_ast_schema, export_schema_to_file
17
+
18
+ __version__ = "0.2.0"
19
+
20
+ # Initialize the registry with default functions
21
+ initialize_registry()
22
+
23
+ def create_navigator(ast: Program) -> AstNavigator:
24
+ """Create an AST navigator for the given AST.
25
+
26
+ Args:
27
+ ast: The AST to navigate
28
+
29
+ Returns:
30
+ An AST navigator for traversing and querying the AST
31
+ """
32
+ return AstNavigator(ast)
33
+
34
+ __all__ = [
35
+ # Core functionality
36
+ "Program",
37
+ "parse_string",
38
+ "parse_file",
39
+
40
+ # AST Navigation
41
+ "AstNavigator",
42
+ "create_navigator",
43
+
44
+ # Visitors
45
+ "PrintVisitor",
46
+ "NodeCounter",
47
+ "VariableCollector",
48
+
49
+ # Error handling
50
+ "ErrorHandler",
51
+ "ErrorType",
52
+ "VexSyntaxError",
53
+
54
+ # Registry
55
+ "registry_api",
56
+ "initialize_registry",
57
+
58
+ # Serialization
59
+ "serialize_ast_to_dict",
60
+ "serialize_ast_to_json",
61
+ "deserialize_ast_from_dict",
62
+ "deserialize_ast_from_json",
63
+ "generate_ast_schema",
64
+ "export_schema_to_file"
65
+ ]
@@ -0,0 +1,75 @@
1
+ """Abstract Syntax Tree (AST) package for VEX code."""
2
+
3
+ # Core AST classes
4
+ from .interfaces import (
5
+ IAstNode, IExpression, IStatement, ILiteral, IIdentifier, IFunctionCall, IAssignment,
6
+ IVisitor, T_VisitorResult, AstNode
7
+ )
8
+ from .core import Expression, Statement, Program
9
+ from .expressions import (
10
+ Identifier, VariableReference, AttributeAccess,
11
+ BinaryOperation, UnaryOperation, FunctionCall, KeywordArgument
12
+ )
13
+ from .statements import (
14
+ ExpressionStatement, Assignment, Argument, FunctionDefinition,
15
+ IfStatement, WhileLoop, ForLoop, ReturnStatement,
16
+ BreakStatement, ContinueStatement
17
+ )
18
+ from .literals import (
19
+ Literal, NumberLiteral, StringLiteral, BooleanLiteral, NoneLiteral
20
+ )
21
+ from .operators import Operator
22
+ from .vex_nodes import (
23
+ VexAPICallType, VexAPICall, MotorControl, SensorReading,
24
+ TimingControl, DisplayOutput, create_vex_api_call,
25
+ create_vex_api_call_from_interface
26
+ )
27
+ from .validators import validate_vex_functions, VexFunctionValidator
28
+ from .navigator import AstNavigator
29
+
30
+ # Expose the navigator as a factory function
31
+ def create_navigator(root: IAstNode) -> AstNavigator:
32
+ """Create an AST navigator for the given root node.
33
+
34
+ Args:
35
+ root: The root node of the AST
36
+
37
+ Returns:
38
+ An AST navigator for traversing and querying the AST
39
+ """
40
+ return AstNavigator(root)
41
+
42
+ __all__ = [
43
+ # Interfaces
44
+ 'IAstNode', 'IExpression', 'IStatement', 'ILiteral', 'IIdentifier',
45
+ 'IFunctionCall', 'IAssignment', 'IVisitor', 'T_VisitorResult', 'AstNode',
46
+
47
+ # Core
48
+ 'Expression', 'Statement', 'Program',
49
+
50
+ # Expressions
51
+ 'Identifier', 'VariableReference', 'AttributeAccess',
52
+ 'BinaryOperation', 'UnaryOperation', 'FunctionCall', 'KeywordArgument',
53
+
54
+ # Statements
55
+ 'ExpressionStatement', 'Assignment', 'Argument', 'FunctionDefinition',
56
+ 'IfStatement', 'WhileLoop', 'ForLoop', 'ReturnStatement',
57
+ 'BreakStatement', 'ContinueStatement',
58
+
59
+ # Literals
60
+ 'Literal', 'NumberLiteral', 'StringLiteral', 'BooleanLiteral', 'NoneLiteral',
61
+
62
+ # Operators
63
+ 'Operator',
64
+
65
+ # VEX-specific
66
+ 'VexAPICallType', 'VexAPICall', 'MotorControl', 'SensorReading',
67
+ 'TimingControl', 'DisplayOutput', 'create_vex_api_call',
68
+ 'create_vex_api_call_from_interface',
69
+
70
+ # Validators
71
+ 'validate_vex_functions', 'VexFunctionValidator',
72
+
73
+ # Navigator
74
+ 'AstNavigator', 'create_navigator',
75
+ ]
vex_ast/ast/core.py ADDED
@@ -0,0 +1,71 @@
1
+ """Core AST node implementations."""
2
+
3
+ from typing import Any, Dict, List, Optional, Union, cast
4
+
5
+ from .interfaces import AstNode, IAstNode, IExpression, IStatement, IVisitor, T_VisitorResult
6
+ from ..utils.source_location import SourceLocation
7
+
8
+ class Expression(AstNode):
9
+ """Base class for expression nodes."""
10
+
11
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
12
+ """Default implementation that defers to visitor."""
13
+ method_name = f"visit_{self.__class__.__name__.lower()}"
14
+ if hasattr(visitor, method_name):
15
+ return getattr(visitor, method_name)(self)
16
+ return visitor.visit_expression(self)
17
+
18
+ class Statement(AstNode):
19
+ """Base class for statement nodes."""
20
+
21
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
22
+ """Default implementation that defers to visitor."""
23
+ method_name = f"visit_{self.__class__.__name__.lower()}"
24
+ if hasattr(visitor, method_name):
25
+ return getattr(visitor, method_name)(self)
26
+ return visitor.visit_statement(self)
27
+
28
+ class Program(AstNode):
29
+ """Root node of the AST, representing the entire program."""
30
+
31
+ _fields = ('body',)
32
+
33
+ def __init__(self, body: List[IStatement], location: Optional[SourceLocation] = None):
34
+ super().__init__(location)
35
+ self.body = body
36
+
37
+ # Set parent references
38
+ for statement in self.body:
39
+ if isinstance(statement, AstNode):
40
+ statement.set_parent(self)
41
+
42
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
43
+ """Accept a visitor."""
44
+ return visitor.visit_program(self)
45
+
46
+ def get_children(self) -> List[IAstNode]:
47
+ """Get child nodes."""
48
+ return cast(List[IAstNode], self.body)
49
+
50
+ def get_statements(self) -> List[IStatement]:
51
+ """Get all statements in the program."""
52
+ return self.body
53
+
54
+ def add_statement(self, statement: IStatement) -> None:
55
+ """Add a statement to the program."""
56
+ self.body.append(statement)
57
+ if isinstance(statement, AstNode):
58
+ statement.set_parent(self)
59
+
60
+ def insert_statement(self, index: int, statement: IStatement) -> None:
61
+ """Insert a statement at a specific position."""
62
+ self.body.insert(index, statement)
63
+ if isinstance(statement, AstNode):
64
+ statement.set_parent(self)
65
+
66
+ def remove_statement(self, statement: IStatement) -> bool:
67
+ """Remove a statement from the program."""
68
+ if statement in self.body:
69
+ self.body.remove(statement)
70
+ return True
71
+ return False
@@ -0,0 +1,233 @@
1
+ """Expression nodes for the AST."""
2
+
3
+ from typing import Dict, List, Optional, Union, cast, Any
4
+
5
+ from .interfaces import IAstNode, IExpression, IVisitor, T_VisitorResult, IIdentifier, IFunctionCall
6
+ from .core import Expression
7
+ from .operators import Operator
8
+ from ..utils.source_location import SourceLocation
9
+
10
+ class Identifier(Expression, IIdentifier):
11
+ """An identifier (variable name, function name, etc.)."""
12
+
13
+ _fields = ('name',)
14
+
15
+ def __init__(self, name: str, location: Optional[SourceLocation] = None):
16
+ super().__init__(location)
17
+ self.name = name
18
+
19
+ def get_children(self) -> List[IAstNode]:
20
+ """Identifiers have no children."""
21
+ return []
22
+
23
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
24
+ return visitor.visit_identifier(self)
25
+
26
+ def get_name(self) -> str:
27
+ """Get the identifier name."""
28
+ return self.name
29
+
30
+ class VariableReference(Expression):
31
+ """A reference to a variable."""
32
+
33
+ _fields = ('identifier',)
34
+
35
+ def __init__(self, identifier: Identifier, location: Optional[SourceLocation] = None):
36
+ super().__init__(location)
37
+ self.identifier = identifier
38
+ if isinstance(identifier, Expression):
39
+ identifier.set_parent(self)
40
+
41
+ @property
42
+ def name(self) -> str:
43
+ """Convenience property to get the variable name."""
44
+ return self.identifier.name
45
+
46
+ def get_children(self) -> List[IAstNode]:
47
+ """Get child nodes."""
48
+ return [self.identifier]
49
+
50
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
51
+ return visitor.visit_variablereference(self)
52
+
53
+ def get_identifier(self) -> IIdentifier:
54
+ """Get the identifier being referenced."""
55
+ return self.identifier
56
+
57
+ class AttributeAccess(Expression):
58
+ """An attribute access expression (e.g., object.attribute)."""
59
+
60
+ _fields = ('object', 'attribute')
61
+
62
+ def __init__(self, object_expr: IExpression, attribute: str,
63
+ location: Optional[SourceLocation] = None):
64
+ super().__init__(location)
65
+ self.object = object_expr
66
+ self.attribute = attribute
67
+ if isinstance(object_expr, Expression):
68
+ object_expr.set_parent(self)
69
+
70
+ def get_children(self) -> List[IAstNode]:
71
+ """Get child nodes."""
72
+ return [cast(IAstNode, self.object)]
73
+
74
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
75
+ return visitor.visit_attributeaccess(self)
76
+
77
+ def get_object(self) -> IExpression:
78
+ """Get the object expression."""
79
+ return self.object
80
+
81
+ def get_attribute_name(self) -> str:
82
+ """Get the attribute name."""
83
+ return self.attribute
84
+
85
+ class BinaryOperation(Expression):
86
+ """A binary operation (e.g., a + b)."""
87
+
88
+ _fields = ('left', 'op', 'right')
89
+
90
+ def __init__(self, left: IExpression, op: Operator, right: IExpression,
91
+ location: Optional[SourceLocation] = None):
92
+ super().__init__(location)
93
+ self.left = left
94
+ self.op = op
95
+ self.right = right
96
+ if isinstance(left, Expression):
97
+ left.set_parent(self)
98
+ if isinstance(right, Expression):
99
+ right.set_parent(self)
100
+
101
+ def get_children(self) -> List[IAstNode]:
102
+ """Get child nodes."""
103
+ return [cast(IAstNode, self.left), cast(IAstNode, self.right)]
104
+
105
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
106
+ return visitor.visit_binaryoperation(self)
107
+
108
+ def get_left(self) -> IExpression:
109
+ """Get the left operand."""
110
+ return self.left
111
+
112
+ def get_right(self) -> IExpression:
113
+ """Get the right operand."""
114
+ return self.right
115
+
116
+ def get_operator(self) -> Operator:
117
+ """Get the operator."""
118
+ return self.op
119
+
120
+ class UnaryOperation(Expression):
121
+ """A unary operation (e.g., -a, not b)."""
122
+
123
+ _fields = ('op', 'operand')
124
+
125
+ def __init__(self, op: Operator, operand: IExpression,
126
+ location: Optional[SourceLocation] = None):
127
+ super().__init__(location)
128
+ self.op = op
129
+ self.operand = operand
130
+ if isinstance(operand, Expression):
131
+ operand.set_parent(self)
132
+
133
+ def get_children(self) -> List[IAstNode]:
134
+ """Get child nodes."""
135
+ return [cast(IAstNode, self.operand)]
136
+
137
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
138
+ return visitor.visit_unaryoperation(self)
139
+
140
+ def get_operand(self) -> IExpression:
141
+ """Get the operand."""
142
+ return self.operand
143
+
144
+ def get_operator(self) -> Operator:
145
+ """Get the operator."""
146
+ return self.op
147
+
148
+ class KeywordArgument(Expression):
149
+ """A keyword argument in a function call (name=value)."""
150
+
151
+ _fields = ('name', 'value')
152
+
153
+ def __init__(self, name: str, value: IExpression,
154
+ location: Optional[SourceLocation] = None):
155
+ super().__init__(location)
156
+ self.name = name
157
+ self.value = value
158
+ if isinstance(value, Expression):
159
+ value.set_parent(self)
160
+
161
+ def get_children(self) -> List[IAstNode]:
162
+ """Get child nodes."""
163
+ return [cast(IAstNode, self.value)]
164
+
165
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
166
+ return visitor.visit_keywordargument(self)
167
+
168
+ def get_name(self) -> str:
169
+ """Get the keyword name."""
170
+ return self.name
171
+
172
+ def get_value(self) -> IExpression:
173
+ """Get the keyword value."""
174
+ return self.value
175
+
176
+ class FunctionCall(Expression, IFunctionCall):
177
+ """A function call."""
178
+
179
+ _fields = ('function', 'args', 'keywords')
180
+
181
+ def __init__(self, function: IExpression, args: List[IExpression],
182
+ keywords: List[KeywordArgument] = None,
183
+ location: Optional[SourceLocation] = None):
184
+ super().__init__(location)
185
+ self.function = function
186
+ self.args = args or []
187
+ self.keywords = keywords or []
188
+
189
+ # Set parent references
190
+ if isinstance(function, Expression):
191
+ function.set_parent(self)
192
+
193
+ for arg in self.args:
194
+ if isinstance(arg, Expression):
195
+ arg.set_parent(self)
196
+
197
+ for kw in self.keywords:
198
+ if isinstance(kw, Expression):
199
+ kw.set_parent(self)
200
+
201
+ def get_children(self) -> List[IAstNode]:
202
+ """Get child nodes."""
203
+ result: List[IAstNode] = [cast(IAstNode, self.function)]
204
+ result.extend(cast(List[IAstNode], self.args))
205
+ result.extend(cast(List[IAstNode], self.keywords))
206
+ return result
207
+
208
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
209
+ return visitor.visit_functioncall(self)
210
+
211
+ def get_function_expr(self) -> IExpression:
212
+ """Get the function expression."""
213
+ return self.function
214
+
215
+ def get_arguments(self) -> List[IExpression]:
216
+ """Get the positional arguments."""
217
+ return self.args
218
+
219
+ def get_keyword_arguments(self) -> Dict[str, IExpression]:
220
+ """Get the keyword arguments as a dictionary."""
221
+ return {kw.name: kw.value for kw in self.keywords}
222
+
223
+ def add_argument(self, arg: IExpression) -> None:
224
+ """Add a positional argument."""
225
+ self.args.append(arg)
226
+ if isinstance(arg, Expression):
227
+ arg.set_parent(self)
228
+
229
+ def add_keyword_argument(self, name: str, value: IExpression) -> None:
230
+ """Add a keyword argument."""
231
+ kw = KeywordArgument(name, value)
232
+ self.keywords.append(kw)
233
+ kw.set_parent(self)
@@ -0,0 +1,192 @@
1
+ """Fundamental interfaces and protocols for the AST."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Iterable, List, Optional, Protocol, TypeVar, Generic, runtime_checkable, Dict, Union, Tuple
5
+
6
+ from ..utils.source_location import SourceLocation
7
+
8
+ # Visitor pattern type variable
9
+ T_VisitorResult = TypeVar('T_VisitorResult')
10
+
11
+ class IVisitor(Protocol, Generic[T_VisitorResult]):
12
+ """Protocol for AST visitors."""
13
+
14
+ def visit(self, node: 'IAstNode') -> T_VisitorResult:
15
+ """Visit an AST node."""
16
+ ...
17
+
18
+ @runtime_checkable
19
+ class IAstNode(Protocol):
20
+ """Protocol defining the minimum interface for an AST node."""
21
+
22
+ location: Optional[SourceLocation]
23
+
24
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
25
+ """Accept a visitor (Visitor Pattern)."""
26
+ ...
27
+
28
+ def get_children(self) -> Iterable['IAstNode']:
29
+ """Get child nodes of this node."""
30
+ ...
31
+
32
+ def get_child_by_name(self, name: str) -> Optional['IAstNode']:
33
+ """Get a child node by its field name."""
34
+ ...
35
+
36
+ def get_child_names(self) -> List[str]:
37
+ """Get the names of all child fields."""
38
+ ...
39
+
40
+ def get_parent(self) -> Optional['IAstNode']:
41
+ """Get the parent node, if available."""
42
+ ...
43
+
44
+ def get_attributes(self) -> Dict[str, Any]:
45
+ """Get all attributes of this node as a dictionary."""
46
+ ...
47
+
48
+ def to_dict(self) -> Dict[str, Any]:
49
+ """
50
+ Convert the node to a dictionary representation.
51
+
52
+ This is an optional method for serialization support.
53
+
54
+ Returns:
55
+ A dictionary representation of the node
56
+ """
57
+ ...
58
+
59
+ @runtime_checkable
60
+ class IExpression(IAstNode, Protocol):
61
+ """Protocol for expression nodes."""
62
+ pass
63
+
64
+ @runtime_checkable
65
+ class IStatement(IAstNode, Protocol):
66
+ """Protocol for statement nodes."""
67
+ pass
68
+
69
+ @runtime_checkable
70
+ class ILiteral(IExpression, Protocol):
71
+ """Protocol for literal value nodes."""
72
+ value: Any
73
+
74
+ def get_value(self) -> Any:
75
+ """Get the literal value."""
76
+ ...
77
+
78
+ @runtime_checkable
79
+ class IIdentifier(IExpression, Protocol):
80
+ """Protocol for identifier nodes."""
81
+ name: str
82
+
83
+ def get_name(self) -> str:
84
+ """Get the identifier name."""
85
+ ...
86
+
87
+ @runtime_checkable
88
+ class IFunctionCall(IExpression, Protocol):
89
+ """Protocol for function call nodes."""
90
+
91
+ def get_function_expr(self) -> IExpression:
92
+ """Get the function expression."""
93
+ ...
94
+
95
+ def get_arguments(self) -> List[IExpression]:
96
+ """Get the positional arguments."""
97
+ ...
98
+
99
+ def get_keyword_arguments(self) -> Dict[str, IExpression]:
100
+ """Get the keyword arguments as a dictionary."""
101
+ ...
102
+
103
+ @runtime_checkable
104
+ class IAssignment(IStatement, Protocol):
105
+ """Protocol for assignment nodes."""
106
+
107
+ def get_target(self) -> IExpression:
108
+ """Get the assignment target."""
109
+ ...
110
+
111
+ def get_value(self) -> IExpression:
112
+ """Get the assigned value."""
113
+ ...
114
+
115
+ class AstNode(ABC):
116
+ """Abstract base implementation of the IAstNode protocol."""
117
+
118
+ def __init__(self, location: Optional[SourceLocation] = None):
119
+ self.location = location
120
+ self._parent: Optional[IAstNode] = None
121
+
122
+ @abstractmethod
123
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
124
+ """Accept a visitor (Visitor Pattern)."""
125
+ pass
126
+
127
+ @abstractmethod
128
+ def get_children(self) -> List[IAstNode]:
129
+ """Get child nodes of this node."""
130
+ pass
131
+
132
+ def get_child_by_name(self, name: str) -> Optional[IAstNode]:
133
+ """Get a child node by its field name."""
134
+ if not hasattr(self, '_fields') or name not in getattr(self, '_fields'):
135
+ return None
136
+
137
+ value = getattr(self, name, None)
138
+ if isinstance(value, IAstNode):
139
+ return value
140
+ elif isinstance(value, list) and value and isinstance(value[0], IAstNode):
141
+ return value[0] # Return first item if it's a list of nodes
142
+
143
+ return None
144
+
145
+ def get_child_names(self) -> List[str]:
146
+ """Get the names of all child fields."""
147
+ if not hasattr(self, '_fields'):
148
+ return []
149
+ return list(getattr(self, '_fields'))
150
+
151
+ def get_parent(self) -> Optional[IAstNode]:
152
+ """Get the parent node, if available."""
153
+ return getattr(self, '_parent', None)
154
+
155
+ def set_parent(self, parent: IAstNode) -> None:
156
+ """Set the parent node reference."""
157
+ self._parent = parent
158
+
159
+ def get_attributes(self) -> Dict[str, Any]:
160
+ """Get all attributes of this node as a dictionary."""
161
+ result = {}
162
+ if hasattr(self, '_fields'):
163
+ for field in getattr(self, '_fields'):
164
+ result[field] = getattr(self, field, None)
165
+ return result
166
+
167
+ def __eq__(self, other: Any) -> bool:
168
+ """Compare nodes for equality."""
169
+ if not isinstance(other, self.__class__):
170
+ return NotImplemented
171
+
172
+ # Compare all attributes defined in _fields
173
+ if hasattr(self, '_fields'):
174
+ for field in getattr(self, '_fields'):
175
+ if getattr(self, field, None) != getattr(other, field, None):
176
+ return False
177
+
178
+ # Compare location
179
+ return self.location == other.location
180
+
181
+ def to_dict(self) -> Dict[str, Any]:
182
+ """
183
+ Convert the node to a dictionary representation.
184
+
185
+ This implementation uses the serialization visitor to create a
186
+ dictionary representation of the node.
187
+
188
+ Returns:
189
+ A dictionary representation of the node
190
+ """
191
+ from ..serialization.json_serializer import serialize_ast_to_dict
192
+ return serialize_ast_to_dict(self)