vex-ast 0.2.3__py3-none-any.whl → 0.2.4__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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  from typing import Dict, List, Optional, Union, cast, Any
4
4
 
5
- from .interfaces import IAstNode, IExpression, IVisitor, T_VisitorResult, IIdentifier, IFunctionCall
5
+ from .interfaces import IAstNode, IExpression, IVisitor, T_VisitorResult, IIdentifier, IFunctionCall, IConditionalExpression
6
6
  from .core import Expression
7
7
  from .operators import Operator
8
8
  from ..utils.source_location import SourceLocation
@@ -173,6 +173,49 @@ class KeywordArgument(Expression):
173
173
  """Get the keyword value."""
174
174
  return self.value
175
175
 
176
+ class ConditionalExpression(Expression, IConditionalExpression):
177
+ """A conditional expression (ternary operator, e.g., a if condition else b)."""
178
+
179
+ _fields = ('condition', 'true_expr', 'false_expr')
180
+
181
+ def __init__(self, condition: IExpression, true_expr: IExpression, false_expr: IExpression,
182
+ location: Optional[SourceLocation] = None):
183
+ super().__init__(location)
184
+ self.condition = condition
185
+ self.true_expr = true_expr
186
+ self.false_expr = false_expr
187
+
188
+ # Set parent references
189
+ if isinstance(condition, Expression):
190
+ condition.set_parent(self)
191
+ if isinstance(true_expr, Expression):
192
+ true_expr.set_parent(self)
193
+ if isinstance(false_expr, Expression):
194
+ false_expr.set_parent(self)
195
+
196
+ def get_children(self) -> List[IAstNode]:
197
+ """Get child nodes."""
198
+ return [
199
+ cast(IAstNode, self.condition),
200
+ cast(IAstNode, self.true_expr),
201
+ cast(IAstNode, self.false_expr)
202
+ ]
203
+
204
+ def accept(self, visitor: IVisitor[T_VisitorResult]) -> T_VisitorResult:
205
+ return visitor.visit_conditionalexpression(self)
206
+
207
+ def get_condition(self) -> IExpression:
208
+ """Get the condition expression."""
209
+ return self.condition
210
+
211
+ def get_true_expression(self) -> IExpression:
212
+ """Get the expression to evaluate if condition is true."""
213
+ return self.true_expr
214
+
215
+ def get_false_expression(self) -> IExpression:
216
+ """Get the expression to evaluate if condition is false."""
217
+ return self.false_expr
218
+
176
219
  class FunctionCall(Expression, IFunctionCall):
177
220
  """A function call."""
178
221
 
vex_ast/ast/interfaces.py CHANGED
@@ -84,6 +84,22 @@ class IIdentifier(IExpression, Protocol):
84
84
  """Get the identifier name."""
85
85
  ...
86
86
 
87
+ @runtime_checkable
88
+ class IConditionalExpression(IExpression, Protocol):
89
+ """Protocol for conditional expression (ternary operator) nodes."""
90
+
91
+ def get_condition(self) -> IExpression:
92
+ """Get the condition expression."""
93
+ ...
94
+
95
+ def get_true_expression(self) -> IExpression:
96
+ """Get the expression to evaluate if condition is true."""
97
+ ...
98
+
99
+ def get_false_expression(self) -> IExpression:
100
+ """Get the expression to evaluate if condition is false."""
101
+ ...
102
+
87
103
  @runtime_checkable
88
104
  class IFunctionCall(IExpression, Protocol):
89
105
  """Protocol for function call nodes."""
vex_ast/parser/factory.py CHANGED
@@ -4,7 +4,7 @@ from typing import Any, Dict, Optional, Type, Union, cast, List
4
4
 
5
5
  from ..ast.core import Expression, Program, Statement
6
6
  from ..ast.expressions import (
7
- AttributeAccess, BinaryOperation, FunctionCall, Identifier, KeywordArgument,
7
+ AttributeAccess, BinaryOperation, ConditionalExpression, FunctionCall, Identifier, KeywordArgument,
8
8
  UnaryOperation, VariableReference
9
9
  )
