vex-ast 0.2.2__tar.gz → 0.2.3__tar.gz

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 (83) hide show
  1. {vex_ast-0.2.2/vex_ast.egg-info → vex_ast-0.2.3}/PKG-INFO +1 -1
  2. {vex_ast-0.2.2 → vex_ast-0.2.3}/pyproject.toml +1 -1
  3. {vex_ast-0.2.2 → vex_ast-0.2.3}/setup.py +1 -1
  4. vex_ast-0.2.3/tests/test_comprehensive_integration.py +338 -0
  5. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_parser.py +5 -4
  6. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/vex_nodes.py +15 -2
  7. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/parser/factory.py +7 -1
  8. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/parser/python_parser.py +7 -1
  9. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/signature.py +44 -1
  10. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/serialization/json_deserializer.py +13 -2
  11. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/serialization/schema.py +3 -4
  12. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/utils/errors.py +2 -2
  13. {vex_ast-0.2.2 → vex_ast-0.2.3/vex_ast.egg-info}/PKG-INFO +1 -1
  14. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast.egg-info/SOURCES.txt +1 -0
  15. {vex_ast-0.2.2 → vex_ast-0.2.3}/LICENSE +0 -0
  16. {vex_ast-0.2.2 → vex_ast-0.2.3}/MANIFEST.in +0 -0
  17. {vex_ast-0.2.2 → vex_ast-0.2.3}/README.md +0 -0
  18. {vex_ast-0.2.2 → vex_ast-0.2.3}/pytest.ini +0 -0
  19. {vex_ast-0.2.2 → vex_ast-0.2.3}/requirements.txt +0 -0
  20. {vex_ast-0.2.2 → vex_ast-0.2.3}/setup.cfg +0 -0
  21. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/conftest.py +0 -0
  22. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_core.py +0 -0
  23. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_integration.py +0 -0
  24. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_literals.py +0 -0
  25. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_navigator.py +0 -0
  26. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_registry.py +0 -0
  27. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_serialization.py +0 -0
  28. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_statements.py +0 -0
  29. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_vex_nodes.py +0 -0
  30. {vex_ast-0.2.2 → vex_ast-0.2.3}/tests/test_visitors.py +0 -0
  31. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/README.md +0 -0
  32. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/READMEAPI.md +0 -0
  33. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/__init__.py +0 -0
  34. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/README.md +0 -0
  35. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/__init__.py +0 -0
  36. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/core.py +0 -0
  37. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/expressions.py +0 -0
  38. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/interfaces.py +0 -0
  39. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/literals.py +0 -0
  40. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/navigator.py +0 -0
  41. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/operators.py +0 -0
  42. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/statements.py +0 -0
  43. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/ast/validators.py +0 -0
  44. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/parser/README.md +0 -0
  45. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/parser/__init__.py +0 -0
  46. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/parser/interfaces.py +0 -0
  47. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/parser/strategies.py +0 -0
  48. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/README.md +0 -0
  49. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/__init__.py +0 -0
  50. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/api.py +0 -0
  51. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/categories.py +0 -0
  52. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/__init__.py +0 -0
  53. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/display.py +0 -0
  54. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/drivetrain.py +0 -0
  55. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/initialize.py +0 -0
  56. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/motor.py +0 -0
  57. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/sensors.py +0 -0
  58. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/functions/timing.py +0 -0
  59. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/language_map.py +0 -0
  60. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/registry.py +0 -0
  61. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/simulation_behavior.py +0 -0
  62. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/registry/validation.py +0 -0
  63. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/serialization/__init__.py +0 -0
  64. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/serialization/json_serializer.py +0 -0
  65. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/README.md +0 -0
  66. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/__init__.py +0 -0
  67. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/base.py +0 -0
  68. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/enums.py +0 -0
  69. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/objects.py +0 -0
  70. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/primitives.py +0 -0
  71. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/types/type_checker.py +0 -0
  72. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/utils/README.md +0 -0
  73. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/utils/__init__.py +0 -0
  74. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/utils/source_location.py +0 -0
  75. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/utils/type_definitions.py +0 -0
  76. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/visitors/README.md +0 -0
  77. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/visitors/__init__.py +0 -0
  78. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/visitors/analyzer.py +0 -0
  79. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/visitors/base.py +0 -0
  80. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/visitors/printer.py +0 -0
  81. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast/visitors/transformer.py +0 -0
  82. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast.egg-info/dependency_links.txt +0 -0
  83. {vex_ast-0.2.2 → vex_ast-0.2.3}/vex_ast.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vex_ast
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python package for generating Abstract Syntax Trees for VEX V5 code.
5
5
  Home-page: https://github.com/heartx2/vex_ast
