vex-ast 0.2.5__py3-none-any.whl → 0.2.6__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/README.md +101 -51
- vex_ast/READMEAPI.md +133 -318
- vex_ast/__init__.py +81 -81
- vex_ast/ast/README.md +87 -87
- vex_ast/ast/__init__.py +74 -74
- vex_ast/ast/core.py +71 -71
- vex_ast/ast/expressions.py +276 -276
- vex_ast/ast/interfaces.py +208 -208
- vex_ast/ast/literals.py +80 -80
- vex_ast/ast/navigator.py +225 -225
- vex_ast/ast/operators.py +135 -135
- vex_ast/ast/statements.py +351 -351
- vex_ast/ast/validators.py +121 -121
- vex_ast/ast/vex_nodes.py +279 -279
- vex_ast/parser/README.md +47 -47
- vex_ast/parser/__init__.py +26 -26
- vex_ast/parser/factory.py +190 -190
- vex_ast/parser/interfaces.py +34 -34
- vex_ast/parser/python_parser.py +831 -831
- vex_ast/registry/README.md +107 -29
- vex_ast/registry/__init__.py +51 -51
- vex_ast/registry/api.py +190 -155
- vex_ast/registry/categories.py +179 -136
- vex_ast/registry/functions/__init__.py +10 -10
- vex_ast/registry/functions/constructors.py +71 -35
- vex_ast/registry/functions/display.py +146 -146
- vex_ast/registry/functions/drivetrain.py +163 -163
- vex_ast/registry/functions/initialize.py +31 -31
- vex_ast/registry/functions/motor.py +140 -140
- vex_ast/registry/functions/sensors.py +194 -194
- vex_ast/registry/functions/timing.py +103 -103
- vex_ast/registry/language_map.py +77 -77
- vex_ast/registry/registry.py +164 -153
- vex_ast/registry/signature.py +269 -191
- vex_ast/registry/simulation_behavior.py +8 -8
- vex_ast/registry/validation.py +43 -43
- vex_ast/serialization/__init__.py +37 -37
- vex_ast/serialization/json_deserializer.py +284 -284
- vex_ast/serialization/json_serializer.py +148 -148
- vex_ast/serialization/schema.py +492 -492
- vex_ast/types/README.md +78 -26
- vex_ast/types/__init__.py +140 -140
- vex_ast/types/base.py +83 -83
- vex_ast/types/enums.py +122 -122
- vex_ast/types/objects.py +64 -64
- vex_ast/types/primitives.py +68 -68
- vex_ast/types/type_checker.py +31 -31
- vex_ast/utils/README.md +39 -39
- vex_ast/utils/__init__.py +37 -37
- vex_ast/utils/errors.py +112 -112
- vex_ast/utils/source_location.py +38 -38
- vex_ast/utils/type_definitions.py +8 -8
- vex_ast/visitors/README.md +49 -49
- vex_ast/visitors/__init__.py +27 -27
- vex_ast/visitors/analyzer.py +102 -102
- vex_ast/visitors/base.py +133 -133
- vex_ast/visitors/printer.py +196 -196
- {vex_ast-0.2.5.dist-info → vex_ast-0.2.6.dist-info}/METADATA +206 -174
- vex_ast-0.2.6.dist-info/RECORD +64 -0
- vex_ast-0.2.5.dist-info/RECORD +0 -64
- {vex_ast-0.2.5.dist-info → vex_ast-0.2.6.dist-info}/WHEEL +0 -0
- {vex_ast-0.2.5.dist-info → vex_ast-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {vex_ast-0.2.5.dist-info → vex_ast-0.2.6.dist-info}/top_level.txt +0 -0
vex_ast/ast/vex_nodes.py
CHANGED
@@ -1,279 +1,279 @@
|
|
1
|
-
"""VEX V5-specific AST nodes with registry integration."""
|
2
|
-
|
3
|
-
from typing import Dict, List, Optional, cast, Tuple, Any
|
4
|
-
from enum import Enum, auto
|
5
|
-
|
6
|
-
from .interfaces import IAstNode, IExpression, IVisitor, T_VisitorResult, IFunctionCall
|
7
|
-
from .expressions import FunctionCall, KeywordArgument
|
8
|
-
from ..utils.source_location import SourceLocation
|
9
|
-
from ..registry.api import registry_api
|
10
|
-
from ..registry.signature import VexFunctionSignature, SimulationCategory
|
11
|
-
|
12
|
-
class VexAPICallType(Enum):
|
13
|
-
"""Types of VEX API calls for classification"""
|
14
|
-
MOTOR_CONTROL = auto()
|
15
|
-
SENSOR_READING = auto()
|
16
|
-
TIMING_CONTROL = auto()
|
17
|
-
DISPLAY_OUTPUT = auto()
|
18
|
-
BRAIN_FUNCTION = auto()
|
19
|
-
CONTROLLER_FUNCTION = auto()
|
20
|
-
COMPETITION = auto()
|
21
|
-
OTHER = auto()
|
22
|
-
|
23
|
-
class VexAPICall(FunctionCall):
|
24
|
-
"""Base class for VEX API function calls."""
|
25
|
-
|
26
|
-
def __init__(self, function: IExpression, args: List[IExpression],
|
27
|
-
keywords: List[KeywordArgument] = None,
|
28
|
-
location: Optional[SourceLocation] = None,
|
29
|
-
call_type: VexAPICallType = VexAPICallType.OTHER):
|
30
|
-
super().__init__(function, args, keywords, location)
|
31
|
-
self.call_type = call_type
|
32
|
-
self._signature: Optional[VexFunctionSignature] = None
|
33
|
-
self._validation_error: Optional[str] = None
|
34
|
-
|
35
|
-
def get_function_name(self) -> Optional[str]:
|
36
|
-
"""Get the function name if available"""
|
37
|
-
if hasattr(self.function, 'name'):
|
38
|
-
return self.function.name
|
39
|
-
|
40
|
-
# Try to handle attribute access (e.g., motor.spin)
|
41
|
-
if hasattr(self.function, 'attribute') and hasattr(self.function, 'object'):
|
42
|
-
obj = self.function.object
|
43
|
-
attr = self.function.attribute
|
44
|
-
if hasattr(obj, 'name'):
|
45
|
-
return f"{obj.name}.{attr}"
|
46
|
-
|
47
|
-
return None
|
48
|
-
|
49
|
-
def resolve_signature(self) -> Optional[VexFunctionSignature]:
|
50
|
-
"""Resolve the function signature from the registry"""
|
51
|
-
if self._signature:
|
52
|
-
return self._signature
|
53
|
-
|
54
|
-
function_name = self.get_function_name()
|
55
|
-
if not function_name:
|
56
|
-
return None
|
57
|
-
|
58
|
-
# Try to get signature from registry API
|
59
|
-
if '.' in function_name:
|
60
|
-
# For method calls like "motor1.spin", extract the method name
|
61
|
-
obj_name, method_name = function_name.split('.', 1)
|
62
|
-
|
63
|
-
# First try to get the method signature directly
|
64
|
-
self._signature = registry_api.get_function(method_name)
|
65
|
-
|
66
|
-
# If that fails, try to get it as a method of a specific object type
|
67
|
-
# This is a fallback since we don't know the actual type at parse time
|
68
|
-
if not self._signature:
|
69
|
-
# Try common object types
|
70
|
-
from ..types.objects import MOTOR, TIMER, BRAIN, CONTROLLER
|
71
|
-
for obj_type in [MOTOR, TIMER, BRAIN, CONTROLLER]:
|
72
|
-
method_sig = registry_api.get_method(obj_type, method_name)
|
73
|
-
if method_sig:
|
74
|
-
self._signature = method_sig
|
75
|
-
break
|
76
|
-
else:
|
77
|
-
# For direct function calls
|
78
|
-
self._signature = registry_api.get_function(function_name)
|
79
|
-
|
80
|
-
return self._signature
|
81
|
-
|
82
|
-
def validate(self) -> Tuple[bool, Optional[str]]:
|
83
|
-
"""Validate this function call against the registry"""
|
84
|
-
if self._validation_error:
|
85
|
-
return False, self._validation_error
|
86
|
-
|
87
|
-
signature = self.resolve_signature()
|
88
|
-
if not signature:
|
89
|
-
function_name = self.get_function_name() or "<unknown>"
|
90
|
-
self._validation_error = f"Unknown VEX function: {function_name}"
|
91
|
-
return False, self._validation_error
|
92
|
-
|
93
|
-
# Convert args and kwargs to appropriate format
|
94
|
-
arg_values = []
|
95
|
-
for arg in self.args:
|
96
|
-
# For string literals, use their actual string value for validation
|
97
|
-
if hasattr(arg, 'value') and hasattr(arg, '__class__') and arg.__class__.__name__ == 'StringLiteral':
|
98
|
-
arg_values.append(arg.value)
|
99
|
-
else:
|
100
|
-
arg_values.append(arg)
|
101
|
-
|
102
|
-
kwarg_values = {}
|
103
|
-
for kw in (self.keywords or []):
|
104
|
-
# For string literals, use their actual string value for validation
|
105
|
-
if hasattr(kw.value, 'value') and hasattr(kw.value, '__class__') and kw.value.__class__.__name__ == 'StringLiteral':
|
106
|
-
kwarg_values[kw.name] = kw.value.value
|
107
|
-
else:
|
108
|
-
kwarg_values[kw.name] = kw.value
|
109
|
-
|
110
|
-
# Validate against the signature
|
111
|
-
valid, error = signature.validate_arguments(arg_values, kwarg_values)
|
112
|
-
if not valid:
|
113
|
-
self._validation_error = error
|
114
|
-
|
115
|
-
return valid, error
|
116
|
-
|
117
|
-
def get_simulation_category(self) -> Optional[SimulationCategory]:
|
118
|
-
"""Get the simulation category for this function call"""
|
119
|
-
signature = self.resolve_signature()
|
120
|
-
if signature:
|
121
|
-
return signature.category
|
122
|
-
return None
|
123
|
-
|
124
|
-
def get_call_type(self) -> VexAPICallType:
|
125
|
-
"""Get the call type of this VEX API call."""
|
126
|
-
return self.call_type
|
127
|
-
|
128
|
-
def get_signature(self) -> Optional[VexFunctionSignature]:
|
129
|
-
"""Get the function signature if resolved."""
|
130
|
-
return self._signature
|
131
|
-
|
132
|
-
def get_validation_error(self) -> Optional[str]:
|
133
|
-
"""Get the validation error if any."""
|
134
|
-
return self._validation_error
|
135
|
-
|
136
|
-
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
137
|
-
return visitor.visit_vexapicall(self)
|
138
|
-
|
139
|
-
class MotorControl(VexAPICall):
|
140
|
-
"""A VEX motor control function call."""
|
141
|
-
|
142
|
-
def __init__(self, function: IExpression, args: List[IExpression],
|
143
|
-
keywords: List[KeywordArgument] = None,
|
144
|
-
location: Optional[SourceLocation] = None):
|
145
|
-
super().__init__(function, args, keywords, location, VexAPICallType.MOTOR_CONTROL)
|
146
|
-
|
147
|
-
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
148
|
-
return visitor.visit_motorcontrol(self)
|
149
|
-
|
150
|
-
def get_motor_name(self) -> Optional[str]:
|
151
|
-
"""Get the motor name if this is a method call on a motor object."""
|
152
|
-
function_name = self.get_function_name()
|
153
|
-
if function_name and '.' in function_name:
|
154
|
-
return function_name.split('.', 1)[0]
|
155
|
-
return None
|
156
|
-
|
157
|
-
class SensorReading(VexAPICall):
|
158
|
-
"""A VEX sensor reading function call."""
|
159
|
-
|
160
|
-
def __init__(self, function: IExpression, args: List[IExpression],
|
161
|
-
keywords: List[KeywordArgument] = None,
|
162
|
-
location: Optional[SourceLocation] = None):
|
163
|
-
super().__init__(function, args, keywords, location, VexAPICallType.SENSOR_READING)
|
164
|
-
|
165
|
-
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
166
|
-
return visitor.visit_sensorreading(self)
|
167
|
-
|
168
|
-
def get_sensor_name(self) -> Optional[str]:
|
169
|
-
"""Get the sensor name if this is a method call on a sensor object."""
|
170
|
-
function_name = self.get_function_name()
|
171
|
-
if function_name and '.' in function_name:
|
172
|
-
return function_name.split('.', 1)[0]
|
173
|
-
return None
|
174
|
-
|
175
|
-
class TimingControl(VexAPICall):
|
176
|
-
"""A VEX timing control function call."""
|
177
|
-
|
178
|
-
def __init__(self, function: IExpression, args: List[IExpression],
|
179
|
-
keywords: List[KeywordArgument] = None,
|
180
|
-
location: Optional[SourceLocation] = None):
|
181
|
-
super().__init__(function, args, keywords, location, VexAPICallType.TIMING_CONTROL)
|
182
|
-
|
183
|
-
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
184
|
-
return visitor.visit_timingcontrol(self)
|
185
|
-
|
186
|
-
def get_timing_method(self) -> Optional[str]:
|
187
|
-
"""Get the timing method name."""
|
188
|
-
function_name = self.get_function_name()
|
189
|
-
if function_name and '.' in function_name:
|
190
|
-
return function_name.split('.', 1)[1]
|
191
|
-
return function_name
|
192
|
-
|
193
|
-
class DisplayOutput(VexAPICall):
|
194
|
-
"""A VEX display output function call."""
|
195
|
-
|
196
|
-
def __init__(self, function: IExpression, args: List[IExpression],
|
197
|
-
keywords: List[KeywordArgument] = None,
|
198
|
-
location: Optional[SourceLocation] = None):
|
199
|
-
super().__init__(function, args, keywords, location, VexAPICallType.DISPLAY_OUTPUT)
|
200
|
-
|
201
|
-
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
202
|
-
return visitor.visit_displayoutput(self)
|
203
|
-
|
204
|
-
def get_display_method(self) -> Optional[str]:
|
205
|
-
"""Get the display method name."""
|
206
|
-
function_name = self.get_function_name()
|
207
|
-
if function_name and '.' in function_name:
|
208
|
-
return function_name.split('.', 1)[1]
|
209
|
-
return function_name
|
210
|
-
|
211
|
-
def create_vex_api_call(function: IExpression, args: List[IExpression],
|
212
|
-
keywords: List[KeywordArgument] = None,
|
213
|
-
location: Optional[SourceLocation] = None) -> VexAPICall:
|
214
|
-
"""Factory function to create the appropriate VEX API call node"""
|
215
|
-
# Determine the function name
|
216
|
-
function_name = None
|
217
|
-
if hasattr(function, 'name'):
|
218
|
-
function_name = function.name
|
219
|
-
elif hasattr(function, 'attribute') and hasattr(function, 'object'):
|
220
|
-
obj = function.object
|
221
|
-
attr = function.attribute
|
222
|
-
if hasattr(obj, 'name'):
|
223
|
-
function_name = f"{obj.name}.{attr}"
|
224
|
-
|
225
|
-
# If we can't determine the function name, return a generic VexAPICall
|
226
|
-
if not function_name:
|
227
|
-
return VexAPICall(function, args, keywords, location)
|
228
|
-
|
229
|
-
# Look up in the registry API to get the function category
|
230
|
-
signature = None
|
231
|
-
if '.' in function_name:
|
232
|
-
# For method calls like "motor1.spin", extract the method name
|
233
|
-
obj_name, method_name = function_name.split('.', 1)
|
234
|
-
|
235
|
-
# First try to get the method signature directly
|
236
|
-
signature = registry_api.get_function(method_name)
|
237
|
-
|
238
|
-
# If that fails, try to get it as a method of a specific object type
|
239
|
-
if not signature:
|
240
|
-
# Try common object types
|
241
|
-
from ..types.objects import MOTOR, TIMER, BRAIN, CONTROLLER
|
242
|
-
for obj_type in [MOTOR, TIMER, BRAIN, CONTROLLER]:
|
243
|
-
method_sig = registry_api.get_method(obj_type, method_name)
|
244
|
-
if method_sig:
|
245
|
-
signature = method_sig
|
246
|
-
break
|
247
|
-
else:
|
248
|
-
# For direct function calls
|
249
|
-
signature = registry_api.get_function(function_name)
|
250
|
-
|
251
|
-
if not signature:
|
252
|
-
return VexAPICall(function, args, keywords, location)
|
253
|
-
|
254
|
-
# Create the appropriate node type based on the simulation category
|
255
|
-
if signature.category == SimulationCategory.MOTOR_CONTROL:
|
256
|
-
return MotorControl(function, args, keywords, location)
|
257
|
-
elif signature.category == SimulationCategory.SENSOR_READING:
|
258
|
-
return SensorReading(function, args, keywords, location)
|
259
|
-
elif signature.category == SimulationCategory.TIMING_CONTROL:
|
260
|
-
return TimingControl(function, args, keywords, location)
|
261
|
-
elif signature.category == SimulationCategory.DISPLAY_OUTPUT:
|
262
|
-
return DisplayOutput(function, args, keywords, location)
|
263
|
-
|
264
|
-
# Default case
|
265
|
-
return VexAPICall(function, args, keywords, location)
|
266
|
-
|
267
|
-
# Factory function to create VEX API calls from interfaces
|
268
|
-
def create_vex_api_call_from_interface(function_expr: IExpression,
|
269
|
-
args: List[IExpression],
|
270
|
-
kwargs: Dict[str, IExpression] = None,
|
271
|
-
location: Optional[SourceLocation] = None) -> VexAPICall:
|
272
|
-
"""Create a VEX API call from interface types."""
|
273
|
-
# Convert kwargs to KeywordArgument objects
|
274
|
-
keywords = []
|
275
|
-
if kwargs:
|
276
|
-
for name, value in kwargs.items():
|
277
|
-
keywords.append(KeywordArgument(name, value))
|
278
|
-
|
279
|
-
return create_vex_api_call(function_expr, args, keywords, location)
|
1
|
+
"""VEX V5-specific AST nodes with registry integration."""
|
2
|
+
|
3
|
+
from typing import Dict, List, Optional, cast, Tuple, Any
|
4
|
+
from enum import Enum, auto
|
5
|
+
|
6
|
+
from .interfaces import IAstNode, IExpression, IVisitor, T_VisitorResult, IFunctionCall
|
7
|
+
from .expressions import FunctionCall, KeywordArgument
|
8
|
+
from ..utils.source_location import SourceLocation
|
9
|
+
from ..registry.api import registry_api
|
10
|
+
from ..registry.signature import VexFunctionSignature, SimulationCategory
|
11
|
+
|
12
|
+
class VexAPICallType(Enum):
|
13
|
+
"""Types of VEX API calls for classification"""
|
14
|
+
MOTOR_CONTROL = auto()
|
15
|
+
SENSOR_READING = auto()
|
16
|
+
TIMING_CONTROL = auto()
|
17
|
+
DISPLAY_OUTPUT = auto()
|
18
|
+
BRAIN_FUNCTION = auto()
|
19
|
+
CONTROLLER_FUNCTION = auto()
|
20
|
+
COMPETITION = auto()
|
21
|
+
OTHER = auto()
|
22
|
+
|
23
|
+
class VexAPICall(FunctionCall):
|
24
|
+
"""Base class for VEX API function calls."""
|
25
|
+
|
26
|
+
def __init__(self, function: IExpression, args: List[IExpression],
|
27
|
+
keywords: List[KeywordArgument] = None,
|
28
|
+
location: Optional[SourceLocation] = None,
|
29
|
+
call_type: VexAPICallType = VexAPICallType.OTHER):
|
30
|
+
super().__init__(function, args, keywords, location)
|
31
|
+
self.call_type = call_type
|
32
|
+
self._signature: Optional[VexFunctionSignature] = None
|
33
|
+
self._validation_error: Optional[str] = None
|
34
|
+
|
35
|
+
def get_function_name(self) -> Optional[str]:
|
36
|
+
"""Get the function name if available"""
|
37
|
+
if hasattr(self.function, 'name'):
|
38
|
+
return self.function.name
|
39
|
+
|
40
|
+
# Try to handle attribute access (e.g., motor.spin)
|
41
|
+
if hasattr(self.function, 'attribute') and hasattr(self.function, 'object'):
|
42
|
+
obj = self.function.object
|
43
|
+
attr = self.function.attribute
|
44
|
+
if hasattr(obj, 'name'):
|
45
|
+
return f"{obj.name}.{attr}"
|
46
|
+
|
47
|
+
return None
|
48
|
+
|
49
|
+
def resolve_signature(self) -> Optional[VexFunctionSignature]:
|
50
|
+
"""Resolve the function signature from the registry"""
|
51
|
+
if self._signature:
|
52
|
+
return self._signature
|
53
|
+
|
54
|
+
function_name = self.get_function_name()
|
55
|
+
if not function_name:
|
56
|
+
return None
|
57
|
+
|
58
|
+
# Try to get signature from registry API
|
59
|
+
if '.' in function_name:
|
60
|
+
# For method calls like "motor1.spin", extract the method name
|
61
|
+
obj_name, method_name = function_name.split('.', 1)
|
62
|
+
|
63
|
+
# First try to get the method signature directly
|
64
|
+
self._signature = registry_api.get_function(method_name)
|
65
|
+
|
66
|
+
# If that fails, try to get it as a method of a specific object type
|
67
|
+
# This is a fallback since we don't know the actual type at parse time
|
68
|
+
if not self._signature:
|
69
|
+
# Try common object types
|
70
|
+
from ..types.objects import MOTOR, TIMER, BRAIN, CONTROLLER
|
71
|
+
for obj_type in [MOTOR, TIMER, BRAIN, CONTROLLER]:
|
72
|
+
method_sig = registry_api.get_method(obj_type, method_name)
|
73
|
+
if method_sig:
|
74
|
+
self._signature = method_sig
|
75
|
+
break
|
76
|
+
else:
|
77
|
+
# For direct function calls
|
78
|
+
self._signature = registry_api.get_function(function_name)
|
79
|
+
|
80
|
+
return self._signature
|
81
|
+
|
82
|
+
def validate(self) -> Tuple[bool, Optional[str]]:
|
83
|
+
"""Validate this function call against the registry"""
|
84
|
+
if self._validation_error:
|
85
|
+
return False, self._validation_error
|
86
|
+
|
87
|
+
signature = self.resolve_signature()
|
88
|
+
if not signature:
|
89
|
+
function_name = self.get_function_name() or "<unknown>"
|
90
|
+
self._validation_error = f"Unknown VEX function: {function_name}"
|
91
|
+
return False, self._validation_error
|
92
|
+
|
93
|
+
# Convert args and kwargs to appropriate format
|
94
|
+
arg_values = []
|
95
|
+
for arg in self.args:
|
96
|
+
# For string literals, use their actual string value for validation
|
97
|
+
if hasattr(arg, 'value') and hasattr(arg, '__class__') and arg.__class__.__name__ == 'StringLiteral':
|
98
|
+
arg_values.append(arg.value)
|
99
|
+
else:
|
100
|
+
arg_values.append(arg)
|
101
|
+
|
102
|
+
kwarg_values = {}
|
103
|
+
for kw in (self.keywords or []):
|
104
|
+
# For string literals, use their actual string value for validation
|
105
|
+
if hasattr(kw.value, 'value') and hasattr(kw.value, '__class__') and kw.value.__class__.__name__ == 'StringLiteral':
|
106
|
+
kwarg_values[kw.name] = kw.value.value
|
107
|
+
else:
|
108
|
+
kwarg_values[kw.name] = kw.value
|
109
|
+
|
110
|
+
# Validate against the signature
|
111
|
+
valid, error = signature.validate_arguments(arg_values, kwarg_values)
|
112
|
+
if not valid:
|
113
|
+
self._validation_error = error
|
114
|
+
|
115
|
+
return valid, error
|
116
|
+
|
117
|
+
def get_simulation_category(self) -> Optional[SimulationCategory]:
|
118
|
+
"""Get the simulation category for this function call"""
|
119
|
+
signature = self.resolve_signature()
|
120
|
+
if signature:
|
121
|
+
return signature.category
|
122
|
+
return None
|
123
|
+
|
124
|
+
def get_call_type(self) -> VexAPICallType:
|
125
|
+
"""Get the call type of this VEX API call."""
|
126
|
+
return self.call_type
|
127
|
+
|
128
|
+
def get_signature(self) -> Optional[VexFunctionSignature]:
|
129
|
+
"""Get the function signature if resolved."""
|
130
|
+
return self._signature
|
131
|
+
|
132
|
+
def get_validation_error(self) -> Optional[str]:
|
133
|
+
"""Get the validation error if any."""
|
134
|
+
return self._validation_error
|
135
|
+
|
136
|
+
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
137
|
+
return visitor.visit_vexapicall(self)
|
138
|
+
|
139
|
+
class MotorControl(VexAPICall):
|
140
|
+
"""A VEX motor control function call."""
|
141
|
+
|
142
|
+
def __init__(self, function: IExpression, args: List[IExpression],
|
143
|
+
keywords: List[KeywordArgument] = None,
|
144
|
+
location: Optional[SourceLocation] = None):
|
145
|
+
super().__init__(function, args, keywords, location, VexAPICallType.MOTOR_CONTROL)
|
146
|
+
|
147
|
+
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
148
|
+
return visitor.visit_motorcontrol(self)
|
149
|
+
|
150
|
+
def get_motor_name(self) -> Optional[str]:
|
151
|
+
"""Get the motor name if this is a method call on a motor object."""
|
152
|
+
function_name = self.get_function_name()
|
153
|
+
if function_name and '.' in function_name:
|
154
|
+
return function_name.split('.', 1)[0]
|
155
|
+
return None
|
156
|
+
|
157
|
+
class SensorReading(VexAPICall):
|
158
|
+
"""A VEX sensor reading function call."""
|
159
|
+
|
160
|
+
def __init__(self, function: IExpression, args: List[IExpression],
|
161
|
+
keywords: List[KeywordArgument] = None,
|
162
|
+
location: Optional[SourceLocation] = None):
|
163
|
+
super().__init__(function, args, keywords, location, VexAPICallType.SENSOR_READING)
|
164
|
+
|
165
|
+
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
166
|
+
return visitor.visit_sensorreading(self)
|
167
|
+
|
168
|
+
def get_sensor_name(self) -> Optional[str]:
|
169
|
+
"""Get the sensor name if this is a method call on a sensor object."""
|
170
|
+
function_name = self.get_function_name()
|
171
|
+
if function_name and '.' in function_name:
|
172
|
+
return function_name.split('.', 1)[0]
|
173
|
+
return None
|
174
|
+
|
175
|
+
class TimingControl(VexAPICall):
|
176
|
+
"""A VEX timing control function call."""
|
177
|
+
|
178
|
+
def __init__(self, function: IExpression, args: List[IExpression],
|
179
|
+
keywords: List[KeywordArgument] = None,
|
180
|
+
location: Optional[SourceLocation] = None):
|
181
|
+
super().__init__(function, args, keywords, location, VexAPICallType.TIMING_CONTROL)
|
182
|
+
|
183
|
+
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
184
|
+
return visitor.visit_timingcontrol(self)
|
185
|
+
|
186
|
+
def get_timing_method(self) -> Optional[str]:
|
187
|
+
"""Get the timing method name."""
|
188
|
+
function_name = self.get_function_name()
|
189
|
+
if function_name and '.' in function_name:
|
190
|
+
return function_name.split('.', 1)[1]
|
191
|
+
return function_name
|
192
|
+
|
193
|
+
class DisplayOutput(VexAPICall):
|
194
|
+
"""A VEX display output function call."""
|
195
|
+
|
196
|
+
def __init__(self, function: IExpression, args: List[IExpression],
|
197
|
+
keywords: List[KeywordArgument] = None,
|
198
|
+
location: Optional[SourceLocation] = None):
|
199
|
+
super().__init__(function, args, keywords, location, VexAPICallType.DISPLAY_OUTPUT)
|
200
|
+
|
201
|
+
def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
|
202
|
+
return visitor.visit_displayoutput(self)
|
203
|
+
|
204
|
+
def get_display_method(self) -> Optional[str]:
|
205
|
+
"""Get the display method name."""
|
206
|
+
function_name = self.get_function_name()
|
207
|
+
if function_name and '.' in function_name:
|
208
|
+
return function_name.split('.', 1)[1]
|
209
|
+
return function_name
|
210
|
+
|
211
|
+
def create_vex_api_call(function: IExpression, args: List[IExpression],
|
212
|
+
keywords: List[KeywordArgument] = None,
|
213
|
+
location: Optional[SourceLocation] = None) -> VexAPICall:
|
214
|
+
"""Factory function to create the appropriate VEX API call node"""
|
215
|
+
# Determine the function name
|
216
|
+
function_name = None
|
217
|
+
if hasattr(function, 'name'):
|
218
|
+
function_name = function.name
|
219
|
+
elif hasattr(function, 'attribute') and hasattr(function, 'object'):
|
220
|
+
obj = function.object
|
221
|
+
attr = function.attribute
|
222
|
+
if hasattr(obj, 'name'):
|
223
|
+
function_name = f"{obj.name}.{attr}"
|
224
|
+
|
225
|
+
# If we can't determine the function name, return a generic VexAPICall
|
226
|
+
if not function_name:
|
227
|
+
return VexAPICall(function, args, keywords, location)
|
228
|
+
|
229
|
+
# Look up in the registry API to get the function category
|
230
|
+
signature = None
|
231
|
+
if '.' in function_name:
|
232
|
+
# For method calls like "motor1.spin", extract the method name
|
233
|
+
obj_name, method_name = function_name.split('.', 1)
|
234
|
+
|
235
|
+
# First try to get the method signature directly
|
236
|
+
signature = registry_api.get_function(method_name)
|
237
|
+
|
238
|
+
# If that fails, try to get it as a method of a specific object type
|
239
|
+
if not signature:
|
240
|
+
# Try common object types
|
241
|
+
from ..types.objects import MOTOR, TIMER, BRAIN, CONTROLLER
|
242
|
+
for obj_type in [MOTOR, TIMER, BRAIN, CONTROLLER]:
|
243
|
+
method_sig = registry_api.get_method(obj_type, method_name)
|
244
|
+
if method_sig:
|
245
|
+
signature = method_sig
|
246
|
+
break
|
247
|
+
else:
|
248
|
+
# For direct function calls
|
249
|
+
signature = registry_api.get_function(function_name)
|
250
|
+
|
251
|
+
if not signature:
|
252
|
+
return VexAPICall(function, args, keywords, location)
|
253
|
+
|
254
|
+
# Create the appropriate node type based on the simulation category
|
255
|
+
if signature.category == SimulationCategory.MOTOR_CONTROL:
|
256
|
+
return MotorControl(function, args, keywords, location)
|
257
|
+
elif signature.category == SimulationCategory.SENSOR_READING:
|
258
|
+
return SensorReading(function, args, keywords, location)
|
259
|
+
elif signature.category == SimulationCategory.TIMING_CONTROL:
|
260
|
+
return TimingControl(function, args, keywords, location)
|
261
|
+
elif signature.category == SimulationCategory.DISPLAY_OUTPUT:
|
262
|
+
return DisplayOutput(function, args, keywords, location)
|
263
|
+
|
264
|
+
# Default case
|
265
|
+
return VexAPICall(function, args, keywords, location)
|
266
|
+
|
267
|
+
# Factory function to create VEX API calls from interfaces
|
268
|
+
def create_vex_api_call_from_interface(function_expr: IExpression,
|
269
|
+
args: List[IExpression],
|
270
|
+
kwargs: Dict[str, IExpression] = None,
|
271
|
+
location: Optional[SourceLocation] = None) -> VexAPICall:
|
272
|
+
"""Create a VEX API call from interface types."""
|
273
|
+
# Convert kwargs to KeywordArgument objects
|
274
|
+
keywords = []
|
275
|
+
if kwargs:
|
276
|
+
for name, value in kwargs.items():
|
277
|
+
keywords.append(KeywordArgument(name, value))
|
278
|
+
|
279
|
+
return create_vex_api_call(function_expr, args, keywords, location)
|
vex_ast/parser/README.md
CHANGED
@@ -1,47 +1,47 @@
|
|
1
|
-
Code Parser (vex_ast.parser)
|
2
|
-
|
3
|
-
This directory contains the components responsible for parsing VEX V5 Python source code and constructing the Abstract Syntax Tree (AST).
|
4
|
-
|
5
|
-
Purpose
|
6
|
-
|
7
|
-
The parser's role is to take raw source code (as a string or from a file) and transform it into the structured AST representation defined in vex_ast.ast. It handles syntax rules, identifies different code constructs, and builds the corresponding tree of nodes.
|
8
|
-
|
9
|
-
Key Components
|
10
|
-
|
11
|
-
Interfaces (interfaces.py):
|
12
|
-
|
13
|
-
IParser: Protocol defining the essential parse() method that all parser implementations must provide.
|
14
|
-
|
15
|
-
BaseParser: An abstract base class providing common functionality, such as integrating with the ErrorHandler.
|
16
|
-
|
17
|
-
Node Factory (factory.py):
|
18
|
-
|
19
|
-
NodeFactory: Implements the Factory pattern. It provides methods (create_identifier, create_binary_operation, etc.) to instantiate specific AST node types defined in vex_ast.ast. This decouples the main parsing logic from the details of node creation and allows for easier management of node instantiation, including attaching source locations and potentially handling errors during creation.
|
20
|
-
|
21
|
-
Python Parser (python_parser.py):
|
22
|
-
|
23
|
-
PythonParser: The concrete implementation of IParser. It leverages Python's built-in ast module to parse the input Python code into a standard Python AST.
|
24
|
-
|
25
|
-
It then traverses the Python AST, using the NodeFactory to convert the standard Python nodes (ast.Assign, ast.Call, ast.BinOp, etc.) into the corresponding custom VEX AST nodes (Assignment, FunctionCall, BinaryOperation, etc.).
|
26
|
-
|
27
|
-
Provides convenience functions parse_string and parse_file.
|
28
|
-
|
29
|
-
Workflow
|
30
|
-
|
31
|
-
The user calls parse_string or parse_file.
|
32
|
-
|
33
|
-
An instance of PythonParser is created with the source code and an optional ErrorHandler.
|
34
|
-
|
35
|
-
The PythonParser uses Python's ast.parse to generate a standard Python AST.
|
36
|
-
|
37
|
-
The PythonParser walks through the Python AST. For each Python ast node, it determines the corresponding VEX AST node type.
|
38
|
-
|
39
|
-
It calls the appropriate create_... method on its internal NodeFactory instance, passing the necessary components (sub-expressions, statements, names, values) converted recursively.
|
40
|
-
|
41
|
-
The NodeFactory creates the VEX AST node, potentially adding source location information extracted from the original Python ast node.
|
42
|
-
|
43
|
-
This process continues recursively until the entire Python AST is converted into the custom VEX AST (Program node).
|
44
|
-
|
45
|
-
Any syntax errors during Python parsing or unsupported constructs during conversion are reported via the ErrorHandler.
|
46
|
-
|
47
|
-
The final Program node representing the VEX AST is returned.
|
1
|
+
Code Parser (vex_ast.parser)
|
2
|
+
|
3
|
+
This directory contains the components responsible for parsing VEX V5 Python source code and constructing the Abstract Syntax Tree (AST).
|
4
|
+
|
5
|
+
Purpose
|
6
|
+
|
7
|
+
The parser's role is to take raw source code (as a string or from a file) and transform it into the structured AST representation defined in vex_ast.ast. It handles syntax rules, identifies different code constructs, and builds the corresponding tree of nodes.
|
8
|
+
|
9
|
+
Key Components
|
10
|
+
|
11
|
+
Interfaces (interfaces.py):
|
12
|
+
|
13
|
+
IParser: Protocol defining the essential parse() method that all parser implementations must provide.
|
14
|
+
|
15
|
+
BaseParser: An abstract base class providing common functionality, such as integrating with the ErrorHandler.
|
16
|
+
|
17
|
+
Node Factory (factory.py):
|
18
|
+
|
19
|
+
NodeFactory: Implements the Factory pattern. It provides methods (create_identifier, create_binary_operation, etc.) to instantiate specific AST node types defined in vex_ast.ast. This decouples the main parsing logic from the details of node creation and allows for easier management of node instantiation, including attaching source locations and potentially handling errors during creation.
|
20
|
+
|
21
|
+
Python Parser (python_parser.py):
|
22
|
+
|
23
|
+
PythonParser: The concrete implementation of IParser. It leverages Python's built-in ast module to parse the input Python code into a standard Python AST.
|
24
|
+
|
25
|
+
It then traverses the Python AST, using the NodeFactory to convert the standard Python nodes (ast.Assign, ast.Call, ast.BinOp, etc.) into the corresponding custom VEX AST nodes (Assignment, FunctionCall, BinaryOperation, etc.).
|
26
|
+
|
27
|
+
Provides convenience functions parse_string and parse_file.
|
28
|
+
|
29
|
+
Workflow
|
30
|
+
|
31
|
+
The user calls parse_string or parse_file.
|
32
|
+
|
33
|
+
An instance of PythonParser is created with the source code and an optional ErrorHandler.
|
34
|
+
|
35
|
+
The PythonParser uses Python's ast.parse to generate a standard Python AST.
|
36
|
+
|
37
|
+
The PythonParser walks through the Python AST. For each Python ast node, it determines the corresponding VEX AST node type.
|
38
|
+
|
39
|
+
It calls the appropriate create_... method on its internal NodeFactory instance, passing the necessary components (sub-expressions, statements, names, values) converted recursively.
|
40
|
+
|
41
|
+
The NodeFactory creates the VEX AST node, potentially adding source location information extracted from the original Python ast node.
|
42
|
+
|
43
|
+
This process continues recursively until the entire Python AST is converted into the custom VEX AST (Program node).
|
44
|
+
|
45
|
+
Any syntax errors during Python parsing or unsupported constructs during conversion are reported via the ErrorHandler.
|
46
|
+
|
47
|
+
The final Program node representing the VEX AST is returned.
|