10
10
  from ..ast.literals import (
@@ -77,6 +77,11 @@ class NodeFactory:
77
77
  """Create a unary operation node."""
78
78
  return UnaryOperation(op, operand, location)
79
79
 
80
+ def create_conditional_expression(self, condition: Expression, true_expr: Expression, false_expr: Expression,
81
+ location: Optional[SourceLocation] = None) -> ConditionalExpression:
82
+ """Create a conditional expression (ternary operator) node."""
83
+ return ConditionalExpression(condition, true_expr, false_expr, location)
84
+
80
85
  def create_function_call(self, function: Expression, args: List[Expression] = None,
81
86
  keywords: List[KeywordArgument] = None,
82
87
  location: Optional[SourceLocation] = None) -> FunctionCall:
@@ -294,21 +294,12 @@ class PythonParser(BaseParser):
294
294
  op_name = op_type.__name__
295
295
 
296
296
  op_map = {
297
- 'And': 'and',
298
- 'Or': 'or'
297
+ 'And': Operator.LOGICAL_AND,
298
+ 'Or': Operator.LOGICAL_OR
299
299
  }
300
300
 
301
301
  if op_name in op_map:
302
- op_str = op_map[op_name]
303
- vex_op = PYTHON_COMP_OP_MAP.get(op_str)
304
-
305
- if not vex_op:
306
- self.error_handler.add_error(
307
- ErrorType.PARSER_ERROR,
308
- f"Unsupported boolean operator: {op_name}",
309
- loc
310
- )
311
- vex_op = Operator.LOGICAL_AND # Fallback
302
+ vex_op = op_map[op_name]
312
303
  else:
313
304
  self.error_handler.add_error(
314
305
  ErrorType.PARSER_ERROR,
@@ -329,6 +320,15 @@ class PythonParser(BaseParser):
329
320
 
330
321
  return result
331
322
 
323
+ # Conditional expressions (ternary operators)
324
+ elif isinstance(node, ast.IfExp):
325
+ loc = self._get_location(node)
326
+ test = self._convert_expression(node.test)
327
+ body = self._convert_expression(node.body)
328
+ orelse = self._convert_expression(node.orelse)
329
+
330
+ return self.factory.create_conditional_expression(test, body, orelse, loc)
331
+
332
332
  # List literals
333
333
  elif isinstance(node, ast.List) or isinstance(node, ast.Tuple):
334
334
  # We don't have a dedicated list/tuple node, so use function call
@@ -607,22 +607,77 @@ class PythonParser(BaseParser):
607
607
  self._get_location(node)
608
608
  )
609
609
 
610
- # Import statements - not fully supported yet
611
- elif isinstance(node, (ast.Import, ast.ImportFrom)):
610
+ # Import statements
611
+ elif isinstance(node, ast.Import):
612
612
  loc = self._get_location(node)
613
- self.error_handler.add_error(
614
- ErrorType.PARSER_ERROR,
615
- "Import statements are not fully supported",
616
- loc
617
- )
618
- # Create a placeholder expression statement
619
- return self.factory.create_expression_statement(
620
- self.factory.create_identifier(
621
- f"<import:{getattr(node, 'names', [])}>",
613
+ # Create a list of assignments for each imported name
614
+ statements = []
615
+
616
+ for name in node.names:
617
+ # Create an identifier for the module
618
+ module_name = name.name
619
+ as_name = name.asname or module_name
620
+
621
+ # Create an assignment: as_name = module_name
622
+ target = self.factory.create_identifier(as_name, loc)
623
+ value = self.factory.create_identifier(f"<import:{module_name}>", loc)
624
+
625
+ statements.append(self.factory.create_assignment(target, value, loc))
626
+
627
+ # If there's only one statement, return it
628
+ if len(statements) == 1:
629
+ return statements[0]
630
+
631
+ # Otherwise, return the first one and add a warning
632
+ if len(statements) > 1:
633
+ self.error_handler.add_error(
634
+ ErrorType.PARSER_ERROR,
635
+ "Multiple imports in a single statement are not fully supported",
622
636
  loc
623
- ),
624
- loc
625
- )
637
+ )
638
+
639
+ return statements[0]
640
+
641
+ # Import from statements
642
+ elif isinstance(node, ast.ImportFrom):
643
+ loc = self._get_location(node)
644
+ module_name = node.module or ""
645
+
646
+ # Special case for "from vex import *"
647
+ if module_name == "vex" and any(name.name == "*" for name in node.names):
648
+ # Create a special identifier that represents "from vex import *"
649
+ return self.factory.create_expression_statement(
650
+ self.factory.create_identifier("<import:vex:*>", loc),
651
+ loc
652
+ )
653
+
654
+ # For other import from statements, create assignments
655
+ statements = []
656
+
657
+ for name in node.names:
658
+ # Create an identifier for the imported name
659
+ imported_name = name.name
660
+ as_name = name.asname or imported_name
661
+
662
+ # Create an assignment: as_name = module_name.imported_name
663
+ target = self.factory.create_identifier(as_name, loc)
664
+ value = self.factory.create_identifier(f"<import:{module_name}.{imported_name}>", loc)
665
+
666
+ statements.append(self.factory.create_assignment(target, value, loc))
667
+
668
+ # If there's only one statement, return it
669
+ if len(statements) == 1:
670
+ return statements[0]
671
+
672
+ # Otherwise, return the first one and add a warning
673
+ if len(statements) > 1:
674
+ self.error_handler.add_error(
675
+ ErrorType.PARSER_ERROR,
676
+ "Multiple imports in a single statement are not fully supported",
677
+ loc
678
+ )
679
+
680
+ return statements[0]
626
681
 
627
682
  # Class definitions - not supported yet
628
683
  elif isinstance(node, ast.ClassDef):
@@ -24,6 +24,11 @@ class VexFunctionParameter:
24
24
  self.description = description
25
25
  self.is_optional = default_value is not None
26
26
 
27
+ @property
28
+ def optional(self) -> bool:
29
+ """Alias for is_optional for compatibility."""
30
+ return self.is_optional
31
+
27
32
  def __str__(self) -> str:
28
33
  mode_str = ""
29
34
  if self.mode == ParameterMode.REFERENCE:
vex_ast/visitors/base.py CHANGED
@@ -47,6 +47,9 @@ class AstVisitor(Generic[T_VisitorResult], ABC):
47
47
  def visit_unaryoperation(self, node: Any) -> T_VisitorResult:
48
48
  return self.generic_visit(node)
49
49
 
50
+ def visit_conditionalexpression(self, node: Any) -> T_VisitorResult:
51
+ return self.generic_visit(node)
52
+
50
53
  def visit_functioncall(self, node: Any) -> T_VisitorResult:
51
54
  return self.generic_visit(node)
52
55
 
@@ -127,4 +130,4 @@ class TypedVisitorMixin:
127
130
  method_name = self.node_type_to_method_name(type(node))
128
131
  if hasattr(self, method_name):
129
132
  return getattr(self, method_name)(node)
130
- return self.generic_visit(node)
133
+ return self.generic_visit(node)
@@ -126,6 +126,7 @@ class PrintVisitor(AstVisitor[str]):
126
126
  visit_variablereference = generic_visit
127
127
  visit_binaryoperation = generic_visit
128
128
  visit_unaryoperation = generic_visit
129
+ visit_conditionalexpression = generic_visit
129
130
  visit_functioncall = generic_visit
130
131
  visit_keywordargument = generic_visit
131
132
  visit_expressionstatement = generic_visit
@@ -142,4 +143,4 @@ class PrintVisitor(AstVisitor[str]):
142
143
  visit_motorcontrol = generic_visit
143
144
  visit_sensorreading = generic_visit
144
145
  visit_timingcontrol = generic_visit
145
- visit_displayoutput = generic_visit
146
+ visit_displayoutput = generic_visit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vex_ast
3
- Version: 0.2.3
3
+ Version: 0.2.4
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,8 +4,8 @@ vex_ast/__init__.py,sha256=0xxA3UL3UMPB1Vlf1jL99bNUnvm41yJEoG5sMeylptY,1757
4
4
  vex_ast/ast/README.md,sha256=7IQDHEXmKyJ8zfJNwa3pMGTDZKVPMFD0YeHT06ATRyM,3205
5
5
  vex_ast/ast/__init__.py,sha256=_KT8R_WG9_DLvPlDhVCNWz-yw0OtdoKxxWZYbtloCzU,2434
6
6
  vex_ast/ast/core.py,sha256=q-15q5VfGIVPAmFsSiWeFAZ55MPsXfCRa21rrt2hmlM,2670
7
- vex_ast/ast/expressions.py,sha256=b0cDs239wvygCIL9s0W8uuyuPeMZz3DwxQ0wmBta_XM,7758
8
- vex_ast/ast/interfaces.py,sha256=DXKdAzts0EBWjCRgYkz_kVSMcYbNIyPdwYpTr7c6P2A,5928
7
+ vex_ast/ast/expressions.py,sha256=FGGWyNpJynnXqw0W_M08pGwa8wplF0oucbqk4x2W7SI,9404
8
+ vex_ast/ast/interfaces.py,sha256=KR_3jM5J97vQb6O5cgDzBi7IIQHjr2uXTobaVCT6RSg,6448
9
9
  vex_ast/ast/literals.py,sha256=PXbOH5Y2fxEngWk_FOiFsx3PC2SEqcx0bcfOGJ3SdbI,2618
10
10
  vex_ast/ast/navigator.py,sha256=9DaVXrknBbBr4omAAMHQnZL9Wpj5wjtoCS6_lni8MYM,7529
11
11
  vex_ast/ast/operators.py,sha256=I-yWvhsrz-OxmBZs5zIss_GTZF5S-nwcSmIzvAVtddM,3160
@@ -14,9 +14,9 @@ vex_ast/ast/validators.py,sha256=ySpi5H0XRdGXXMFAfmuVEyc3Q9mMXhubZAkP4N8NL3c,471
14
14
  vex_ast/ast/vex_nodes.py,sha256=8Wq9vl8Mp70E3fxEut9tB6WZE1z0Q_i5V0qh_5Zoeag,11981
15
15
  vex_ast/parser/README.md,sha256=P1qq_97skpgluLDpNu9V_pAdkuF9StjkzOEXJJYpEuM,2565
16
16
  vex_ast/parser/__init__.py,sha256=LGHnFm3UzR4Nw7royGH3c_2RqeY66y8O6DdXHbm9yL4,504
17
- vex_ast/parser/factory.py,sha256=ryTs8dQej8GbATyc7H4NUUISL1Bq65eIjyq84HaFTlo,9098
17
+ vex_ast/parser/factory.py,sha256=tcEYSgMNrjqhFDx3tYX22a68Qsxv3fcQpefki_tB8lE,9498
18
18
  vex_ast/parser/interfaces.py,sha256=Ttc0bD_5X420ZCT9MLUf_wE1aZLWLkaJRQqCwBy9wIs,956
19
- vex_ast/parser/python_parser.py,sha256=EHcro2NqVLScOBBG0XuWUMiT7Dmqep4M9scGOxpJoYw,29947
19
+ vex_ast/parser/python_parser.py,sha256=c36vfmzZvGGNDdi6g4kwQnESaKxVF8cSpaRoGVp_lBo,32428
20
20
  vex_ast/parser/strategies.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  vex_ast/registry/README.md,sha256=J8xEXEscEZhWa6wmn05kjE395AjrV7p2h7nq4SXI5uE,1535
22
22
  vex_ast/registry/__init__.py,sha256=dXT8OS7uLyBSdQKZDWrtbANzgSWcg9HUQDA1EaLOvjQ,1188
@@ -24,7 +24,7 @@ vex_ast/registry/api.py,sha256=yyW-TsCIULTzePAoyMvEqD_RNlkFSb4PLpiSkw6ueQk,5460
24
24
  vex_ast/registry/categories.py,sha256=y5tnCzHQ9PPg9hOzOMgyE1ZAhcsjgeFSrgqdJrdqHEg,4885
25
25
  vex_ast/registry/language_map.py,sha256=eekN3k-K9g90GFi4--If3nfJCyarUJxDWDCDvLm7mdU,2656
26
26
  vex_ast/registry/registry.py,sha256=gvsQKyeXWp84T2uDB4O_WQgKCeq8W9udl8TD5MnPZzc,6661
27
- vex_ast/registry/signature.py,sha256=_Lde3bteVVdYG31fN4v62UViWSVyxi4U3mYT1-9b4A0,7881
27
+ vex_ast/registry/signature.py,sha256=xMMay6zycjf4a0XMVJuyjqZxTuK6X2bLeyW4HAS0suQ,8019
28
28
  vex_ast/registry/simulation_behavior.py,sha256=WwkVKae0owtv3WTrfnVH93rpsh9lbqJ_RSAVmpIVA_k,313
29
29
  vex_ast/registry/validation.py,sha256=xq5UjSWYAcRxjxsCGg3WU5RtPN-DB-JAqKqDNGvnJGk,1973
30
30
  vex_ast/registry/functions/__init__.py,sha256=X9aw0-Y9Q6Gcx01zepP8G_20ybfwC-LDPQfkK2RDOrE,207
@@ -53,11 +53,11 @@ vex_ast/utils/type_definitions.py,sha256=2rB85B1vZL1GLWItBuJJpyr-vmp3W346EMMX2TP
53
53
  vex_ast/visitors/README.md,sha256=BKDj8LMuBlCrzgSSLmCkbET7WIqgxe-oCkqQbqXekrE,2725
54
54
  vex_ast/visitors/__init__.py,sha256=cndW3yp0Mu9w5vzwqqS2N5EqS0Ioh0V5UCDDrwCO0_Q,492
55
55
  vex_ast/visitors/analyzer.py,sha256=cBT5PRbtfQ_Dt1qiHN-wdO5KnC2yl-Ul5RasXg9geHY,3668
56
- vex_ast/visitors/base.py,sha256=Ph0taTIVMHV_QjXfDd0ipZnCXVJErkyRtPpfwfzo-kY,4859
57
- vex_ast/visitors/printer.py,sha256=CUY_73hxm7MoC-63JeIXaVYnZ8QqtfMqbek2R2HMEmE,5411
56
+ vex_ast/visitors/base.py,sha256=fLeMuIXJEHsag17PDM33he79x7Mp4V0wgtnF_8LuFpc,4978
57
+ vex_ast/visitors/printer.py,sha256=kofemEXvGBXEm1wy57R-Pgj2QBnSp_lNjFk3p2hNU2o,5460
58
58
  vex_ast/visitors/transformer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- vex_ast-0.2.3.dist-info/licenses/LICENSE,sha256=IOSlfCuxGv4OAg421BRDKVi16RZ7-5kCMJ4B16r4kuc,69
60
- vex_ast-0.2.3.dist-info/METADATA,sha256=2TsjEVVuPN94XQ8QOW5bHItBbFh3IAvn1uuCoEJe8nQ,5295
61
- vex_ast-0.2.3.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
62
- vex_ast-0.2.3.dist-info/top_level.txt,sha256=MoZGrpKgNUDiqL9gWp4q3wMw3q93XPEEjmBNPJQcNAs,8
63
- vex_ast-0.2.3.dist-info/RECORD,,
59
+ vex_ast-0.2.4.dist-info/licenses/LICENSE,sha256=IOSlfCuxGv4OAg421BRDKVi16RZ7-5kCMJ4B16r4kuc,69
60
+ vex_ast-0.2.4.dist-info/METADATA,sha256=_cjCd-H6a70oJPJ8X-P_m7NiAwikUQiAX2aLRUDDfFo,5295
61
+ vex_ast-0.2.4.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
62
+ vex_ast-0.2.4.dist-info/top_level.txt,sha256=MoZGrpKgNUDiqL9gWp4q3wMw3q93XPEEjmBNPJQcNAs,8
63
+ vex_ast-0.2.4.dist-info/RECORD,,