machine-dialect 0.1.0a1__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 (268) hide show
  1. machine_dialect/__main__.py +667 -0
  2. machine_dialect/agent/__init__.py +5 -0
  3. machine_dialect/agent/agent.py +360 -0
  4. machine_dialect/ast/__init__.py +95 -0
  5. machine_dialect/ast/ast_node.py +35 -0
  6. machine_dialect/ast/call_expression.py +82 -0
  7. machine_dialect/ast/dict_extraction.py +60 -0
  8. machine_dialect/ast/expressions.py +439 -0
  9. machine_dialect/ast/literals.py +309 -0
  10. machine_dialect/ast/program.py +35 -0
  11. machine_dialect/ast/statements.py +1433 -0
  12. machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
  13. machine_dialect/ast/tests/test_boolean_literal.py +29 -0
  14. machine_dialect/ast/tests/test_collection_hir.py +138 -0
  15. machine_dialect/ast/tests/test_define_statement.py +142 -0
  16. machine_dialect/ast/tests/test_desugar.py +541 -0
  17. machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
  18. machine_dialect/cfg/__init__.py +6 -0
  19. machine_dialect/cfg/config.py +156 -0
  20. machine_dialect/cfg/examples.py +221 -0
  21. machine_dialect/cfg/generate_with_ai.py +187 -0
  22. machine_dialect/cfg/openai_generation.py +200 -0
  23. machine_dialect/cfg/parser.py +94 -0
  24. machine_dialect/cfg/tests/__init__.py +1 -0
  25. machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
  26. machine_dialect/cfg/tests/test_config.py +188 -0
  27. machine_dialect/cfg/tests/test_examples.py +391 -0
  28. machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
  29. machine_dialect/cfg/tests/test_openai_generation.py +256 -0
  30. machine_dialect/codegen/__init__.py +5 -0
  31. machine_dialect/codegen/bytecode_module.py +89 -0
  32. machine_dialect/codegen/bytecode_serializer.py +300 -0
  33. machine_dialect/codegen/opcodes.py +101 -0
  34. machine_dialect/codegen/register_codegen.py +1996 -0
  35. machine_dialect/codegen/symtab.py +208 -0
  36. machine_dialect/codegen/tests/__init__.py +1 -0
  37. machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
  38. machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
  39. machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
  40. machine_dialect/codegen/tests/test_symtab.py +418 -0
  41. machine_dialect/codegen/vm_serializer.py +621 -0
  42. machine_dialect/compiler/__init__.py +18 -0
  43. machine_dialect/compiler/compiler.py +197 -0
  44. machine_dialect/compiler/config.py +149 -0
  45. machine_dialect/compiler/context.py +149 -0
  46. machine_dialect/compiler/phases/__init__.py +19 -0
  47. machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
  48. machine_dialect/compiler/phases/codegen.py +40 -0
  49. machine_dialect/compiler/phases/hir_generation.py +39 -0
  50. machine_dialect/compiler/phases/mir_generation.py +86 -0
  51. machine_dialect/compiler/phases/optimization.py +110 -0
  52. machine_dialect/compiler/phases/parsing.py +39 -0
  53. machine_dialect/compiler/pipeline.py +143 -0
  54. machine_dialect/compiler/tests/__init__.py +1 -0
  55. machine_dialect/compiler/tests/test_compiler.py +568 -0
  56. machine_dialect/compiler/vm_runner.py +173 -0
  57. machine_dialect/errors/__init__.py +32 -0
  58. machine_dialect/errors/exceptions.py +369 -0
  59. machine_dialect/errors/messages.py +82 -0
  60. machine_dialect/errors/tests/__init__.py +0 -0
  61. machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
  62. machine_dialect/errors/tests/test_name_errors.py +118 -0
  63. machine_dialect/helpers/__init__.py +0 -0
  64. machine_dialect/helpers/stopwords.py +225 -0
  65. machine_dialect/helpers/validators.py +30 -0
  66. machine_dialect/lexer/__init__.py +9 -0
  67. machine_dialect/lexer/constants.py +23 -0
  68. machine_dialect/lexer/lexer.py +907 -0
  69. machine_dialect/lexer/tests/__init__.py +0 -0
  70. machine_dialect/lexer/tests/helpers.py +86 -0
  71. machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
  72. machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
  73. machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
  74. machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
  75. machine_dialect/lexer/tests/test_comments.py +200 -0
  76. machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
  77. machine_dialect/lexer/tests/test_lexer_position.py +113 -0
  78. machine_dialect/lexer/tests/test_list_tokens.py +282 -0
  79. machine_dialect/lexer/tests/test_stopwords.py +80 -0
  80. machine_dialect/lexer/tests/test_strict_equality.py +129 -0
  81. machine_dialect/lexer/tests/test_token.py +41 -0
  82. machine_dialect/lexer/tests/test_tokenization.py +294 -0
  83. machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
  84. machine_dialect/lexer/tests/test_url_literals.py +169 -0
  85. machine_dialect/lexer/tokens.py +487 -0
  86. machine_dialect/linter/__init__.py +10 -0
  87. machine_dialect/linter/__main__.py +144 -0
  88. machine_dialect/linter/linter.py +154 -0
  89. machine_dialect/linter/rules/__init__.py +8 -0
  90. machine_dialect/linter/rules/base.py +112 -0
  91. machine_dialect/linter/rules/statement_termination.py +99 -0
  92. machine_dialect/linter/tests/__init__.py +1 -0
  93. machine_dialect/linter/tests/mdrules/__init__.py +0 -0
  94. machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
  95. machine_dialect/linter/tests/test_linter.py +81 -0
  96. machine_dialect/linter/tests/test_rules.py +110 -0
  97. machine_dialect/linter/tests/test_violations.py +71 -0
  98. machine_dialect/linter/violations.py +51 -0
  99. machine_dialect/mir/__init__.py +69 -0
  100. machine_dialect/mir/analyses/__init__.py +20 -0
  101. machine_dialect/mir/analyses/alias_analysis.py +315 -0
  102. machine_dialect/mir/analyses/dominance_analysis.py +49 -0
  103. machine_dialect/mir/analyses/escape_analysis.py +286 -0
  104. machine_dialect/mir/analyses/loop_analysis.py +272 -0
  105. machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
  106. machine_dialect/mir/analyses/type_analysis.py +448 -0
  107. machine_dialect/mir/analyses/use_def_chains.py +232 -0
  108. machine_dialect/mir/basic_block.py +385 -0
  109. machine_dialect/mir/dataflow.py +445 -0
  110. machine_dialect/mir/debug_info.py +208 -0
  111. machine_dialect/mir/hir_to_mir.py +1738 -0
  112. machine_dialect/mir/mir_dumper.py +366 -0
  113. machine_dialect/mir/mir_function.py +167 -0
  114. machine_dialect/mir/mir_instructions.py +1877 -0
  115. machine_dialect/mir/mir_interpreter.py +556 -0
  116. machine_dialect/mir/mir_module.py +225 -0
  117. machine_dialect/mir/mir_printer.py +480 -0
  118. machine_dialect/mir/mir_transformer.py +410 -0
  119. machine_dialect/mir/mir_types.py +367 -0
  120. machine_dialect/mir/mir_validation.py +455 -0
  121. machine_dialect/mir/mir_values.py +268 -0
  122. machine_dialect/mir/optimization_config.py +233 -0
  123. machine_dialect/mir/optimization_pass.py +251 -0
  124. machine_dialect/mir/optimization_pipeline.py +355 -0
  125. machine_dialect/mir/optimizations/__init__.py +84 -0
  126. machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
  127. machine_dialect/mir/optimizations/branch_prediction.py +372 -0
  128. machine_dialect/mir/optimizations/constant_propagation.py +634 -0
  129. machine_dialect/mir/optimizations/cse.py +398 -0
  130. machine_dialect/mir/optimizations/dce.py +288 -0
  131. machine_dialect/mir/optimizations/inlining.py +551 -0
  132. machine_dialect/mir/optimizations/jump_threading.py +487 -0
  133. machine_dialect/mir/optimizations/licm.py +405 -0
  134. machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
  135. machine_dialect/mir/optimizations/strength_reduction.py +422 -0
  136. machine_dialect/mir/optimizations/tail_call.py +207 -0
  137. machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
  138. machine_dialect/mir/optimizations/type_narrowing.py +397 -0
  139. machine_dialect/mir/optimizations/type_specialization.py +447 -0
  140. machine_dialect/mir/optimizations/type_specific.py +906 -0
  141. machine_dialect/mir/optimize_mir.py +89 -0
  142. machine_dialect/mir/pass_manager.py +391 -0
  143. machine_dialect/mir/profiling/__init__.py +26 -0
  144. machine_dialect/mir/profiling/profile_collector.py +318 -0
  145. machine_dialect/mir/profiling/profile_data.py +372 -0
  146. machine_dialect/mir/profiling/profile_reader.py +272 -0
  147. machine_dialect/mir/profiling/profile_writer.py +226 -0
  148. machine_dialect/mir/register_allocation.py +302 -0
  149. machine_dialect/mir/reporting/__init__.py +17 -0
  150. machine_dialect/mir/reporting/optimization_reporter.py +314 -0
  151. machine_dialect/mir/reporting/report_formatter.py +289 -0
  152. machine_dialect/mir/ssa_construction.py +342 -0
  153. machine_dialect/mir/tests/__init__.py +1 -0
  154. machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
  155. machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
  156. machine_dialect/mir/tests/test_algebraic_division.py +126 -0
  157. machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
  158. machine_dialect/mir/tests/test_basic_block.py +425 -0
  159. machine_dialect/mir/tests/test_branch_prediction.py +459 -0
  160. machine_dialect/mir/tests/test_call_lowering.py +168 -0
  161. machine_dialect/mir/tests/test_collection_lowering.py +604 -0
  162. machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
  163. machine_dialect/mir/tests/test_custom_passes.py +166 -0
  164. machine_dialect/mir/tests/test_debug_info.py +285 -0
  165. machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
  166. machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
  167. machine_dialect/mir/tests/test_double_negation.py +231 -0
  168. machine_dialect/mir/tests/test_escape_analysis.py +233 -0
  169. machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
  170. machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
  171. machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
  172. machine_dialect/mir/tests/test_inlining.py +435 -0
  173. machine_dialect/mir/tests/test_licm.py +472 -0
  174. machine_dialect/mir/tests/test_mir_dumper.py +313 -0
  175. machine_dialect/mir/tests/test_mir_instructions.py +445 -0
  176. machine_dialect/mir/tests/test_mir_module.py +860 -0
  177. machine_dialect/mir/tests/test_mir_printer.py +387 -0
  178. machine_dialect/mir/tests/test_mir_types.py +123 -0
  179. machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
  180. machine_dialect/mir/tests/test_mir_validation.py +378 -0
  181. machine_dialect/mir/tests/test_mir_values.py +168 -0
  182. machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
  183. machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
  184. machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
  185. machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
  186. machine_dialect/mir/tests/test_pass_manager.py +294 -0
  187. machine_dialect/mir/tests/test_pass_registration.py +64 -0
  188. machine_dialect/mir/tests/test_profiling.py +356 -0
  189. machine_dialect/mir/tests/test_register_allocation.py +307 -0
  190. machine_dialect/mir/tests/test_report_formatters.py +372 -0
  191. machine_dialect/mir/tests/test_ssa_construction.py +433 -0
  192. machine_dialect/mir/tests/test_tail_call.py +236 -0
  193. machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
  194. machine_dialect/mir/tests/test_type_narrowing.py +277 -0
  195. machine_dialect/mir/tests/test_type_specialization.py +421 -0
  196. machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
  197. machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
  198. machine_dialect/mir/type_inference.py +368 -0
  199. machine_dialect/parser/__init__.py +12 -0
  200. machine_dialect/parser/enums.py +45 -0
  201. machine_dialect/parser/parser.py +3655 -0
  202. machine_dialect/parser/protocols.py +11 -0
  203. machine_dialect/parser/symbol_table.py +169 -0
  204. machine_dialect/parser/tests/__init__.py +0 -0
  205. machine_dialect/parser/tests/helper_functions.py +193 -0
  206. machine_dialect/parser/tests/test_action_statements.py +334 -0
  207. machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
  208. machine_dialect/parser/tests/test_call_statements.py +154 -0
  209. machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
  210. machine_dialect/parser/tests/test_collection_mutations.py +264 -0
  211. machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
  212. machine_dialect/parser/tests/test_define_integration.py +468 -0
  213. machine_dialect/parser/tests/test_define_statements.py +311 -0
  214. machine_dialect/parser/tests/test_dict_extraction.py +115 -0
  215. machine_dialect/parser/tests/test_empty_literal.py +155 -0
  216. machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
  217. machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
  218. machine_dialect/parser/tests/test_if_empty_block.py +61 -0
  219. machine_dialect/parser/tests/test_if_statements.py +299 -0
  220. machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
  221. machine_dialect/parser/tests/test_infix_expressions.py +680 -0
  222. machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
  223. machine_dialect/parser/tests/test_interaction_statements.py +269 -0
  224. machine_dialect/parser/tests/test_list_literals.py +277 -0
  225. machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
  226. machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
  227. machine_dialect/parser/tests/test_parse_errors.py +114 -0
  228. machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
  229. machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
  230. machine_dialect/parser/tests/test_program.py +13 -0
  231. machine_dialect/parser/tests/test_return_statements.py +89 -0
  232. machine_dialect/parser/tests/test_set_statements.py +152 -0
  233. machine_dialect/parser/tests/test_strict_equality.py +258 -0
  234. machine_dialect/parser/tests/test_symbol_table.py +217 -0
  235. machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
  236. machine_dialect/parser/tests/test_utility_statements.py +423 -0
  237. machine_dialect/parser/token_buffer.py +159 -0
  238. machine_dialect/repl/__init__.py +3 -0
  239. machine_dialect/repl/repl.py +426 -0
  240. machine_dialect/repl/tests/__init__.py +0 -0
  241. machine_dialect/repl/tests/test_repl.py +606 -0
  242. machine_dialect/semantic/__init__.py +12 -0
  243. machine_dialect/semantic/analyzer.py +906 -0
  244. machine_dialect/semantic/error_messages.py +189 -0
  245. machine_dialect/semantic/tests/__init__.py +1 -0
  246. machine_dialect/semantic/tests/test_analyzer.py +364 -0
  247. machine_dialect/semantic/tests/test_error_messages.py +104 -0
  248. machine_dialect/tests/edge_cases/__init__.py +10 -0
  249. machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
  250. machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
  251. machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
  252. machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
  253. machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
  254. machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
  255. machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
  256. machine_dialect/tests/integration/test_list_compilation.py +395 -0
  257. machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
  258. machine_dialect/type_checking/__init__.py +21 -0
  259. machine_dialect/type_checking/tests/__init__.py +1 -0
  260. machine_dialect/type_checking/tests/test_type_system.py +230 -0
  261. machine_dialect/type_checking/type_system.py +270 -0
  262. machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
  263. machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
  264. machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
  265. machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
  266. machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  267. machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
  268. machine_dialect_vm/__init__.pyi +15 -0