6
6
  Author: Chaze
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vex_ast"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "A Python package for generating Abstract Syntax Trees for VEX V5 code."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='vex_ast',
5
- version='0.2.2',
5
+ version='0.2.3',
6
6
  description='A Python package for generating Abstract Syntax Trees for VEX V5 code.',
7
7
  long_description=open('README.md').read(),
8
8
  long_description_content_type='text/markdown',
@@ -0,0 +1,338 @@
1
+ """
2
+ Comprehensive integration tests for the vex_ast package.
3
+
4
+ These tests ensure that all API endpoints work together correctly in various scenarios.
5
+ """
6
+
7
+ import os
8
+ import json
9
+ import tempfile
10
+ import pytest
11
+ from typing import Dict, Any
12
+
13
+ from vex_ast import (
14
+ parse_string,
15
+ parse_file,
16
+ ErrorHandler,
17
+ VexSyntaxError,
18
+ VexAstError,
19
+ PrintVisitor,
20
+ NodeCounter,
21
+ VariableCollector,
22
+ create_navigator,
23
+ serialize_ast_to_dict,
24
+ serialize_ast_to_json,
25
+ deserialize_ast_from_dict,
26
+ deserialize_ast_from_json,
27
+ generate_ast_schema,
28
+ export_schema_to_file,
29
+ registry_api
30
+ )
31
+ from vex_ast.ast.vex_nodes import VexAPICall
32
+ from vex_ast.ast.validators import validate_vex_functions
33
+
34
+
35
+ class TestComprehensiveIntegration:
36
+ """Comprehensive integration tests for the vex_ast package."""
37
+
38
+ def test_parse_file_functionality(self):
39
+ """Test the parse_file functionality with a temporary file."""
40
+ # Create a temporary file with VEX code
41
+ with tempfile.NamedTemporaryFile(mode='w+', suffix='.py', delete=False) as temp_file:
42
+ temp_file.write("""
43
+ # Define a motor
44
+ motor1 = Motor(PORT1)
45
+
46
+ # Define a function
47
+ def move_forward(speed):
48
+ motor1.spin(FORWARD, speed, PERCENT)
49
+ wait(1, SECONDS)
50
+ motor1.stop()
51
+ return True
52
+
53
+ # Call the function
54
+ success = move_forward(50)
55
+ """)
56
+ temp_file_path = temp_file.name
57
+
58
+ try:
59
+ # Parse the file
60
+ ast = parse_file(temp_file_path)
61
+
62
+ # Verify the AST structure
63
+ assert ast is not None
64
+ assert len(ast.body) > 0
65
+
66
+ # Find function definitions
67
+ func_defs = [node for node in ast.body if node.__class__.__name__ == "FunctionDefinition"]
68
+ assert len(func_defs) == 1
69
+ assert func_defs[0].name == "move_forward"
70
+
71
+ # Test that visitors work with the parsed AST
72
+ printer = PrintVisitor()
73
+ result = printer.visit(ast)
74
+ assert isinstance(result, str)
75
+ assert len(result) > 0
76
+
77
+ counter = NodeCounter()
78
+ count = counter.visit(ast)
79
+ assert count > 10
80
+
81
+ collector = VariableCollector()
82
+ variables = collector.visit(ast)
83
+ assert "motor1" in variables
84
+ assert "speed" in variables
85
+
86
+ finally:
87
+ # Clean up the temporary file
88
+ os.unlink(temp_file_path)
89
+
90
+ def test_error_handling_integration(self):
91
+ """Test error handling integration with parsing and visitors."""
92
+ # Create an error handler that doesn't raise exceptions
93
+ error_handler = ErrorHandler(raise_on_error=False)
94
+
95
+ # Parse code with syntax errors
96
+ code_with_errors = """
97
+ def invalid_function(
98
+ # Missing closing parenthesis
99
+ print("This will cause an error")
100
+ """
101
+
102
+ # This should not raise an exception due to raise_on_error=False
103
+ ast = parse_string(code_with_errors, error_handler=error_handler)
104
+
105
+ # Verify that errors were collected
106
+ assert error_handler.has_errors()
107
+ errors = error_handler.get_errors()
108
+ assert len(errors) > 0
109
+
110
+ # Try with a different error handler that raises exceptions
111
+ error_handler_strict = ErrorHandler(raise_on_error=True)
112
+
113
+ # This should raise a VexSyntaxError
114
+ with pytest.raises(VexSyntaxError):
115
+ parse_string(code_with_errors, error_handler=error_handler_strict)
116
+
117
+ # Test error handling with valid code but invalid VEX API calls
118
+ invalid_vex_code = """
119
+ motor1 = Motor(PORT1)
120
+ # Invalid: wrong arguments to spin
121
+ motor1.spin(INVALID_DIRECTION, "not_a_number", "not_a_unit")
122
+ """
123
+
124
+ # Parse the code (should succeed syntactically)
125
+ ast = parse_string(invalid_vex_code)
126
+
127
+ # Validate VEX functions (should find errors)
128
+ validation_errors = validate_vex_functions(ast)
129
+ assert len(validation_errors) > 0
130
+
131
+ def test_end_to_end_workflow(self):
132
+ """Test an end-to-end workflow combining multiple API functions."""
133
+ # 1. Parse a complex program
134
+ code = """
135
+ # Initialize motors
136
+ left_motor = Motor(PORT1)
137
+ right_motor = Motor(PORT2)
138
+
139
+ # Define a function to drive forward
140
+ def drive_forward(speed, time_ms):
141
+ # Set both motors to the specified speed
142
+ left_motor.set_velocity(speed, PERCENT)
143
+ right_motor.set_velocity(speed, PERCENT)
144
+
145
+ # Start the motors
146
+ left_motor.spin(FORWARD)
147
+ right_motor.spin(FORWARD)
148
+
149
+ # Wait for the specified time
150
+ wait(time_ms, MSEC)
151
+
152
+ # Stop the motors
153
+ left_motor.stop()
154
+ right_motor.stop()
155
+
156
+ return True
157
+
158
+ # Define a function to turn
159
+ def turn(direction, speed, angle):
160
+ if direction == LEFT:
161
+ left_motor.set_velocity(-speed, PERCENT)
162
+ right_motor.set_velocity(speed, PERCENT)
163
+ else:
164
+ left_motor.set_velocity(speed, PERCENT)
165
+ right_motor.set_velocity(-speed, PERCENT)
166
+
167
+ left_motor.spin(FORWARD)
168
+ right_motor.spin(FORWARD)
169
+
170
+ # Wait until the robot has turned the specified angle
171
+ wait_until(gyro.rotation() >= angle)
172
+
173
+ left_motor.stop()
174
+ right_motor.stop()
175
+
176
+ return True
177
+
178
+ # Main program
179
+ def main():
180
+ # Display welcome message
181
+ brain.screen.print("Robot starting...")
182
+ wait(1, SECONDS)
183
+
184
+ # Drive forward at 50% speed for 2 seconds
185
+ success = drive_forward(50, 2000)
186
+
187
+ if success:
188
+ # Turn right 90 degrees
189
+ turn(RIGHT, 30, 90)
190
+
191
+ # Drive forward again
192
+ drive_forward(50, 1000)
193
+
194
+ brain.screen.print("Mission completed!")
195
+ else:
196
+ brain.screen.print("Drive failed!")
197
+
198
+ return 0
199
+ """
200
+
201
+ # 2. Parse the code
202
+ ast = parse_string(code)
203
+
204
+ # 3. Use a visitor to analyze the AST
205
+ counter = NodeCounter()
206
+ node_count = counter.visit(ast)
207
+ assert node_count > 50 # Complex program should have many nodes
208
+
209
+ # 4. Use the navigator to find specific nodes
210
+ navigator = create_navigator(ast)
211
+
212
+ # Find all function definitions
213
+ func_defs = navigator.find_function_definitions()
214
+ assert len(func_defs) == 3
215
+ func_names = [func.name for func in func_defs]
216
+ assert "drive_forward" in func_names
217
+ assert "turn" in func_names
218
+ assert "main" in func_names
219
+
220
+ # Find all VEX API calls
221
+ vex_calls = navigator.find_vex_api_calls()
222
+ assert len(vex_calls) > 5
223
+
224
+ # 5. Serialize the AST to JSON
225
+ json_str = serialize_ast_to_json(ast)
226
+ assert isinstance(json_str, str)
227
+
228
+ # 6. Deserialize back to an AST
229
+ deserialized_ast = deserialize_ast_from_json(json_str)
230
+
231
+ # 7. Verify the deserialized AST has the same structure
232
+ deserialized_func_defs = [node for node in deserialized_ast.body
233
+ if node.__class__.__name__ == "FunctionDefinition"]
234
+ assert len(deserialized_func_defs) == 3
235
+ deserialized_func_names = [func.name for func in deserialized_func_defs]
236
+ assert set(deserialized_func_names) == set(func_names)
237
+
238
+ # 8. Use a visitor on the deserialized AST
239
+ collector = VariableCollector()
240
+ variables = collector.visit(deserialized_ast)
241
+ assert "left_motor" in variables
242
+ assert "right_motor" in variables
243
+ assert "speed" in variables
244
+ assert "direction" in variables
245
+
246
+ def test_schema_export_functionality(self):
247
+ """Test the schema export functionality."""
248
+ # Generate the schema
249
+ schema = generate_ast_schema()
250
+ assert isinstance(schema, dict)
251
+
252
+ # Export the schema to a temporary file
253
+ with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as temp_file:
254
+ temp_file_path = temp_file.name
255
+
256
+ try:
257
+ # Export the schema
258
+ export_schema_to_file(schema, temp_file_path)
259
+
260
+ # Verify the file exists and contains valid JSON
261
+ assert os.path.exists(temp_file_path)
262
+
263
+ with open(temp_file_path, 'r') as f:
264
+ loaded_schema = json.load(f)
265
+
266
+ # Check that it's the same schema
267
+ assert loaded_schema["$schema"] == schema["$schema"]
268
+ assert "definitions" in loaded_schema
269
+ assert set(loaded_schema["definitions"].keys()) == set(schema["definitions"].keys())
270
+
271
+ finally:
272
+ # Clean up
273
+ if os.path.exists(temp_file_path):
274
+ os.unlink(temp_file_path)
275
+
276
+ def test_edge_cases(self):
277
+ """Test edge cases in parsing and serialization."""
278
+ # Test parsing an empty program
279
+ empty_ast = parse_string("")
280
+ assert empty_ast is not None
281
+ assert len(empty_ast.body) == 0
282
+
283
+ # Test parsing a program with only comments
284
+ comments_ast = parse_string("# This is a comment\n# Another comment")
285
+ assert comments_ast is not None
286
+ assert len(comments_ast.body) == 0
287
+
288
+ # Test parsing a program with unusual whitespace
289
+ whitespace_code = """
290
+
291
+
292
+ x = 42
293
+
294
+
295
+ y = x + 10
296
+
297
+
298
+ """
299
+ whitespace_ast = parse_string(whitespace_code)
300
+ assert whitespace_ast is not None
301
+ assert len(whitespace_ast.body) == 2
302
+
303
+ # Test serializing and deserializing an empty program
304
+ empty_json = serialize_ast_to_json(empty_ast)
305
+ deserialized_empty = deserialize_ast_from_json(empty_json)
306
+ assert deserialized_empty is not None
307
+ assert len(deserialized_empty.body) == 0
308
+
309
+ # Test with a program containing all types of literals
310
+ literals_code = """
311
+ # Number literals
312
+ int_val = 42
313
+ float_val = 3.14
314
+
315
+ # String literals
316
+ str_val = "Hello, world!"
317
+ str_val2 = 'Single quotes'
318
+
319
+ # Boolean literals
320
+ bool_val1 = True
321
+ bool_val2 = False
322
+
323
+ # None literal
324
+ none_val = None
325
+ """
326
+
327
+ literals_ast = parse_string(literals_code)
328
+ assert literals_ast is not None
329
+ assert len(literals_ast.body) > 0
330
+
331
+ # Serialize and deserialize
332
+ literals_json = serialize_ast_to_json(literals_ast)
333
+ deserialized_literals = deserialize_ast_from_json(literals_json)
334
+
335
+ # Verify the literals were preserved
336
+ collector = VariableCollector()
337
+ variables = collector.visit(deserialized_literals)
338
+ assert len(variables) == 0 # All are assignments, no references
@@ -58,9 +58,10 @@ class TestParser:
58
58
  code = "if x ==" # Incomplete if statement
