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.
- machine_dialect/__main__.py +667 -0
- machine_dialect/agent/__init__.py +5 -0
- machine_dialect/agent/agent.py +360 -0
- machine_dialect/ast/__init__.py +95 -0
- machine_dialect/ast/ast_node.py +35 -0
- machine_dialect/ast/call_expression.py +82 -0
- machine_dialect/ast/dict_extraction.py +60 -0
- machine_dialect/ast/expressions.py +439 -0
- machine_dialect/ast/literals.py +309 -0
- machine_dialect/ast/program.py +35 -0
- machine_dialect/ast/statements.py +1433 -0
- machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
- machine_dialect/ast/tests/test_boolean_literal.py +29 -0
- machine_dialect/ast/tests/test_collection_hir.py +138 -0
- machine_dialect/ast/tests/test_define_statement.py +142 -0
- machine_dialect/ast/tests/test_desugar.py +541 -0
- machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
- machine_dialect/cfg/__init__.py +6 -0
- machine_dialect/cfg/config.py +156 -0
- machine_dialect/cfg/examples.py +221 -0
- machine_dialect/cfg/generate_with_ai.py +187 -0
- machine_dialect/cfg/openai_generation.py +200 -0
- machine_dialect/cfg/parser.py +94 -0
- machine_dialect/cfg/tests/__init__.py +1 -0
- machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
- machine_dialect/cfg/tests/test_config.py +188 -0
- machine_dialect/cfg/tests/test_examples.py +391 -0
- machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
- machine_dialect/cfg/tests/test_openai_generation.py +256 -0
- machine_dialect/codegen/__init__.py +5 -0
- machine_dialect/codegen/bytecode_module.py +89 -0
- machine_dialect/codegen/bytecode_serializer.py +300 -0
- machine_dialect/codegen/opcodes.py +101 -0
- machine_dialect/codegen/register_codegen.py +1996 -0
- machine_dialect/codegen/symtab.py +208 -0
- machine_dialect/codegen/tests/__init__.py +1 -0
- machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
- machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
- machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
- machine_dialect/codegen/tests/test_symtab.py +418 -0
- machine_dialect/codegen/vm_serializer.py +621 -0
- machine_dialect/compiler/__init__.py +18 -0
- machine_dialect/compiler/compiler.py +197 -0
- machine_dialect/compiler/config.py +149 -0
- machine_dialect/compiler/context.py +149 -0
- machine_dialect/compiler/phases/__init__.py +19 -0
- machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
- machine_dialect/compiler/phases/codegen.py +40 -0
- machine_dialect/compiler/phases/hir_generation.py +39 -0
- machine_dialect/compiler/phases/mir_generation.py +86 -0
- machine_dialect/compiler/phases/optimization.py +110 -0
- machine_dialect/compiler/phases/parsing.py +39 -0
- machine_dialect/compiler/pipeline.py +143 -0
- machine_dialect/compiler/tests/__init__.py +1 -0
- machine_dialect/compiler/tests/test_compiler.py +568 -0
- machine_dialect/compiler/vm_runner.py +173 -0
- machine_dialect/errors/__init__.py +32 -0
- machine_dialect/errors/exceptions.py +369 -0
- machine_dialect/errors/messages.py +82 -0
- machine_dialect/errors/tests/__init__.py +0 -0
- machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
- machine_dialect/errors/tests/test_name_errors.py +118 -0
- machine_dialect/helpers/__init__.py +0 -0
- machine_dialect/helpers/stopwords.py +225 -0
- machine_dialect/helpers/validators.py +30 -0
- machine_dialect/lexer/__init__.py +9 -0
- machine_dialect/lexer/constants.py +23 -0
- machine_dialect/lexer/lexer.py +907 -0
- machine_dialect/lexer/tests/__init__.py +0 -0
- machine_dialect/lexer/tests/helpers.py +86 -0
- machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
- machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
- machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
- machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
- machine_dialect/lexer/tests/test_comments.py +200 -0
- machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
- machine_dialect/lexer/tests/test_lexer_position.py +113 -0
- machine_dialect/lexer/tests/test_list_tokens.py +282 -0
- machine_dialect/lexer/tests/test_stopwords.py +80 -0
- machine_dialect/lexer/tests/test_strict_equality.py +129 -0
- machine_dialect/lexer/tests/test_token.py +41 -0
- machine_dialect/lexer/tests/test_tokenization.py +294 -0
- machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
- machine_dialect/lexer/tests/test_url_literals.py +169 -0
- machine_dialect/lexer/tokens.py +487 -0
- machine_dialect/linter/__init__.py +10 -0
- machine_dialect/linter/__main__.py +144 -0
- machine_dialect/linter/linter.py +154 -0
- machine_dialect/linter/rules/__init__.py +8 -0
- machine_dialect/linter/rules/base.py +112 -0
- machine_dialect/linter/rules/statement_termination.py +99 -0
- machine_dialect/linter/tests/__init__.py +1 -0
- machine_dialect/linter/tests/mdrules/__init__.py +0 -0
- machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
- machine_dialect/linter/tests/test_linter.py +81 -0
- machine_dialect/linter/tests/test_rules.py +110 -0
- machine_dialect/linter/tests/test_violations.py +71 -0
- machine_dialect/linter/violations.py +51 -0
- machine_dialect/mir/__init__.py +69 -0
- machine_dialect/mir/analyses/__init__.py +20 -0
- machine_dialect/mir/analyses/alias_analysis.py +315 -0
- machine_dialect/mir/analyses/dominance_analysis.py +49 -0
- machine_dialect/mir/analyses/escape_analysis.py +286 -0
- machine_dialect/mir/analyses/loop_analysis.py +272 -0
- machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
- machine_dialect/mir/analyses/type_analysis.py +448 -0
- machine_dialect/mir/analyses/use_def_chains.py +232 -0
- machine_dialect/mir/basic_block.py +385 -0
- machine_dialect/mir/dataflow.py +445 -0
- machine_dialect/mir/debug_info.py +208 -0
- machine_dialect/mir/hir_to_mir.py +1738 -0
- machine_dialect/mir/mir_dumper.py +366 -0
- machine_dialect/mir/mir_function.py +167 -0
- machine_dialect/mir/mir_instructions.py +1877 -0
- machine_dialect/mir/mir_interpreter.py +556 -0
- machine_dialect/mir/mir_module.py +225 -0
- machine_dialect/mir/mir_printer.py +480 -0
- machine_dialect/mir/mir_transformer.py +410 -0
- machine_dialect/mir/mir_types.py +367 -0
- machine_dialect/mir/mir_validation.py +455 -0
- machine_dialect/mir/mir_values.py +268 -0
- machine_dialect/mir/optimization_config.py +233 -0
- machine_dialect/mir/optimization_pass.py +251 -0
- machine_dialect/mir/optimization_pipeline.py +355 -0
- machine_dialect/mir/optimizations/__init__.py +84 -0
- machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
- machine_dialect/mir/optimizations/branch_prediction.py +372 -0
- machine_dialect/mir/optimizations/constant_propagation.py +634 -0
- machine_dialect/mir/optimizations/cse.py +398 -0
- machine_dialect/mir/optimizations/dce.py +288 -0
- machine_dialect/mir/optimizations/inlining.py +551 -0
- machine_dialect/mir/optimizations/jump_threading.py +487 -0
- machine_dialect/mir/optimizations/licm.py +405 -0
- machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
- machine_dialect/mir/optimizations/strength_reduction.py +422 -0
- machine_dialect/mir/optimizations/tail_call.py +207 -0
- machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
- machine_dialect/mir/optimizations/type_narrowing.py +397 -0
- machine_dialect/mir/optimizations/type_specialization.py +447 -0
- machine_dialect/mir/optimizations/type_specific.py +906 -0
- machine_dialect/mir/optimize_mir.py +89 -0
- machine_dialect/mir/pass_manager.py +391 -0
- machine_dialect/mir/profiling/__init__.py +26 -0
- machine_dialect/mir/profiling/profile_collector.py +318 -0
- machine_dialect/mir/profiling/profile_data.py +372 -0
- machine_dialect/mir/profiling/profile_reader.py +272 -0
- machine_dialect/mir/profiling/profile_writer.py +226 -0
- machine_dialect/mir/register_allocation.py +302 -0
- machine_dialect/mir/reporting/__init__.py +17 -0
- machine_dialect/mir/reporting/optimization_reporter.py +314 -0
- machine_dialect/mir/reporting/report_formatter.py +289 -0
- machine_dialect/mir/ssa_construction.py +342 -0
- machine_dialect/mir/tests/__init__.py +1 -0
- machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
- machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
- machine_dialect/mir/tests/test_algebraic_division.py +126 -0
- machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
- machine_dialect/mir/tests/test_basic_block.py +425 -0
- machine_dialect/mir/tests/test_branch_prediction.py +459 -0
- machine_dialect/mir/tests/test_call_lowering.py +168 -0
- machine_dialect/mir/tests/test_collection_lowering.py +604 -0
- machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
- machine_dialect/mir/tests/test_custom_passes.py +166 -0
- machine_dialect/mir/tests/test_debug_info.py +285 -0
- machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
- machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
- machine_dialect/mir/tests/test_double_negation.py +231 -0
- machine_dialect/mir/tests/test_escape_analysis.py +233 -0
- machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
- machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
- machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
- machine_dialect/mir/tests/test_inlining.py +435 -0
- machine_dialect/mir/tests/test_licm.py +472 -0
- machine_dialect/mir/tests/test_mir_dumper.py +313 -0
- machine_dialect/mir/tests/test_mir_instructions.py +445 -0
- machine_dialect/mir/tests/test_mir_module.py +860 -0
- machine_dialect/mir/tests/test_mir_printer.py +387 -0
- machine_dialect/mir/tests/test_mir_types.py +123 -0
- machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
- machine_dialect/mir/tests/test_mir_validation.py +378 -0
- machine_dialect/mir/tests/test_mir_values.py +168 -0
- machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
- machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
- machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
- machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
- machine_dialect/mir/tests/test_pass_manager.py +294 -0
- machine_dialect/mir/tests/test_pass_registration.py +64 -0
- machine_dialect/mir/tests/test_profiling.py +356 -0
- machine_dialect/mir/tests/test_register_allocation.py +307 -0
- machine_dialect/mir/tests/test_report_formatters.py +372 -0
- machine_dialect/mir/tests/test_ssa_construction.py +433 -0
- machine_dialect/mir/tests/test_tail_call.py +236 -0
- machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
- machine_dialect/mir/tests/test_type_narrowing.py +277 -0
- machine_dialect/mir/tests/test_type_specialization.py +421 -0
- machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
- machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
- machine_dialect/mir/type_inference.py +368 -0
- machine_dialect/parser/__init__.py +12 -0
- machine_dialect/parser/enums.py +45 -0
- machine_dialect/parser/parser.py +3655 -0
- machine_dialect/parser/protocols.py +11 -0
- machine_dialect/parser/symbol_table.py +169 -0
- machine_dialect/parser/tests/__init__.py +0 -0
- machine_dialect/parser/tests/helper_functions.py +193 -0
- machine_dialect/parser/tests/test_action_statements.py +334 -0
- machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
- machine_dialect/parser/tests/test_call_statements.py +154 -0
- machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
- machine_dialect/parser/tests/test_collection_mutations.py +264 -0
- machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
- machine_dialect/parser/tests/test_define_integration.py +468 -0
- machine_dialect/parser/tests/test_define_statements.py +311 -0
- machine_dialect/parser/tests/test_dict_extraction.py +115 -0
- machine_dialect/parser/tests/test_empty_literal.py +155 -0
- machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
- machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
- machine_dialect/parser/tests/test_if_empty_block.py +61 -0
- machine_dialect/parser/tests/test_if_statements.py +299 -0
- machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
- machine_dialect/parser/tests/test_infix_expressions.py +680 -0
- machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
- machine_dialect/parser/tests/test_interaction_statements.py +269 -0
- machine_dialect/parser/tests/test_list_literals.py +277 -0
- machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
- machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
- machine_dialect/parser/tests/test_parse_errors.py +114 -0
- machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
- machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
- machine_dialect/parser/tests/test_program.py +13 -0
- machine_dialect/parser/tests/test_return_statements.py +89 -0
- machine_dialect/parser/tests/test_set_statements.py +152 -0
- machine_dialect/parser/tests/test_strict_equality.py +258 -0
- machine_dialect/parser/tests/test_symbol_table.py +217 -0
- machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
- machine_dialect/parser/tests/test_utility_statements.py +423 -0
- machine_dialect/parser/token_buffer.py +159 -0
- machine_dialect/repl/__init__.py +3 -0
- machine_dialect/repl/repl.py +426 -0
- machine_dialect/repl/tests/__init__.py +0 -0
- machine_dialect/repl/tests/test_repl.py +606 -0
- machine_dialect/semantic/__init__.py +12 -0
- machine_dialect/semantic/analyzer.py +906 -0
- machine_dialect/semantic/error_messages.py +189 -0
- machine_dialect/semantic/tests/__init__.py +1 -0
- machine_dialect/semantic/tests/test_analyzer.py +364 -0
- machine_dialect/semantic/tests/test_error_messages.py +104 -0
- machine_dialect/tests/edge_cases/__init__.py +10 -0
- machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
- machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
- machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
- machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
- machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
- machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
- machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
- machine_dialect/tests/integration/test_list_compilation.py +395 -0
- machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
- machine_dialect/type_checking/__init__.py +21 -0
- machine_dialect/type_checking/tests/__init__.py +1 -0
- machine_dialect/type_checking/tests/test_type_system.py +230 -0
- machine_dialect/type_checking/type_system.py +270 -0
- machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
- machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
- machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
- machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
- machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
- 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
|