@@ -0,0 +1,343 @@
1
+ """Tests for conditional (ternary) expressions in the parser.
2
+
3
+ This module tests the parsing of conditional expressions which follow the pattern:
4
+ consequence if/when/whenever condition, else/otherwise alternative
5
+ """
6
+
7
+ import pytest
8
+
9
+ from machine_dialect.ast import (
10
+ ConditionalExpression,
11
+ ExpressionStatement,
12
+ Identifier,
13
+ InfixExpression,
14
+ PrefixExpression,
15
+ StringLiteral,
16
+ WholeNumberLiteral,
17
+ YesNoLiteral,
18
+ )
19
+ from machine_dialect.parser import Parser
20
+
21
+
22
+ class TestConditionalExpressions:
23
+ """Test parsing of conditional/ternary expressions."""
24
+
25
+ @pytest.mark.parametrize(
26
+ "source,expected_consequence,expected_condition,expected_alternative",
27
+ [
28
+ # Basic integer literals
29
+ ("1 if Yes, else 0", "1", "Yes", "0"),
30
+ ("1 if Yes, otherwise 0", "1", "Yes", "0"),
31
+ ("1 when Yes, else 0", "1", "Yes", "0"),
32
+ ("1 when Yes, otherwise 0", "1", "Yes", "0"),
33
+ ("1 whenever Yes, else 0", "1", "Yes", "0"),
34
+ ("1 whenever Yes, otherwise 0", "1", "Yes", "0"),
35
+ # Semicolon separator
36
+ ("1 if Yes; else 0", "1", "Yes", "0"),
37
+ ("1 if Yes; otherwise 0", "1", "Yes", "0"),
38
+ ("1 when Yes; else 0", "1", "Yes", "0"),
39
+ ("1 when Yes; otherwise 0", "1", "Yes", "0"),
40
+ ("1 whenever Yes; else 0", "1", "Yes", "0"),
41
+ ("1 whenever Yes; otherwise 0", "1", "Yes", "0"),
42
+ ],
43
+ )
44
+ def test_parse_basic_conditional_expression(
45
+ self, source: str, expected_consequence: str, expected_condition: str, expected_alternative: str
46
+ ) -> None:
47
+ """Test parsing of basic conditional expressions with literals."""
48
+ parser = Parser()
49
+ program = parser.parse(source)
50
+
51
+ assert program is not None
52
+ assert len(program.statements) == 1
53
+
54
+ statement = program.statements[0]
55
+ assert isinstance(statement, ExpressionStatement)
56
+ assert statement.expression is not None
57
+
58
+ conditional = statement.expression
59
+ assert isinstance(conditional, ConditionalExpression)
60
+
61
+ # Check consequence
62
+ assert conditional.consequence is not None
63
+ if expected_consequence in ["Yes", "No"]:
64
+ assert isinstance(conditional.consequence, YesNoLiteral)
65
+ # Already in canonical Yes/No representation
66
+ assert str(conditional.consequence) == f"_{expected_consequence}_"
67
+ else:
68
+ assert isinstance(conditional.consequence, WholeNumberLiteral)
69
+ assert conditional.consequence.value == int(expected_consequence)
70
+
71
+ # Check condition
72
+ assert conditional.condition is not None
73
+ assert isinstance(conditional.condition, YesNoLiteral)
74
+ # Already in canonical Yes/No representation
75
+ assert str(conditional.condition) == f"_{expected_condition}_"
76
+
77
+ # Check alternative
78
+ assert conditional.alternative is not None
79
+ if expected_alternative in ["Yes", "No"]:
80
+ assert isinstance(conditional.alternative, YesNoLiteral)
81
+ # Already in canonical Yes/No representation
82
+ assert str(conditional.alternative) == f"_{expected_alternative}_"
83
+ else:
84
+ assert isinstance(conditional.alternative, WholeNumberLiteral)
85
+ assert conditional.alternative.value == int(expected_alternative)
86
+
87
+ def test_conditional_with_identifiers(self) -> None:
88
+ """Test conditional expressions using identifiers."""
89
+ source = "`result` if `some condition`, else `some value`"
90
+ parser = Parser()
91
+ program = parser.parse(source)
92
+
93
+ assert program is not None
94
+ assert len(program.statements) == 1
95
+
96
+ statement = program.statements[0]
97
+ assert isinstance(statement, ExpressionStatement)
98
+ conditional = statement.expression
99
+ assert isinstance(conditional, ConditionalExpression)
100
+
101
+ # Check consequence is an identifier
102
+ assert isinstance(conditional.consequence, Identifier)
103
+ assert conditional.consequence.value == "result"
104
+
105
+ # Check condition is an identifier
106
+ assert isinstance(conditional.condition, Identifier)
107
+ assert conditional.condition.value == "some condition"
108
+
109
+ # Check alternative is an identifier
110
+ assert isinstance(conditional.alternative, Identifier)
111
+ assert conditional.alternative.value == "some value"
112
+
113
+ def test_conditional_with_string_literals(self) -> None:
114
+ """Test conditional expressions with string literals."""
115
+ source = '"yes" if `flag`, else "no"'
116
+ parser = Parser()
117
+ program = parser.parse(source)
118
+
119
+ assert program is not None
120
+ assert len(program.statements) == 1
121
+
122
+ statement = program.statements[0]
123
+ assert isinstance(statement, ExpressionStatement)
124
+ conditional = statement.expression
125
+ assert isinstance(conditional, ConditionalExpression)
126
+
127
+ # Check consequence is a string
128
+ assert isinstance(conditional.consequence, StringLiteral)
129
+ assert conditional.consequence.value == "yes"
130
+
131
+ # Check condition is an identifier
132
+ assert isinstance(conditional.condition, Identifier)
133
+ assert conditional.condition.value == "flag"
134
+
135
+ # Check alternative is a string
136
+ assert isinstance(conditional.alternative, StringLiteral)
137
+ assert conditional.alternative.value == "no"
138
+
139
+ def test_conditional_with_complex_condition(self) -> None:
140
+ """Test conditional with complex boolean condition."""
141
+ source = "1 if `x` > 0, else -1"
142
+ parser = Parser()
143
+ program = parser.parse(source)
144
+
145
+ assert program is not None
146
+ assert len(program.statements) == 1
147
+
148
+ statement = program.statements[0]
149
+ assert isinstance(statement, ExpressionStatement)
150
+ conditional = statement.expression
151
+ assert isinstance(conditional, ConditionalExpression)
152
+
153
+ # Check consequence
154
+ assert isinstance(conditional.consequence, WholeNumberLiteral)
155
+ assert conditional.consequence.value == 1
156
+
157
+ # Check condition is an infix expression
158
+ assert isinstance(conditional.condition, InfixExpression)
159
+ assert conditional.condition.operator == ">"
160
+ assert isinstance(conditional.condition.left, Identifier)
161
+ assert conditional.condition.left.value == "x"
162
+ assert isinstance(conditional.condition.right, WholeNumberLiteral)
163
+ assert conditional.condition.right.value == 0
164
+
165
+ # Check alternative
166
+ # TODO: In the future, negative numbers should be parsed as WholeNumberLiteral, not PrefixExpression
167
+ assert isinstance(conditional.alternative, PrefixExpression)
168
+ assert conditional.alternative.operator == "-"
169
+ assert isinstance(conditional.alternative.right, WholeNumberLiteral)
170
+ assert conditional.alternative.right.value == 1
171
+
172
+ def test_nested_conditional_expressions(self) -> None:
173
+ """Test nested conditional expressions."""
174
+ source = "1 if `a`, else 2 if `b`, else 3"
175
+ parser = Parser()
176
+ program = parser.parse(source)
177
+
178
+ assert program is not None
179
+ assert len(program.statements) == 1
180
+
181
+ statement = program.statements[0]
182
+ assert isinstance(statement, ExpressionStatement)
183
+ outer_conditional = statement.expression
184
+ assert isinstance(outer_conditional, ConditionalExpression)
185
+
186
+ # Check outer consequence
187
+ assert isinstance(outer_conditional.consequence, WholeNumberLiteral)
188
+ assert outer_conditional.consequence.value == 1
189
+
190
+ # Check outer condition
191
+ assert isinstance(outer_conditional.condition, Identifier)
192
+ assert outer_conditional.condition.value == "a"
193
+
194
+ # Check outer alternative is another conditional
195
+ inner_conditional = outer_conditional.alternative
196
+ assert isinstance(inner_conditional, ConditionalExpression)
197
+
198
+ # Check inner conditional
199
+ assert isinstance(inner_conditional.consequence, WholeNumberLiteral)
200
+ assert inner_conditional.consequence.value == 2
201
+ assert isinstance(inner_conditional.condition, Identifier)
202
+ assert inner_conditional.condition.value == "b"
203
+ assert isinstance(inner_conditional.alternative, WholeNumberLiteral)
204
+ assert inner_conditional.alternative.value == 3
205
+
206
+ def test_conditional_with_arithmetic_expressions(self) -> None:
207
+ """Test conditional with arithmetic expressions."""
208
+ source = "`x` + 1 if `flag`, else `x` - 1"
209
+ parser = Parser()
210
+ program = parser.parse(source)
211
+
212
+ assert program is not None
213
+ assert len(program.statements) == 1
214
+
215
+ statement = program.statements[0]
216
+ assert isinstance(statement, ExpressionStatement)
217
+ conditional = statement.expression
218
+ assert isinstance(conditional, ConditionalExpression)
219
+
220
+ # Check consequence is an addition
221
+ assert isinstance(conditional.consequence, InfixExpression)
222
+ assert conditional.consequence.operator == "+"
223
+ assert isinstance(conditional.consequence.left, Identifier)
224
+ assert conditional.consequence.left.value == "x"
225
+ assert isinstance(conditional.consequence.right, WholeNumberLiteral)
226
+ assert conditional.consequence.right.value == 1
227
+
228
+ # Check condition
229
+ assert isinstance(conditional.condition, Identifier)
230
+ assert conditional.condition.value == "flag"
231
+
232
+ # Check alternative is a subtraction
233
+ assert isinstance(conditional.alternative, InfixExpression)
234
+ assert conditional.alternative.operator == "-"
235
+ assert isinstance(conditional.alternative.left, Identifier)
236
+ assert conditional.alternative.left.value == "x"
237
+ assert isinstance(conditional.alternative.right, WholeNumberLiteral)
238
+ assert conditional.alternative.right.value == 1
239
+
240
+ def test_conditional_string_representation(self) -> None:
241
+ """Test the string representation of conditional expressions."""
242
+ source = "1 if Yes, else 0"
243
+ parser = Parser()
244
+ program = parser.parse(source)
245
+
246
+ assert program is not None
247
+ statement = program.statements[0]
248
+ assert isinstance(statement, ExpressionStatement)
249
+ conditional = statement.expression
250
+
251
+ # Check string representation
252
+ assert str(conditional) == "(_1_ if _Yes_ else _0_)"
253
+
254
+ def test_conditional_with_logical_operators(self) -> None:
255
+ """Test conditional with logical operators in condition."""
256
+ source = "1 if `a` and `b`, else 0"
257
+ parser = Parser()
258
+ program = parser.parse(source)
259
+
260
+ assert program is not None
261
+ assert len(program.statements) == 1
262
+
263
+ statement = program.statements[0]
264
+ assert isinstance(statement, ExpressionStatement)
265
+ conditional = statement.expression
266
+ assert isinstance(conditional, ConditionalExpression)
267
+
268
+ # Check condition is a logical AND
269
+ assert isinstance(conditional.condition, InfixExpression)
270
+ assert conditional.condition.operator == "and"
271
+ assert isinstance(conditional.condition.left, Identifier)
272
+ assert conditional.condition.left.value == "a"
273
+ assert isinstance(conditional.condition.right, Identifier)
274
+ assert conditional.condition.right.value == "b"
275
+
276
+ def test_conditional_without_else_clause(self) -> None:
277
+ """Test that conditional without else clause is handled gracefully."""
278
+ source = "1 if True"
279
+ parser = Parser()
280
+ program = parser.parse(source)
281
+
282
+ assert program is not None
283
+ assert len(program.statements) == 1
284
+
285
+ statement = program.statements[0]
286
+ assert isinstance(statement, ExpressionStatement)
287
+ conditional = statement.expression
288
+ assert isinstance(conditional, ConditionalExpression)
289
+
290
+ # Check that alternative is None when else clause is missing
291
+ assert conditional.consequence is not None
292
+ assert conditional.condition is not None
293
+ assert conditional.alternative is None
294
+
295
+ @pytest.mark.parametrize(
296
+ "keyword",
297
+ ["if", "when", "whenever"],
298
+ )
299
+ def test_all_condition_keywords(self, keyword: str) -> None:
300
+ """Test that all condition keywords work correctly."""
301
+ source = f"1 {keyword} Yes, else 0"
302
+ parser = Parser()
303
+ program = parser.parse(source)
304
+
305
+ assert program is not None
306
+ assert len(program.statements) == 1
307
+
308
+ statement = program.statements[0]
309
+ assert isinstance(statement, ExpressionStatement)
310
+ conditional = statement.expression
311
+ assert isinstance(conditional, ConditionalExpression)
312
+
313
+ # Verify the expression was parsed correctly
314
+ assert isinstance(conditional.consequence, WholeNumberLiteral)
315
+ assert conditional.consequence.value == 1
316
+ assert isinstance(conditional.condition, YesNoLiteral)
317
+ assert isinstance(conditional.alternative, WholeNumberLiteral)
318
+ assert conditional.alternative.value == 0
319
+
320
+ @pytest.mark.parametrize(
321
+ "else_keyword",
322
+ ["else", "otherwise"],
323
+ )
324
+ def test_all_else_keywords(self, else_keyword: str) -> None:
325
+ """Test that all else keywords work correctly."""
326
+ source = f"1 if Yes, {else_keyword} 0"
327
+ parser = Parser()
328
+ program = parser.parse(source)
329
+
330
+ assert program is not None
331
+ assert len(program.statements) == 1
332
+
333
+ statement = program.statements[0]
334
+ assert isinstance(statement, ExpressionStatement)
335
+ conditional = statement.expression
336
+ assert isinstance(conditional, ConditionalExpression)
337
+
338
+ # Verify the expression was parsed correctly
339
+ assert isinstance(conditional.consequence, WholeNumberLiteral)
340
+ assert conditional.consequence.value == 1
341
+ assert isinstance(conditional.condition, YesNoLiteral)
342
+ assert isinstance(conditional.alternative, WholeNumberLiteral)
343
+ assert conditional.alternative.value == 0