59
59
  error_handler = ErrorHandler(raise_on_error=False)
60
60
 
61
- with pytest.raises(VexSyntaxError):
62
- parse_string(code, error_handler=error_handler)
63
-
61
+ # Should not raise an exception when raise_on_error=False
62
+ ast = parse_string(code, error_handler=error_handler)
63
+
64
+ # But should collect errors
64
65
  assert error_handler.has_errors()
65
66
  errors = error_handler.get_errors()
66
- assert len(errors) >= 1
67
+ assert len(errors) >= 1
@@ -91,8 +91,21 @@ class VexAPICall(FunctionCall):
91
91
  return False, self._validation_error
92
92
 
93
93
  # Convert args and kwargs to appropriate format
94
- arg_values = [arg for arg in self.args]
95
- kwarg_values = {kw.name: kw.value for kw in self.keywords or []}
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
96
109
 
97
110
  # Validate against the signature
98
111
  valid, error = signature.validate_arguments(arg_values, kwarg_values)
@@ -123,6 +123,12 @@ class NodeFactory:
123
123
  """Create a function definition node."""
124
124
  return FunctionDefinition(name, args, body, return_annotation, location)
125
125
 
126
+ def create_argument(self, name: str, annotation: Optional[Expression] = None,
127
+ default: Optional[Expression] = None,
128
+ location: Optional[SourceLocation] = None) -> Argument:
129
+ """Create an argument node."""
130
+ return Argument(name, annotation, default, location)
131
+
126
132
  def create_return_statement(self, value: Optional[Expression] = None,
127
133
  location: Optional[SourceLocation] = None) -> ReturnStatement:
128
134
  """Create a return statement node."""
@@ -176,4 +182,4 @@ class NodeFactory:
176
182
  return Program(body, location)
177
183
 
178
184
  # Global factory instance for simple use cases
179
- default_factory = NodeFactory()
185
+ default_factory = NodeFactory()
@@ -692,7 +692,13 @@ class PythonParser(BaseParser):
692
692
  f"Syntax error: {e.msg}",
693
693
  loc
694
694
  )
695
- raise VexSyntaxError(f"Syntax error: {e.msg}", loc) from e
695
+
696
+ # Only raise if the error handler is configured to do so
697
+ if self.error_handler._raise_on_error:
698
+ raise VexSyntaxError(f"Syntax error: {e.msg}", loc) from e
699
+
700
+ # Return an empty program if we're not raising
701
+ return self.factory.create_program([])
696
702
 
697
703
  except Exception as e:
698
704
  # Handle other parsing errors
@@ -138,6 +138,49 @@ class VexFunctionSignature:
138
138
  if param_name in kwargs:
139
139
  return False, f"Duplicate argument '{param_name}' for {self.name}"
140
140
 
141
- # TODO: Add type checking for arguments
141
+ # Type checking for arguments
142
+ from ..types.type_checker import type_checker
143
+ from ..types.enums import EnumType
144
+
145
+ # Check positional arguments
146
+ for i, arg in enumerate(args):
147
+ if i >= len(self.parameters):
148
+ break
149
+
150
+ param = self.parameters[i]
151
+ expected_type = param.type
152
+
153
+ # Handle string literals for enum types
154
+ if isinstance(expected_type, EnumType) and isinstance(arg, str):
155
+ if arg not in expected_type.values:
156
+ return False, f"Invalid enum value '{arg}' for parameter '{param.name}' in {self.name}"
157
+ continue
158
+
159
+ # Handle other types
160
+ if hasattr(arg, 'get_type'):
161
+ arg_type = arg.get_type()
162
+ if arg_type and not type_checker.is_compatible(arg_type, expected_type):
163
+ return False, f"Type mismatch for parameter '{param.name}' in {self.name}: expected {expected_type}, got {arg_type}"
164
+
165
+ # Check keyword arguments
166
+ for kwarg_name, kwarg_value in kwargs.items():
167
+ # Find the parameter
168
+ param = next((p for p in self.parameters if p.name == kwarg_name), None)
169
+ if not param:
170
+ continue # Already checked for unknown kwargs above
171
+
172
+ expected_type = param.type
173
+
174
+ # Handle string literals for enum types
175
+ if isinstance(expected_type, EnumType) and isinstance(kwarg_value, str):
176
+ if kwarg_value not in expected_type.values:
177
+ return False, f"Invalid enum value '{kwarg_value}' for parameter '{param.name}' in {self.name}"
178
+ continue
179
+
180
+ # Handle other types
181
+ if hasattr(kwarg_value, 'get_type'):
182
+ kwarg_type = kwarg_value.get_type()
183
+ if kwarg_type and not type_checker.is_compatible(kwarg_type, expected_type):
184
+ return False, f"Type mismatch for parameter '{param.name}' in {self.name}: expected {expected_type}, got {kwarg_type}"
142
185
 
