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,334 @@
1
+ """Tests for Action statements (private methods) in Machine Dialect™."""
2
+
3
+ from machine_dialect.ast import ActionStatement, BlockStatement, EmptyLiteral, Output, SetStatement
4
+ from machine_dialect.parser import Parser
5
+
6
+
7
+ class TestActionStatements:
8
+ """Test parsing of Action statements (private methods)."""
9
+
10
+ def test_simple_action_without_parameters(self) -> None:
11
+ """Test parsing a simple action without parameters."""
12
+ source = """### **Action**: `make noise`
13
+
14
+ <details>
15
+ <summary>Emits the sound of the alarm.</summary>
16
+
17
+ > Define `noise` as Text.
18
+ > Set `noise` to _"WEE-OO WEE-OO WEE-OO"_.
19
+ > Say `noise`.
20
+
21
+ </details>"""
22
+
23
+ parser = Parser()
24
+ program = parser.parse(source)
25
+
26
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
27
+ assert len(program.statements) == 1
28
+
29
+ action_stmt = program.statements[0]
30
+ assert isinstance(action_stmt, ActionStatement)
31
+ assert action_stmt.name.value == "make noise"
32
+ assert len(action_stmt.inputs) == 0
33
+ assert len(action_stmt.outputs) == 0
34
+ assert isinstance(action_stmt.body, BlockStatement)
35
+ assert len(action_stmt.body.statements) == 3
36
+
37
+ # Check first statement: Define `noise` as Text.
38
+ from machine_dialect.ast import DefineStatement
39
+
40
+ define_stmt = action_stmt.body.statements[0]
41
+ assert isinstance(define_stmt, DefineStatement)
42
+ assert define_stmt.name.value == "noise"
43
+
44
+ # Check second statement: Set `noise` to _"WEE-OO WEE-OO WEE-OO"_.
45
+ set_stmt = action_stmt.body.statements[1]
46
+ assert isinstance(set_stmt, SetStatement)
47
+ assert set_stmt.name and set_stmt.name.value == "noise"
48
+
49
+ # Check third statement: Say `noise`.
50
+ say_stmt = action_stmt.body.statements[2]
51
+ from machine_dialect.ast import SayStatement
52
+
53
+ assert isinstance(say_stmt, SayStatement)
54
+
55
+ def test_action_with_heading_level(self) -> None:
56
+ """Test that action heading level (###) is parsed correctly."""
57
+ source = """### **Action**: `calculate`
58
+
59
+ <details>
60
+ <summary>Performs a calculation.</summary>
61
+
62
+ > Define `result` as Whole Number.
63
+ > Set `result` to _42_.
64
+
65
+ </details>"""
66
+
67
+ parser = Parser()
68
+ program = parser.parse(source)
69
+
70
+ assert len(parser.errors) == 0
71
+ assert len(program.statements) == 1
72
+
73
+ action_stmt = program.statements[0]
74
+ assert isinstance(action_stmt, ActionStatement)
75
+ assert action_stmt.name.value == "calculate"
76
+
77
+ def test_action_with_multi_word_name(self) -> None:
78
+ """Test action with multi-word name in backticks."""
79
+ source = """### **Action**: `send alert message`
80
+
81
+ <details>
82
+ <summary>Sends an alert.</summary>
83
+
84
+ > Say _"Alert!"_.
85
+
86
+ </details>"""
87
+
88
+ parser = Parser()
89
+ program = parser.parse(source)
90
+
91
+ assert len(parser.errors) == 0
92
+ assert len(program.statements) == 1
93
+
94
+ action_stmt = program.statements[0]
95
+ assert isinstance(action_stmt, ActionStatement)
96
+ assert action_stmt.name.value == "send alert message"
97
+
98
+ def test_action_plural_form(self) -> None:
99
+ """Test that 'Actions' keyword also works."""
100
+ source = """### **Actions**: `make noise`
101
+
102
+ <details>
103
+ <summary>Emits sound.</summary>
104
+
105
+ > Say _"Noise"_.
106
+
107
+ </details>"""
108
+
109
+ parser = Parser()
110
+ program = parser.parse(source)
111
+
112
+ assert len(parser.errors) == 0
113
+ assert len(program.statements) == 1
114
+
115
+ action_stmt = program.statements[0]
116
+ assert isinstance(action_stmt, ActionStatement)
117
+ assert action_stmt.name.value == "make noise"
118
+
119
+ def test_action_with_empty_body(self) -> None:
120
+ """Test action with no statements in body."""
121
+ source = """### **Action**: `do nothing`
122
+
123
+ <details>
124
+ <summary>Does nothing.</summary>
125
+
126
+ </details>"""
127
+
128
+ parser = Parser()
129
+ program = parser.parse(source)
130
+
131
+ assert len(parser.errors) == 0
132
+ assert len(program.statements) == 1
133
+
134
+ action_stmt = program.statements[0]
135
+ assert isinstance(action_stmt, ActionStatement)
136
+ assert action_stmt.name.value == "do nothing"
137
+ assert len(action_stmt.body.statements) == 0
138
+
139
+ def test_multiple_actions(self) -> None:
140
+ """Test parsing multiple actions in one program."""
141
+ source = """### **Action**: `first action`
142
+
143
+ <details>
144
+ <summary>First action.</summary>
145
+
146
+ > Define `x` as Whole Number.
147
+ > Set `x` to _1_.
148
+
149
+ </details>
150
+
151
+ ### **Action**: `second action`
152
+
153
+ <details>
154
+ <summary>Second action.</summary>
155
+
156
+ > Define `y` as Whole Number.
157
+ > Set `y` to _2_.
158
+
159
+ </details>"""
160
+
161
+ parser = Parser()
162
+ program = parser.parse(source)
163
+
164
+ assert len(parser.errors) == 0
165
+ assert len(program.statements) == 2
166
+
167
+ first_action = program.statements[0]
168
+ assert isinstance(first_action, ActionStatement)
169
+ assert first_action.name.value == "first action"
170
+
171
+ second_action = program.statements[1]
172
+ assert isinstance(second_action, ActionStatement)
173
+ assert second_action.name.value == "second action"
174
+
175
+ def test_action_with_input_parameters(self) -> None:
176
+ """Test parsing an action with input parameters."""
177
+ source = """### **Action**: `make noise`
178
+
179
+ <details>
180
+ <summary>Emits the sound of the alarm.</summary>
181
+
182
+ > Define `noise` as Text.
183
+ > Set `noise` to `sound`.
184
+ > Say `noise`.
185
+
186
+ </details>
187
+
188
+ #### Inputs:
189
+ - `sound` **as** Text (required)
190
+ - `volume` **as** Whole Number (optional, default: _60_)"""
191
+
192
+ parser = Parser()
193
+ program = parser.parse(source)
194
+
195
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
196
+ assert len(program.statements) == 1
197
+
198
+ action_stmt = program.statements[0]
199
+ assert isinstance(action_stmt, ActionStatement)
200
+ assert action_stmt.name.value == "make noise"
201
+
202
+ # Check inputs
203
+ assert len(action_stmt.inputs) == 2
204
+
205
+ # First input: sound (required)
206
+ sound_param = action_stmt.inputs[0]
207
+ assert sound_param.name.value == "sound"
208
+ assert sound_param.type_name == "Text"
209
+ assert sound_param.is_required is True
210
+ assert sound_param.default_value is None
211
+
212
+ # Second input: volume (optional with default)
213
+ volume_param = action_stmt.inputs[1]
214
+ assert volume_param.name.value == "volume"
215
+ assert volume_param.type_name == "Whole Number"
216
+ assert volume_param.is_required is False
217
+ assert volume_param.default_value is not None
218
+ from machine_dialect.ast import WholeNumberLiteral
219
+
220
+ assert isinstance(volume_param.default_value, WholeNumberLiteral)
221
+ assert volume_param.default_value.value == 60
222
+
223
+ # No outputs
224
+ assert len(action_stmt.outputs) == 0
225
+
226
+ def test_action_with_output_parameters(self) -> None:
227
+ """Test parsing an action with output parameters."""
228
+ source = """### **Action**: `calculate`
229
+
230
+ <details>
231
+ <summary>Performs a calculation.</summary>
232
+
233
+ > Define `result` as Number.
234
+ > Set `result` to _42_.
235
+ > Give back `result`.
236
+
237
+ </details>
238
+
239
+ #### Outputs:
240
+ - `result` **as** Number
241
+ - `success` **as** Yes/No"""
242
+
243
+ parser = Parser()
244
+ program = parser.parse(source)
245
+
246
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
247
+ assert len(program.statements) == 1
248
+
249
+ action_stmt = program.statements[0]
250
+ assert isinstance(action_stmt, ActionStatement)
251
+ assert action_stmt.name.value == "calculate"
252
+
253
+ # No inputs
254
+ assert len(action_stmt.inputs) == 0
255
+
256
+ # Check outputs
257
+ assert len(action_stmt.outputs) == 2
258
+
259
+ # First output: result
260
+ result_param = action_stmt.outputs[0]
261
+ assert isinstance(result_param, Output)
262
+ assert result_param.name.value == "result"
263
+ assert result_param.type_name == "Number"
264
+
265
+ # Second output: success
266
+ success_param = action_stmt.outputs[1]
267
+ assert isinstance(success_param, Output)
268
+ assert success_param.name.value == "success"
269
+ assert success_param.type_name == "Yes/No"
270
+
271
+ def test_action_with_both_inputs_and_outputs(self) -> None:
272
+ """Test parsing an action with both input and output parameters."""
273
+ source = """### **Action**: `process data`
274
+
275
+ <details>
276
+ <summary>Processes input data.</summary>
277
+
278
+ > Define `result` as Text.
279
+ > Set `result` to `input`.
280
+ > Give back `result`.
281
+
282
+ </details>
283
+
284
+ #### Inputs:
285
+ - `input` **as** Text (required)
286
+ - `format` **as** Text (optional, default: _"json"_)
287
+
288
+ #### Outputs:
289
+ - `result` **as** Text
290
+ - `error` **as** Text"""
291
+
292
+ parser = Parser()
293
+ program = parser.parse(source)
294
+
295
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
296
+ assert len(program.statements) == 1
297
+
298
+ action_stmt = program.statements[0]
299
+ assert isinstance(action_stmt, ActionStatement)
300
+ assert action_stmt.name.value == "process data"
301
+
302
+ # Check inputs
303
+ assert len(action_stmt.inputs) == 2
304
+
305
+ input_param = action_stmt.inputs[0]
306
+ assert input_param.name.value == "input"
307
+ assert input_param.type_name == "Text"
308
+ assert input_param.is_required is True
309
+
310
+ format_param = action_stmt.inputs[1]
311
+ assert format_param.name.value == "format"
312
+ assert format_param.type_name == "Text"
313
+ assert format_param.is_required is False
314
+ assert format_param.default_value is not None
315
+ from machine_dialect.ast import StringLiteral
316
+
317
+ assert isinstance(format_param.default_value, StringLiteral)
318
+ assert format_param.default_value.value == "json"
319
+
320
+ # Check outputs
321
+ assert len(action_stmt.outputs) == 2
322
+
323
+ result_param = action_stmt.outputs[0]
324
+ assert isinstance(result_param, Output)
325
+ assert result_param.name.value == "result"
326
+ assert result_param.type_name == "Text"
327
+
328
+ error_param = action_stmt.outputs[1]
329
+ assert isinstance(error_param, Output)
330
+ assert error_param.name.value == "error"
331
+ assert error_param.type_name == "Text"
332
+ # Outputs without explicit defaults should have Empty as default
333
+ assert error_param.default_value is not None
334
+ assert isinstance(error_param.default_value, EmptyLiteral)
@@ -0,0 +1,152 @@
1
+ """Tests for parsing boolean literal expressions.
2
+
3
+ This module tests the parser's ability to handle boolean literal expressions
4
+ including Yes and No in both regular and underscore-wrapped forms.
5
+ """
6
+
7
+ import pytest
8
+
9
+ from machine_dialect.ast import ExpressionStatement
10
+ from machine_dialect.parser import Parser
11
+ from machine_dialect.parser.tests.helper_functions import (
12
+ assert_literal_expression,
13
+ assert_program_statements,
14
+ )
15
+
16
+
17
+ class TestBooleanLiteralExpressions:
18
+ """Test parsing of boolean literal expressions."""
19
+
20
+ @pytest.mark.parametrize(
21
+ "source,expected_value",
22
+ [
23
+ # Simple boolean literals
24
+ ("Yes", True),
25
+ ("No", False),
26
+ # Underscore-wrapped boolean literals
27
+ ("_Yes_", True),
28
+ ("_No_", False),
29
+ ],
30
+ )
31
+ def test_boolean_literal_expression(self, source: str, expected_value: bool) -> None:
32
+ """Test parsing various boolean literal expressions.
33
+
34
+ Args:
35
+ source: The source code containing a boolean literal.
36
+ expected_value: The expected boolean value.
37
+ """
38
+ parser = Parser()
39
+
40
+ program = parser.parse(source)
41
+
42
+ assert len(parser.errors) == 0
43
+ assert_program_statements(parser, program)
44
+
45
+ statement = program.statements[0]
46
+ assert isinstance(statement, ExpressionStatement)
47
+ assert statement.expression is not None
48
+
49
+ assert_literal_expression(statement.expression, expected_value)
50
+
51
+ def test_boolean_with_period(self) -> None:
52
+ """Test parsing boolean literal with explicit statement terminator."""
53
+ source = "Yes."
54
+ parser = Parser()
55
+
56
+ program = parser.parse(source)
57
+
58
+ assert len(parser.errors) == 0
59
+ assert_program_statements(parser, program)
60
+
61
+ statement = program.statements[0]
62
+ assert isinstance(statement, ExpressionStatement)
63
+ assert statement.expression is not None
64
+
65
+ assert_literal_expression(statement.expression, True)
66
+
67
+ def test_multiple_boolean_statements(self) -> None:
68
+ """Test parsing multiple boolean literal statements."""
69
+ source = "Yes. No. Yes. No."
70
+ parser = Parser()
71
+
72
+ program = parser.parse(source)
73
+
74
+ assert len(parser.errors) == 0
75
+ assert len(program.statements) == 4
76
+
77
+ # Check each statement
78
+ expected_values = [True, False, True, False]
79
+ for i, expected_value in enumerate(expected_values):
80
+ statement = program.statements[i]
81
+ assert isinstance(statement, ExpressionStatement)
82
+ assert statement.expression is not None
83
+ assert_literal_expression(statement.expression, expected_value)
84
+
85
+ def test_mixed_literals_with_booleans(self) -> None:
86
+ """Test parsing mixed literal types including booleans."""
87
+ source = "Yes. 42. No. 3.14. _Yes_."
88
+ parser = Parser()
89
+
90
+ program = parser.parse(source)
91
+
92
+ assert len(parser.errors) == 0
93
+ assert len(program.statements) == 5
94
+
95
+ # Check boolean statements
96
+ statement = program.statements[0]
97
+ assert isinstance(statement, ExpressionStatement)
98
+ assert statement.expression is not None
99
+ assert_literal_expression(statement.expression, True)
100
+
101
+ statement = program.statements[2]
102
+ assert isinstance(statement, ExpressionStatement)
103
+ assert statement.expression is not None
104
+ assert_literal_expression(statement.expression, False)
105
+
106
+ statement = program.statements[4]
107
+ assert isinstance(statement, ExpressionStatement)
108
+ assert statement.expression is not None
109
+ assert_literal_expression(statement.expression, True)
110
+
111
+ @pytest.mark.parametrize(
112
+ "source,expected_value",
113
+ [
114
+ # Case insensitive variations should all parse as True
115
+ ("yes", True),
116
+ ("YES", True),
117
+ ("YeS", True),
118
+ ("yEs", True),
119
+ # Case insensitive variations should all parse as False
120
+ ("no", False),
121
+ ("NO", False),
122
+ ("No", False),
123
+ ("nO", False),
124
+ # Underscore-wrapped case variations
125
+ ("_yes_", True),
126
+ ("_YES_", True),
127
+ ("_no_", False),
128
+ ("_NO_", False),
129
+ ],
130
+ )
131
+ def test_case_insensitive_boolean_literals(self, source: str, expected_value: bool) -> None:
132
+ """Test that boolean literals are case-insensitive.
133
+
134
+ Boolean literals should be recognized regardless of case and stored
135
+ in their canonical form as defined in tokens.py.
136
+
137
+ Args:
138
+ source: The source code with various case boolean literals.
139
+ expected_value: The expected boolean value.
140
+ """
141
+ parser = Parser()
142
+
143
+ program = parser.parse(source)
144
+
145
+ assert len(parser.errors) == 0
146
+ assert_program_statements(parser, program)
147
+
148
+ statement = program.statements[0]
149
+ assert isinstance(statement, ExpressionStatement)
150
+ assert statement.expression is not None
151
+
152
+ assert_literal_expression(statement.expression, expected_value)
@@ -0,0 +1,154 @@
1
+ """Tests for use statements in Machine Dialect™."""
2
+
3
+ from machine_dialect.ast import (
4
+ Arguments,
5
+ CallStatement,
6
+ Identifier,
7
+ StringLiteral,
8
+ WholeNumberLiteral,
9
+ )
10
+ from machine_dialect.parser import Parser
11
+
12
+
13
+ class TestCallStatements:
14
+ """Test parsing of use statements."""
15
+
16
+ def test_call_without_parameters(self) -> None:
17
+ """Test parsing a use statement without parameters."""
18
+ source = "use `turn alarm off`."
19
+
20
+ parser = Parser()
21
+ program = parser.parse(source, check_semantics=False)
22
+
23
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
24
+ assert len(program.statements) == 1
25
+
26
+ call_stmt = program.statements[0]
27
+ assert isinstance(call_stmt, CallStatement)
28
+ assert isinstance(call_stmt.function_name, Identifier)
29
+ assert call_stmt.function_name.value == "turn alarm off"
30
+ assert call_stmt.arguments is None
31
+
32
+ def test_call_with_positional_arguments(self) -> None:
33
+ """Test parsing a use statement with positional arguments."""
34
+ source = "use `add numbers` with _5_, _10_."
35
+
36
+ parser = Parser()
37
+ program = parser.parse(source, check_semantics=False)
38
+
39
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
40
+ assert len(program.statements) == 1
41
+
42
+ call_stmt = program.statements[0]
43
+ assert isinstance(call_stmt, CallStatement)
44
+ assert isinstance(call_stmt.function_name, Identifier)
45
+ assert call_stmt.function_name.value == "add numbers"
46
+
47
+ assert call_stmt.arguments is not None
48
+ assert isinstance(call_stmt.arguments, Arguments)
49
+ # Should have 2 positional arguments
50
+ assert len(call_stmt.arguments.positional) == 2
51
+ assert len(call_stmt.arguments.named) == 0
52
+
53
+ # First argument
54
+ assert isinstance(call_stmt.arguments.positional[0], WholeNumberLiteral)
55
+ assert call_stmt.arguments.positional[0].value == 5
56
+
57
+ # Second argument
58
+ assert isinstance(call_stmt.arguments.positional[1], WholeNumberLiteral)
59
+ assert call_stmt.arguments.positional[1].value == 10
60
+
61
+ def test_call_with_named_arguments(self) -> None:
62
+ """Test parsing a use statement with named arguments."""
63
+ source = 'use `make noise` where `sound` is _"WEE-OO WEE-OO WEE-OO"_, `volume` is _80_.'
64
+
65
+ parser = Parser()
66
+ program = parser.parse(source, check_semantics=False)
67
+
68
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
69
+ assert len(program.statements) == 1
70
+
71
+ call_stmt = program.statements[0]
72
+ assert isinstance(call_stmt, CallStatement)
73
+ assert isinstance(call_stmt.function_name, Identifier)
74
+ assert call_stmt.function_name.value == "make noise"
75
+
76
+ assert call_stmt.arguments is not None
77
+ assert isinstance(call_stmt.arguments, Arguments)
78
+ # Should have 2 named arguments
79
+ assert len(call_stmt.arguments.positional) == 0
80
+ assert len(call_stmt.arguments.named) == 2
81
+
82
+ # First named argument: sound
83
+ name0, val0 = call_stmt.arguments.named[0]
84
+ assert isinstance(name0, Identifier)
85
+ assert name0.value == "sound"
86
+ assert isinstance(val0, StringLiteral)
87
+ assert val0.value == "WEE-OO WEE-OO WEE-OO"
88
+
89
+ # Second named argument: volume
90
+ name1, val1 = call_stmt.arguments.named[1]
91
+ assert isinstance(name1, Identifier)
92
+ assert name1.value == "volume"
93
+ assert isinstance(val1, WholeNumberLiteral)
94
+ assert val1.value == 80
95
+
96
+ def test_call_with_identifier_as_function_name(self) -> None:
97
+ """Test parsing a use statement with an identifier as the function name."""
98
+ source = 'use `my_function` with _"test"_.'
99
+
100
+ parser = Parser()
101
+ program = parser.parse(source, check_semantics=False)
102
+
103
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
104
+ assert len(program.statements) == 1
105
+
106
+ call_stmt = program.statements[0]
107
+ assert isinstance(call_stmt, CallStatement)
108
+ assert isinstance(call_stmt.function_name, Identifier)
109
+ assert call_stmt.function_name.value == "my_function"
110
+
111
+ def test_call_without_period(self) -> None:
112
+ """Test that use statement without period fails."""
113
+ source = "use `my_function`"
114
+
115
+ parser = Parser()
116
+ parser.parse(source)
117
+
118
+ # Should have an error about missing period
119
+ assert len(parser.errors) > 0
120
+ assert any("period" in str(err).lower() for err in parser.errors)
121
+
122
+ def test_multiple_call_statements(self) -> None:
123
+ """Test parsing multiple use statements."""
124
+ source = """use `start process`.
125
+ use `log message` with _"Process started"_.
126
+ use `stop process`.
127
+ """
128
+
129
+ parser = Parser()
130
+ program = parser.parse(source, check_semantics=False)
131
+
132
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
133
+ assert len(program.statements) == 3
134
+
135
+ # First call
136
+ call1 = program.statements[0]
137
+ assert isinstance(call1, CallStatement)
138
+ assert isinstance(call1.function_name, Identifier)
139
+ assert call1.function_name.value == "start process"
140
+ assert call1.arguments is None
141
+
142
+ # Second call
143
+ call2 = program.statements[1]
144
+ assert isinstance(call2, CallStatement)
145
+ assert isinstance(call2.function_name, Identifier)
146
+ assert call2.function_name.value == "log message"
147
+ assert call2.arguments is not None
148
+
149
+ # Third call
150
+ call3 = program.statements[2]
151
+ assert isinstance(call3, CallStatement)
152
+ assert isinstance(call3.function_name, Identifier)
153
+ assert call3.function_name.value == "stop process"
154
+ assert call3.arguments is None