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.
- vex_ast/__init__.py +65 -0
- vex_ast/ast/__init__.py +75 -0
- vex_ast/ast/core.py +71 -0
- vex_ast/ast/expressions.py +233 -0
- vex_ast/ast/interfaces.py +192 -0
- vex_ast/ast/literals.py +80 -0
- vex_ast/ast/navigator.py +213 -0
- vex_ast/ast/operators.py +136 -0
- vex_ast/ast/statements.py +351 -0
- vex_ast/ast/validators.py +114 -0
- vex_ast/ast/vex_nodes.py +241 -0
- vex_ast/parser/__init__.py +0 -0
- vex_ast/parser/factory.py +179 -0
- vex_ast/parser/interfaces.py +35 -0
- vex_ast/parser/python_parser.py +725 -0
- vex_ast/parser/strategies.py +0 -0
- vex_ast/registry/__init__.py +51 -0
- vex_ast/registry/api.py +155 -0
- vex_ast/registry/categories.py +136 -0
- vex_ast/registry/language_map.py +78 -0
- vex_ast/registry/registry.py +153 -0
- vex_ast/registry/signature.py +143 -0
- vex_ast/registry/simulation_behavior.py +9 -0
- vex_ast/registry/validation.py +44 -0
- vex_ast/serialization/__init__.py +37 -0
- vex_ast/serialization/json_deserializer.py +264 -0
- vex_ast/serialization/json_serializer.py +148 -0
- vex_ast/serialization/schema.py +471 -0
- vex_ast/utils/__init__.py +0 -0
- vex_ast/utils/errors.py +112 -0
- vex_ast/utils/source_location.py +39 -0
- vex_ast/utils/type_definitions.py +0 -0
- vex_ast/visitors/__init__.py +0 -0
- vex_ast/visitors/analyzer.py +103 -0
- vex_ast/visitors/base.py +130 -0
- vex_ast/visitors/printer.py +145 -0
- vex_ast/visitors/transformer.py +0 -0
- vex_ast-0.1.0.dist-info/METADATA +176 -0
- vex_ast-0.1.0.dist-info/RECORD +41 -0
- vex_ast-0.1.0.dist-info/WHEEL +5 -0
- 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
|
+
]
|
vex_ast/ast/__init__.py
ADDED
@@ -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)
|