143
186
  return True, None
@@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Type, Union, cast
10
10
  from ..ast.interfaces import IAstNode
11
11
  from ..parser.factory import NodeFactory
12
12
  from ..utils.source_location import SourceLocation
13
- from ..utils.errors import ErrorHandler
13
+ from ..utils.errors import ErrorHandler, ErrorType
14
14
 
15
15
 
16
16
  class DeserializationFactory:
@@ -153,6 +153,7 @@ class DeserializationFactory:
153
153
  "WhileLoop": self.node_factory.create_while_loop,
154
154
  "ForLoop": self.node_factory.create_for_loop,
155
155
  "FunctionDefinition": self.node_factory.create_function_definition,
156
+ "Argument": self.node_factory.create_argument,
156
157
  "ReturnStatement": self.node_factory.create_return_statement,
157
158
  "BreakStatement": self.node_factory.create_break_statement,
158
159
  "ContinueStatement": self.node_factory.create_continue_statement,
@@ -198,11 +199,21 @@ class DeserializationFactory:
198
199
  except TypeError as e:
199
200
  # If the factory method doesn't accept the kwargs, report an error
200
201
  if self.error_handler:
201
- self.error_handler.report_error(f"Failed to create {node_type}: {str(e)}")
202
+ self.error_handler.add_error(
203
+ error_type=ErrorType.INTERNAL_ERROR,
204
+ message=f"Failed to create {node_type}: {str(e)}"
205
+ )
202
206
  raise ValueError(f"Failed to deserialize {node_type}: {str(e)}")
