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
@@ -0,0 +1,241 @@
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
+ self._signature = registry_api.get_function(method_name)
63
+ else:
64
+ # For direct function calls
65
+ self._signature = registry_api.get_function(function_name)
66
+
67
+ return self._signature
68
+
69
+ def validate(self) -> Tuple[bool, Optional[str]]:
70
+ """Validate this function call against the registry"""
71
+ if self._validation_error:
72
+ return False, self._validation_error
73
+
74
+ signature = self.resolve_signature()
75
+ if not signature:
76
+ function_name = self.get_function_name() or "<unknown>"
77
+ self._validation_error = f"Unknown VEX function: {function_name}"
78
+ return False, self._validation_error
79
+
80
+ # Convert args and kwargs to appropriate format
81
+ arg_values = [arg for arg in self.args]
82
+ kwarg_values = {kw.name: kw.value for kw in self.keywords or []}
83
+
84
+ # Validate against the signature
85
+ valid, error = signature.validate_arguments(arg_values, kwarg_values)
86
+ if not valid:
87
+ self._validation_error = error
88
+
89
+ return valid, error
90
+
91
+ def get_simulation_category(self) -> Optional[SimulationCategory]:
92
+ """Get the simulation category for this function call"""
93
+ signature = self.resolve_signature()
94
+ if signature:
95
+ return signature.category
96
+ return None
97
+
98
+ def get_call_type(self) -> VexAPICallType:
99
+ """Get the call type of this VEX API call."""
100
+ return self.call_type
101
+
102
+ def get_signature(self) -> Optional[VexFunctionSignature]:
103
+ """Get the function signature if resolved."""
104
+ return self._signature
105
+
106
+ def get_validation_error(self) -> Optional[str]:
107
+ """Get the validation error if any."""
108
+ return self._validation_error
109
+
110
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
111
+ return visitor.visit_vexapicall(self)
112
+
113
+ class MotorControl(VexAPICall):
114
+ """A VEX motor control function call."""
115
+
116
+ def __init__(self, function: IExpression, args: List[IExpression],
117
+ keywords: List[KeywordArgument] = None,
118
+ location: Optional[SourceLocation] = None):
119
+ super().__init__(function, args, keywords, location, VexAPICallType.MOTOR_CONTROL)
120
+
121
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
122
+ return visitor.visit_motorcontrol(self)
123
+
124
+ def get_motor_name(self) -> Optional[str]:
125
+ """Get the motor name if this is a method call on a motor object."""
126
+ function_name = self.get_function_name()
127
+ if function_name and '.' in function_name:
128
+ return function_name.split('.', 1)[0]
129
+ return None
130
+
131
+ class SensorReading(VexAPICall):
132
+ """A VEX sensor reading function call."""
133
+
134
+ def __init__(self, function: IExpression, args: List[IExpression],
135
+ keywords: List[KeywordArgument] = None,
136
+ location: Optional[SourceLocation] = None):
137
+ super().__init__(function, args, keywords, location, VexAPICallType.SENSOR_READING)
138
+
139
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
140
+ return visitor.visit_sensorreading(self)
141
+
142
+ def get_sensor_name(self) -> Optional[str]:
143
+ """Get the sensor name if this is a method call on a sensor object."""
144
+ function_name = self.get_function_name()
145
+ if function_name and '.' in function_name:
146
+ return function_name.split('.', 1)[0]
147
+ return None
148
+
149
+ class TimingControl(VexAPICall):
150
+ """A VEX timing control function call."""
151
+
152
+ def __init__(self, function: IExpression, args: List[IExpression],
153
+ keywords: List[KeywordArgument] = None,
154
+ location: Optional[SourceLocation] = None):
155
+ super().__init__(function, args, keywords, location, VexAPICallType.TIMING_CONTROL)
156
+
157
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
158
+ return visitor.visit_timingcontrol(self)
159
+
160
+ def get_timing_method(self) -> Optional[str]:
161
+ """Get the timing method name."""
162
+ function_name = self.get_function_name()
163
+ if function_name and '.' in function_name:
164
+ return function_name.split('.', 1)[1]
165
+ return function_name
166
+
167
+ class DisplayOutput(VexAPICall):
168
+ """A VEX display output function call."""
169
+
170
+ def __init__(self, function: IExpression, args: List[IExpression],
171
+ keywords: List[KeywordArgument] = None,
172
+ location: Optional[SourceLocation] = None):
173
+ super().__init__(function, args, keywords, location, VexAPICallType.DISPLAY_OUTPUT)
174
+
175
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
176
+ return visitor.visit_displayoutput(self)
177
+
178
+ def get_display_method(self) -> Optional[str]:
179
+ """Get the display method name."""
180
+ function_name = self.get_function_name()
181
+ if function_name and '.' in function_name:
182
+ return function_name.split('.', 1)[1]
183
+ return function_name
184
+
185
+ def create_vex_api_call(function: IExpression, args: List[IExpression],
186
+ keywords: List[KeywordArgument] = None,
187
+ location: Optional[SourceLocation] = None) -> VexAPICall:
188
+ """Factory function to create the appropriate VEX API call node"""
189
+ # Determine the function name
190
+ function_name = None
191
+ if hasattr(function, 'name'):
192
+ function_name = function.name
193
+ elif hasattr(function, 'attribute') and hasattr(function, 'object'):
194
+ obj = function.object
195
+ attr = function.attribute
196
+ if hasattr(obj, 'name'):
197
+ function_name = f"{obj.name}.{attr}"
198
+
199
+ # If we can't determine the function name, return a generic VexAPICall
200
+ if not function_name:
201
+ return VexAPICall(function, args, keywords, location)
202
+
203
+ # Look up in the registry API to get the function category
204
+ signature = None
205
+ if '.' in function_name:
206
+ # For method calls like "motor1.spin", extract the method name
207
+ obj_name, method_name = function_name.split('.', 1)
208
+ signature = registry_api.get_function(method_name)
209
+ else:
210
+ # For direct function calls
211
+ signature = registry_api.get_function(function_name)
212
+
213
+ if not signature:
214
+ return VexAPICall(function, args, keywords, location)
215
+
216
+ # Create the appropriate node type based on the simulation category
217
+ if signature.category == SimulationCategory.MOTOR_CONTROL:
218
+ return MotorControl(function, args, keywords, location)
219
+ elif signature.category == SimulationCategory.SENSOR_READING:
220
+ return SensorReading(function, args, keywords, location)
221
+ elif signature.category == SimulationCategory.TIMING_CONTROL:
222
+ return TimingControl(function, args, keywords, location)
223
+ elif signature.category == SimulationCategory.DISPLAY_OUTPUT:
224
+ return DisplayOutput(function, args, keywords, location)
225
+
226
+ # Default case
227
+ return VexAPICall(function, args, keywords, location)
228
+
229
+ # Factory function to create VEX API calls from interfaces
230
+ def create_vex_api_call_from_interface(function_expr: IExpression,
231
+ args: List[IExpression],
232
+ kwargs: Dict[str, IExpression] = None,
233
+ location: Optional[SourceLocation] = None) -> VexAPICall:
234
+ """Create a VEX API call from interface types."""
235
+ # Convert kwargs to KeywordArgument objects
236
+ keywords = []
237
+ if kwargs:
238
+ for name, value in kwargs.items():
239
+ keywords.append(KeywordArgument(name, value))
240
+
241
+ return create_vex_api_call(function_expr, args, keywords, location)
File without changes
@@ -0,0 +1,179 @@
1
+ """Node factory for creating AST nodes (Factory Pattern)."""
2
+
3
+ from typing import Any, Dict, Optional, Type, Union, cast, List
4
+
5
+ from ..ast.core import Expression, Program, Statement
6
+ from ..ast.expressions import (
7
+ AttributeAccess, BinaryOperation, FunctionCall, Identifier, KeywordArgument,
8
+ UnaryOperation, VariableReference
9
+ )
10
+ from ..ast.literals import (
11
+ BooleanLiteral, NoneLiteral, NumberLiteral, StringLiteral
12
+ )
13
+ from ..ast.operators import Operator
14
+ from ..ast.statements import (
15
+ Assignment, BreakStatement, ContinueStatement, ExpressionStatement,
16
+ ForLoop, FunctionDefinition, IfStatement, ReturnStatement, WhileLoop, Argument
17
+ )
18
+ from ..ast.vex_nodes import (
19
+ DisplayOutput, MotorControl, SensorReading, TimingControl, VexAPICall
20
+ )
21
+ from ..utils.errors import ErrorHandler
22
+ from ..utils.source_location import SourceLocation
23
+
24
+ class NodeFactory:
25
+ """Factory for creating AST nodes."""
26
+
27
+ def __init__(self, error_handler: Optional[ErrorHandler] = None):
28
+ """Initialize with an optional error handler."""
29
+ self.error_handler = error_handler
30
+
31
+ # --- Literals ---
32
+
33
+ def create_number_literal(self, value: Union[int, float],
34
+ location: Optional[SourceLocation] = None) -> NumberLiteral:
35
+ """Create a number literal node."""
36
+ return NumberLiteral(value, location)
37
+
38
+ def create_string_literal(self, value: str,
39
+ location: Optional[SourceLocation] = None) -> StringLiteral:
40
+ """Create a string literal node."""
41
+ return StringLiteral(value, location)
42
+
43
+ def create_boolean_literal(self, value: bool,
44
+ location: Optional[SourceLocation] = None) -> BooleanLiteral:
45
+ """Create a boolean literal node."""
46
+ return BooleanLiteral(value, location)
47
+
48
+ def create_none_literal(self,
49
+ location: Optional[SourceLocation] = None) -> NoneLiteral:
50
+ """Create a None literal node."""
51
+ return NoneLiteral(location)
52
+
53
+ # --- Expressions ---
54
+
55
+ def create_identifier(self, name: str,
56
+ location: Optional[SourceLocation] = None) -> Identifier:
57
+ """Create an identifier node."""
58
+ return Identifier(name, location)
59
+
60
+ def create_variable_reference(self, identifier: Identifier,
61
+ location: Optional[SourceLocation] = None) -> VariableReference:
62
+ """Create a variable reference node."""
63
+ return VariableReference(identifier, location)
64
+
65
+ def create_attribute_access(self, object_expr: Expression, attribute: str,
66
+ location: Optional[SourceLocation] = None) -> AttributeAccess:
67
+ """Create an attribute access node."""
68
+ return AttributeAccess(object_expr, attribute, location)
69
+
70
+ def create_binary_operation(self, left: Expression, op: Operator, right: Expression,
71
+ location: Optional[SourceLocation] = None) -> BinaryOperation:
72
+ """Create a binary operation node."""
73
+ return BinaryOperation(left, op, right, location)
74
+
75
+ def create_unary_operation(self, op: Operator, operand: Expression,
76
+ location: Optional[SourceLocation] = None) -> UnaryOperation:
77
+ """Create a unary operation node."""
78
+ return UnaryOperation(op, operand, location)
79
+
80
+ def create_function_call(self, function: Expression, args: List[Expression] = None,
81
+ keywords: List[KeywordArgument] = None,
82
+ location: Optional[SourceLocation] = None) -> FunctionCall:
83
+ """Create a function call node."""
84
+ return FunctionCall(function, args or [], keywords or [], location)
85
+
86
+ def create_keyword_argument(self, name: str, value: Expression,
87
+ location: Optional[SourceLocation] = None) -> KeywordArgument:
88
+ """Create a keyword argument node."""
89
+ return KeywordArgument(name, value, location)
90
+
91
+ # --- Statements ---
92
+
93
+ def create_expression_statement(self, expression: Expression,
94
+ location: Optional[SourceLocation] = None) -> ExpressionStatement:
95
+ """Create an expression statement node."""
96
+ return ExpressionStatement(expression, location)
97
+
98
+ def create_assignment(self, target: Expression, value: Expression,
99
+ location: Optional[SourceLocation] = None) -> Assignment:
100
+ """Create an assignment statement node."""
101
+ return Assignment(target, value, location)
102
+
103
+ def create_if_statement(self, test: Expression, body: List[Statement],
104
+ orelse: Optional[Union[List[Statement], IfStatement]] = None,
105
+ location: Optional[SourceLocation] = None) -> IfStatement:
106
+ """Create an if statement node."""
107
+ return IfStatement(test, body, orelse, location)
108
+
109
+ def create_while_loop(self, test: Expression, body: List[Statement],
110
+ location: Optional[SourceLocation] = None) -> WhileLoop:
111
+ """Create a while loop node."""
112
+ return WhileLoop(test, body, location)
113
+
114
+ def create_for_loop(self, target: Expression, iterable: Expression,
115
+ body: List[Statement],
116
+ location: Optional[SourceLocation] = None) -> ForLoop:
117
+ """Create a for loop node."""
118
+ return ForLoop(target, iterable, body, location)
119
+
120
+ def create_function_definition(self, name: str, args: List[Argument], body: List[Statement],
121
+ return_annotation: Optional[Expression] = None,
122
+ location: Optional[SourceLocation] = None) -> FunctionDefinition:
123
+ """Create a function definition node."""
124
+ return FunctionDefinition(name, args, body, return_annotation, location)
125
+
126
+ def create_return_statement(self, value: Optional[Expression] = None,
127
+ location: Optional[SourceLocation] = None) -> ReturnStatement:
128
+ """Create a return statement node."""
129
+ return ReturnStatement(value, location)
130
+
131
+ def create_break_statement(self,
132
+ location: Optional[SourceLocation] = None) -> BreakStatement:
133
+ """Create a break statement node."""
134
+ return BreakStatement(location)
135
+
136
+ def create_continue_statement(self,
137
+ location: Optional[SourceLocation] = None) -> ContinueStatement:
138
+ """Create a continue statement node."""
139
+ return ContinueStatement(location)
140
+
141
+ # --- VEX-specific Nodes ---
142
+
143
+ def create_vex_api_call(self, function: Expression, args: List[Expression],
144
+ keywords: List[KeywordArgument] = None,
145
+ location: Optional[SourceLocation] = None) -> VexAPICall:
146
+ """Create a VEX API call node."""
147
+ return VexAPICall(function, args, keywords or [], location)
148
+
149
+ def create_motor_control(self, function: Expression, args: List[Expression],
150
+ keywords: List[KeywordArgument] = None,
151
+ location: Optional[SourceLocation] = None) -> MotorControl:
152
+ """Create a motor control node."""
153
+ return MotorControl(function, args, keywords or [], location)
154
+
155
+ def create_sensor_reading(self, function: Expression, args: List[Expression],
156
+ keywords: List[KeywordArgument] = None,
157
+ location: Optional[SourceLocation] = None) -> SensorReading:
158
+ """Create a sensor reading node."""
159
+ return SensorReading(function, args, keywords or [], location)
160
+
161
+ def create_timing_control(self, function: Expression, args: List[Expression],
162
+ keywords: List[KeywordArgument] = None,
163
+ location: Optional[SourceLocation] = None) -> TimingControl:
164
+ """Create a timing control node."""
165
+ return TimingControl(function, args, keywords or [], location)
166
+
167
+ def create_display_output(self, function: Expression, args: List[Expression],
168
+ keywords: List[KeywordArgument] = None,
169
+ location: Optional[SourceLocation] = None) -> DisplayOutput:
170
+ """Create a display output node."""
171
+ return DisplayOutput(function, args, keywords or [], location)
172
+
173
+ def create_program(self, body: List[Statement],
174
+ location: Optional[SourceLocation] = None) -> Program:
175
+ """Create a program node."""
176
+ return Program(body, location)
177
+
178
+ # Global factory instance for simple use cases
179
+ default_factory = NodeFactory()
@@ -0,0 +1,35 @@
1
+ """Parser interfaces and protocols."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Protocol, Optional
5
+
6
+ from ..ast.core import Program
7
+ from ..utils.errors import ErrorHandler
8
+
9
+ class IParser(Protocol):
10
+ """Protocol for parser implementations."""
11
+
12
+ def parse(self) -> Program:
13
+ """Parse the input and return an AST."""
14
+ ...
15
+
16
+ @property
17
+ def error_handler(self) -> Optional[ErrorHandler]:
18
+ """Get the parser's error handler."""
19
+ ...
20
+
21
+ class BaseParser(ABC):
22
+ """Abstract base class for parsers."""
23
+
24
+ def __init__(self, error_handler: Optional[ErrorHandler] = None):
25
+ self._error_handler = error_handler or ErrorHandler()
26
+
27
+ @property
28
+ def error_handler(self) -> ErrorHandler:
29
+ """Get the parser's error handler."""
30
+ return self._error_handler
31
+
32
+ @abstractmethod
33
+ def parse(self) -> Program:
34
+ """Parse the input and return an AST."""
35
+ pass