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,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)