203
207
 
204
208
  # Specific node creation methods for complex cases
205
209
 
210
+ def _create_attributeaccess(self, data: Dict[str, Any],
211
+ location: Optional[SourceLocation]) -> IAstNode:
212
+ """Create an AttributeAccess node from serialized data."""
213
+ object_expr = self._deserialize_value(data.get("object"))
214
+ attribute = data.get("attribute", "")
215
+ return self.node_factory.create_attribute_access(object_expr, attribute, location)
216
+
206
217
  def _create_program(self, data: Dict[str, Any],
207
218
  location: Optional[SourceLocation]) -> IAstNode:
208
219
  """Create a Program node from serialized data."""
@@ -453,16 +453,15 @@ def _generate_definitions() -> Dict[str, Any]:
453
453
  return definitions
454
454
 
455
455
 
456
- def export_schema_to_file(filepath: str, indent: int = 2) -> None:
456
+ def export_schema_to_file(schema: Dict[str, Any], filepath: str, indent: int = 2) -> None:
457
457
  """
458
- Save the generated schema to a file.
458
+ Save the schema to a file.
459
459
 
460
460
  Args:
461
+ schema: The schema to save
461
462
  filepath: The path to save the schema to
462
463
  indent: The indentation level for pretty-printing
463
464
  """
464
- schema = generate_ast_schema()
465
-
466
465
  # Ensure the directory exists
467
466
  os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
468
467
 
@@ -50,7 +50,7 @@ class ErrorObserver(Protocol[T_Error]):
50
50
  class ErrorHandler:
51
51
  """Manages error collection and notification."""
52
52
 
53
- def __init__(self, raise_on_error: bool = False):
53
+ def __init__(self, raise_on_error: bool = True):
54
54
  self._errors: List[Error] = []
55
55
  self._raise_on_error = raise_on_error
56
56
  self._observers: List[ErrorObserver] = []
@@ -109,4 +109,4 @@ class VexSyntaxError(VexAstError):
109
109
 
110
110
  def __init__(self, message: str, location: Optional[SourceLocation] = None):
111
111
  self.location = location
112
- super().__init__(message)
112
+ super().__init__(message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vex_ast
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python package for generating Abstract Syntax Trees for VEX V5 code.
5
5
  Home-page: https://github.com/heartx2/vex_ast
6
6
  Author: Chaze
@@ -6,6 +6,7 @@ pytest.ini
6
6
  requirements.txt
7
7
  setup.py
8
8
  tests/conftest.py
9
+ tests/test_comprehensive_integration.py
9
10
  tests/test_core.py
10
11
  tests/test_integration.py
11
12
  tests/test_literals.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes