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,391 @@
|
|
1
|
+
"""Unit tests for machine_dialect.cfg.examples module.
|
2
|
+
|
3
|
+
This module tests all example functions that demonstrate CFG parser usage,
|
4
|
+
including parsing, validation, and placeholder generation functionality.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from io import StringIO
|
8
|
+
from unittest.mock import MagicMock, Mock, patch
|
9
|
+
|
10
|
+
import pytest
|
11
|
+
from lark import Tree
|
12
|
+
|
13
|
+
from machine_dialect.cfg.examples import (
|
14
|
+
example_generate_prompt,
|
15
|
+
example_parse_code,
|
16
|
+
example_validate_code,
|
17
|
+
main,
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class TestExampleParseCode:
|
22
|
+
"""Test suite for example_parse_code function."""
|
23
|
+
|
24
|
+
@patch("machine_dialect.cfg.examples.CFGParser")
|
25
|
+
@patch("sys.stdout", new_callable=StringIO)
|
26
|
+
def test_all_examples_parse_successfully(self, mock_stdout: StringIO, mock_parser_class: Mock) -> None:
|
27
|
+
"""Test that all three examples parse successfully.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
mock_stdout: Mocked stdout for capturing print output.
|
31
|
+
mock_parser_class: Mocked CFGParser class.
|
32
|
+
"""
|
33
|
+
# Setup mock parser
|
34
|
+
mock_parser = MagicMock()
|
35
|
+
mock_parser_class.return_value = mock_parser
|
36
|
+
|
37
|
+
# Create mock tree objects for each example
|
38
|
+
mock_tree1 = MagicMock(spec=Tree)
|
39
|
+
mock_tree2 = MagicMock(spec=Tree)
|
40
|
+
mock_tree3 = MagicMock(spec=Tree)
|
41
|
+
|
42
|
+
# Configure parse to return different trees for each call
|
43
|
+
mock_parser.parse.side_effect = [mock_tree1, mock_tree2, mock_tree3]
|
44
|
+
|
45
|
+
# Configure pretty_print to return formatted strings
|
46
|
+
mock_parser.pretty_print.side_effect = [
|
47
|
+
"AST for example 1",
|
48
|
+
"AST for example 2",
|
49
|
+
"AST for example 3",
|
50
|
+
]
|
51
|
+
|
52
|
+
# Execute the function
|
53
|
+
example_parse_code()
|
54
|
+
|
55
|
+
# Verify parse was called three times with expected code snippets
|
56
|
+
assert mock_parser.parse.call_count == 3
|
57
|
+
|
58
|
+
# Check first call (simple arithmetic)
|
59
|
+
first_call_arg = mock_parser.parse.call_args_list[0][0][0]
|
60
|
+
assert "Set `x` to _10_." in first_call_arg
|
61
|
+
assert "Set `y` to _20_." in first_call_arg
|
62
|
+
assert "Set `sum` to `x` + `y`." in first_call_arg
|
63
|
+
|
64
|
+
# Check second call (conditional)
|
65
|
+
second_call_arg = mock_parser.parse.call_args_list[1][0][0]
|
66
|
+
assert "Set `age` to _18_." in second_call_arg
|
67
|
+
assert "If `age` is greater than _17_" in second_call_arg
|
68
|
+
|
69
|
+
# Check third call (logical operations)
|
70
|
+
third_call_arg = mock_parser.parse.call_args_list[2][0][0]
|
71
|
+
assert "Set `is_raining` to _yes_." in third_call_arg
|
72
|
+
assert "Set `have_umbrella` to _no_." in third_call_arg
|
73
|
+
|
74
|
+
# Verify output contains success messages
|
75
|
+
output = mock_stdout.getvalue()
|
76
|
+
assert output.count("Parse successful!") == 3
|
77
|
+
assert "Example 1: Simple arithmetic" in output
|
78
|
+
assert "Example 2: Conditional" in output
|
79
|
+
assert "Example 3: Logical operations" in output
|
80
|
+
assert "AST for example 1" in output
|
81
|
+
assert "AST for example 2" in output
|
82
|
+
assert "AST for example 3" in output
|
83
|
+
|
84
|
+
@patch("machine_dialect.cfg.examples.CFGParser")
|
85
|
+
@patch("sys.stdout", new_callable=StringIO)
|
86
|
+
def test_parse_failures_are_handled(self, mock_stdout: StringIO, mock_parser_class: Mock) -> None:
|
87
|
+
"""Test that parse failures are properly handled and reported.
|
88
|
+
|
89
|
+
Args:
|
90
|
+
mock_stdout: Mocked stdout for capturing print output.
|
91
|
+
mock_parser_class: Mocked CFGParser class.
|
92
|
+
"""
|
93
|
+
# Setup mock parser
|
94
|
+
mock_parser = MagicMock()
|
95
|
+
mock_parser_class.return_value = mock_parser
|
96
|
+
|
97
|
+
# Configure parse to raise ValueError for all examples
|
98
|
+
mock_parser.parse.side_effect = [
|
99
|
+
ValueError("Invalid syntax in example 1"),
|
100
|
+
ValueError("Invalid syntax in example 2"),
|
101
|
+
ValueError("Invalid syntax in example 3"),
|
102
|
+
]
|
103
|
+
|
104
|
+
# Execute the function
|
105
|
+
example_parse_code()
|
106
|
+
|
107
|
+
# Verify parse was called three times
|
108
|
+
assert mock_parser.parse.call_count == 3
|
109
|
+
|
110
|
+
# Verify output contains failure messages
|
111
|
+
output = mock_stdout.getvalue()
|
112
|
+
assert "Parse failed: Invalid syntax in example 1" in output
|
113
|
+
assert "Parse failed: Invalid syntax in example 2" in output
|
114
|
+
assert "Parse failed: Invalid syntax in example 3" in output
|
115
|
+
assert output.count("Parse successful!") == 0
|
116
|
+
|
117
|
+
@patch("machine_dialect.cfg.examples.CFGParser")
|
118
|
+
@patch("sys.stdout", new_callable=StringIO)
|
119
|
+
def test_mixed_success_and_failure(self, mock_stdout: StringIO, mock_parser_class: Mock) -> None:
|
120
|
+
"""Test handling of mixed success and failure cases.
|
121
|
+
|
122
|
+
Args:
|
123
|
+
mock_stdout: Mocked stdout for capturing print output.
|
124
|
+
mock_parser_class: Mocked CFGParser class.
|
125
|
+
"""
|
126
|
+
# Setup mock parser
|
127
|
+
mock_parser = MagicMock()
|
128
|
+
mock_parser_class.return_value = mock_parser
|
129
|
+
|
130
|
+
# Configure parse to succeed, fail, succeed
|
131
|
+
mock_tree1 = MagicMock(spec=Tree)
|
132
|
+
mock_tree3 = MagicMock(spec=Tree)
|
133
|
+
|
134
|
+
mock_parser.parse.side_effect = [
|
135
|
+
mock_tree1,
|
136
|
+
ValueError("Syntax error in conditional"),
|
137
|
+
mock_tree3,
|
138
|
+
]
|
139
|
+
|
140
|
+
mock_parser.pretty_print.side_effect = [
|
141
|
+
"AST for example 1",
|
142
|
+
"AST for example 3", # No call for example 2 due to error
|
143
|
+
]
|
144
|
+
|
145
|
+
# Execute the function
|
146
|
+
example_parse_code()
|
147
|
+
|
148
|
+
# Verify output contains mixed results
|
149
|
+
output = mock_stdout.getvalue()
|
150
|
+
assert output.count("Parse successful!") == 2
|
151
|
+
assert output.count("Parse failed:") == 1
|
152
|
+
assert "Parse failed: Syntax error in conditional" in output
|
153
|
+
|
154
|
+
|
155
|
+
class TestExampleGeneratePrompt:
|
156
|
+
"""Test suite for example_generate_prompt function."""
|
157
|
+
|
158
|
+
@patch("sys.stdout", new_callable=StringIO)
|
159
|
+
def test_prints_placeholder_message(self, mock_stdout: StringIO) -> None:
|
160
|
+
"""Test that the function prints the placeholder message.
|
161
|
+
|
162
|
+
Args:
|
163
|
+
mock_stdout: Mocked stdout for capturing print output.
|
164
|
+
"""
|
165
|
+
example_generate_prompt()
|
166
|
+
|
167
|
+
output = mock_stdout.getvalue()
|
168
|
+
assert "CFG generation functionality coming soon." in output
|
169
|
+
|
170
|
+
def test_function_returns_none(self) -> None:
|
171
|
+
"""Test that the function returns None."""
|
172
|
+
example_generate_prompt() # Function has no return value
|
173
|
+
|
174
|
+
|
175
|
+
class TestExampleValidateCode:
|
176
|
+
"""Test suite for example_validate_code function."""
|
177
|
+
|
178
|
+
@patch("machine_dialect.cfg.examples.CFGParser")
|
179
|
+
@patch("sys.stdout", new_callable=StringIO)
|
180
|
+
def test_validates_valid_and_invalid_code(self, mock_stdout: StringIO, mock_parser_class: Mock) -> None:
|
181
|
+
"""Test validation of both valid and invalid code examples.
|
182
|
+
|
183
|
+
Args:
|
184
|
+
mock_stdout: Mocked stdout for capturing print output.
|
185
|
+
mock_parser_class: Mocked CFGParser class.
|
186
|
+
"""
|
187
|
+
# Setup mock parser
|
188
|
+
mock_parser = MagicMock()
|
189
|
+
mock_parser_class.return_value = mock_parser
|
190
|
+
|
191
|
+
# Configure validate to return True for valid, False for invalid
|
192
|
+
mock_parser.validate.side_effect = [True, False]
|
193
|
+
|
194
|
+
# Execute the function
|
195
|
+
example_validate_code()
|
196
|
+
|
197
|
+
# Verify validate was called twice
|
198
|
+
assert mock_parser.validate.call_count == 2
|
199
|
+
|
200
|
+
# Check valid code call
|
201
|
+
valid_call_arg = mock_parser.validate.call_args_list[0][0][0]
|
202
|
+
assert 'Set `name` to _"Alice"_.' in valid_call_arg
|
203
|
+
assert "Say `name`." in valid_call_arg
|
204
|
+
|
205
|
+
# Check invalid code call
|
206
|
+
invalid_call_arg = mock_parser.validate.call_args_list[1][0][0]
|
207
|
+
assert "Set x to 10" in invalid_call_arg
|
208
|
+
assert "Say x" in invalid_call_arg
|
209
|
+
|
210
|
+
# Verify output contains expected messages
|
211
|
+
output = mock_stdout.getvalue()
|
212
|
+
assert "✓ Code is valid!" in output
|
213
|
+
assert "✗ Code is invalid!" in output
|
214
|
+
assert "Validating valid code:" in output
|
215
|
+
assert "Validating invalid code (missing backticks and periods):" in output
|
216
|
+
|
217
|
+
@patch("machine_dialect.cfg.examples.CFGParser")
|
218
|
+
@patch("sys.stdout", new_callable=StringIO)
|
219
|
+
def test_both_codes_valid(self, mock_stdout: StringIO, mock_parser_class: Mock) -> None:
|
220
|
+
"""Test when both code examples are valid.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
mock_stdout: Mocked stdout for capturing print output.
|
224
|
+
mock_parser_class: Mocked CFGParser class.
|
225
|
+
"""
|
226
|
+
# Setup mock parser
|
227
|
+
mock_parser = MagicMock()
|
228
|
+
mock_parser_class.return_value = mock_parser
|
229
|
+
|
230
|
+
# Configure validate to return True for both
|
231
|
+
mock_parser.validate.return_value = True
|
232
|
+
|
233
|
+
# Execute the function
|
234
|
+
example_validate_code()
|
235
|
+
|
236
|
+
# Verify output shows both as valid
|
237
|
+
output = mock_stdout.getvalue()
|
238
|
+
assert output.count("✓ Code is valid!") == 2
|
239
|
+
assert output.count("✗ Code is invalid!") == 0
|
240
|
+
|
241
|
+
@patch("machine_dialect.cfg.examples.CFGParser")
|
242
|
+
@patch("sys.stdout", new_callable=StringIO)
|
243
|
+
def test_both_codes_invalid(self, mock_stdout: StringIO, mock_parser_class: Mock) -> None:
|
244
|
+
"""Test when both code examples are invalid.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
mock_stdout: Mocked stdout for capturing print output.
|
248
|
+
mock_parser_class: Mocked CFGParser class.
|
249
|
+
"""
|
250
|
+
# Setup mock parser
|
251
|
+
mock_parser = MagicMock()
|
252
|
+
mock_parser_class.return_value = mock_parser
|
253
|
+
|
254
|
+
# Configure validate to return False for both
|
255
|
+
mock_parser.validate.return_value = False
|
256
|
+
|
257
|
+
# Execute the function
|
258
|
+
example_validate_code()
|
259
|
+
|
260
|
+
# Verify output shows both as invalid
|
261
|
+
output = mock_stdout.getvalue()
|
262
|
+
assert output.count("✓ Code is valid!") == 0
|
263
|
+
assert output.count("✗ Code is invalid!") == 2
|
264
|
+
|
265
|
+
|
266
|
+
class TestMain:
|
267
|
+
"""Test suite for main function."""
|
268
|
+
|
269
|
+
@patch("machine_dialect.cfg.examples.example_validate_code")
|
270
|
+
@patch("machine_dialect.cfg.examples.example_generate_prompt")
|
271
|
+
@patch("machine_dialect.cfg.examples.example_parse_code")
|
272
|
+
@patch("sys.stdout", new_callable=StringIO)
|
273
|
+
def test_main_calls_all_examples(
|
274
|
+
self,
|
275
|
+
mock_stdout: StringIO,
|
276
|
+
mock_parse: Mock,
|
277
|
+
mock_generate: Mock,
|
278
|
+
mock_validate: Mock,
|
279
|
+
) -> None:
|
280
|
+
"""Test that main calls all example functions in order.
|
281
|
+
|
282
|
+
Args:
|
283
|
+
mock_stdout: Mocked stdout for capturing print output.
|
284
|
+
mock_parse: Mocked example_parse_code function.
|
285
|
+
mock_generate: Mocked example_generate_prompt function.
|
286
|
+
mock_validate: Mocked example_validate_code function.
|
287
|
+
"""
|
288
|
+
# Execute main
|
289
|
+
main()
|
290
|
+
|
291
|
+
# Verify all example functions were called once
|
292
|
+
mock_parse.assert_called_once()
|
293
|
+
mock_generate.assert_called_once()
|
294
|
+
mock_validate.assert_called_once()
|
295
|
+
|
296
|
+
# Verify output contains section headers
|
297
|
+
output = mock_stdout.getvalue()
|
298
|
+
assert "CFG Parser Examples" in output
|
299
|
+
assert "1. PARSING EXAMPLES" in output
|
300
|
+
assert "2. GENERATION PROMPT EXAMPLES" in output
|
301
|
+
assert "3. VALIDATION EXAMPLES" in output
|
302
|
+
assert "Examples complete!" in output
|
303
|
+
|
304
|
+
@patch("machine_dialect.cfg.examples.example_validate_code")
|
305
|
+
@patch("machine_dialect.cfg.examples.example_generate_prompt")
|
306
|
+
@patch("machine_dialect.cfg.examples.example_parse_code")
|
307
|
+
def test_main_execution_order(self, mock_parse: Mock, mock_generate: Mock, mock_validate: Mock) -> None:
|
308
|
+
"""Test that example functions are called in the correct order.
|
309
|
+
|
310
|
+
Args:
|
311
|
+
mock_parse: Mocked example_parse_code function.
|
312
|
+
mock_generate: Mocked example_generate_prompt function.
|
313
|
+
mock_validate: Mocked example_validate_code function.
|
314
|
+
"""
|
315
|
+
# Track call order
|
316
|
+
call_order: list[str] = []
|
317
|
+
|
318
|
+
def track_parse() -> None:
|
319
|
+
call_order.append("parse")
|
320
|
+
|
321
|
+
def track_generate() -> None:
|
322
|
+
call_order.append("generate")
|
323
|
+
|
324
|
+
def track_validate() -> None:
|
325
|
+
call_order.append("validate")
|
326
|
+
|
327
|
+
mock_parse.side_effect = track_parse
|
328
|
+
mock_generate.side_effect = track_generate
|
329
|
+
mock_validate.side_effect = track_validate
|
330
|
+
|
331
|
+
# Execute main
|
332
|
+
main()
|
333
|
+
|
334
|
+
# Verify correct order
|
335
|
+
assert call_order == ["parse", "generate", "validate"]
|
336
|
+
|
337
|
+
@patch("machine_dialect.cfg.examples.example_validate_code")
|
338
|
+
@patch("machine_dialect.cfg.examples.example_generate_prompt")
|
339
|
+
@patch("machine_dialect.cfg.examples.example_parse_code")
|
340
|
+
@patch("sys.stdout", new_callable=StringIO)
|
341
|
+
def test_main_with_exception_propagation(
|
342
|
+
self,
|
343
|
+
mock_stdout: StringIO,
|
344
|
+
mock_parse: Mock,
|
345
|
+
mock_generate: Mock,
|
346
|
+
mock_validate: Mock,
|
347
|
+
) -> None:
|
348
|
+
"""Test that main propagates exceptions from example functions.
|
349
|
+
|
350
|
+
Args:
|
351
|
+
mock_stdout: Mocked stdout for capturing print output.
|
352
|
+
mock_parse: Mocked example_parse_code function.
|
353
|
+
mock_generate: Mocked example_generate_prompt function.
|
354
|
+
mock_validate: Mocked example_validate_code function.
|
355
|
+
"""
|
356
|
+
# Configure parse to raise an exception
|
357
|
+
mock_parse.side_effect = Exception("Parse example failed")
|
358
|
+
|
359
|
+
# Execute main - should raise the exception
|
360
|
+
with pytest.raises(Exception, match="Parse example failed"):
|
361
|
+
main()
|
362
|
+
|
363
|
+
# Verify parse was called
|
364
|
+
mock_parse.assert_called_once()
|
365
|
+
|
366
|
+
# Verify other functions were not called due to exception
|
367
|
+
mock_generate.assert_not_called()
|
368
|
+
mock_validate.assert_not_called()
|
369
|
+
|
370
|
+
|
371
|
+
class TestModuleExecution:
|
372
|
+
"""Test suite for module execution as __main__."""
|
373
|
+
|
374
|
+
@patch("machine_dialect.cfg.examples.main")
|
375
|
+
def test_main_called_when_module_executed(self, mock_main: Mock) -> None:
|
376
|
+
"""Test that main() is called when module is executed directly.
|
377
|
+
|
378
|
+
Args:
|
379
|
+
mock_main: Mocked main function.
|
380
|
+
"""
|
381
|
+
# Import the module to trigger __main__ check
|
382
|
+
from unittest.mock import patch
|
383
|
+
|
384
|
+
with patch("machine_dialect.cfg.examples.__name__", "__main__"):
|
385
|
+
# Re-import to trigger the if __name__ == "__main__" block
|
386
|
+
import machine_dialect.cfg.examples
|
387
|
+
|
388
|
+
# Note: In practice, this test would need special handling
|
389
|
+
# for the if __name__ == "__main__" block
|
390
|
+
# For now we just verify main exists and is callable
|
391
|
+
assert callable(machine_dialect.cfg.examples.main)
|