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,415 @@
1
+ """Tests for parsing prefix expressions.
2
+
3
+ This module tests the parser's ability to handle prefix expressions including:
4
+ - Negative numbers (-42, -3.14)
5
+ - Boolean negation (not True, not False)
6
+ """
7
+
8
+ import pytest
9
+
10
+ from machine_dialect.ast import ExpressionStatement, Identifier, PrefixExpression
11
+ from machine_dialect.parser import Parser
12
+ from machine_dialect.parser.tests.helper_functions import (
13
+ assert_literal_expression,
14
+ assert_program_statements,
15
+ )
16
+
17
+
18
+ class TestPrefixExpressions:
19
+ """Test parsing of prefix expressions."""
20
+
21
+ @pytest.mark.parametrize(
22
+ "source,operator,value",
23
+ [
24
+ # Negative integers
25
+ ("-5", "-", 5),
26
+ ("-42", "-", 42),
27
+ ("-123", "-", 123),
28
+ ("-0", "-", 0),
29
+ ("-999", "-", 999),
30
+ # Negative integers with underscores wrapping the positive number
31
+ ("-_5_", "-", 5),
32
+ ("-_42_", "-", 42),
33
+ ("-_123_", "-", 123),
34
+ # Underscore-wrapped negative integers (entire expression)
35
+ # ("_-5_", "-", 5),
36
+ # ("_-42_", "-", 42),
37
+ # ("_-123_", "-", 123),
38
+ ],
39
+ )
40
+ def test_negative_integer_expressions(self, source: str, operator: str, value: int) -> None:
41
+ """Test parsing negative integer expressions.
42
+
43
+ Args:
44
+ source: The source code containing a negative integer.
45
+ operator: The expected operator (should be "-").
46
+ value: The expected positive integer value.
47
+ """
48
+ parser = Parser()
49
+
50
+ program = parser.parse(source)
51
+
52
+ assert len(parser.errors) == 0
53
+ assert_program_statements(parser, program)
54
+
55
+ statement = program.statements[0]
56
+ assert isinstance(statement, ExpressionStatement)
57
+ assert statement.expression is not None
58
+
59
+ # Check that it's a prefix expression
60
+ assert isinstance(statement.expression, PrefixExpression)
61
+ prefix_exp = statement.expression
62
+
63
+ # Check the operator
64
+ assert prefix_exp.operator == operator
65
+
66
+ # Check the right expression (should be the positive number)
67
+ assert prefix_exp.right is not None
68
+ assert_literal_expression(prefix_exp.right, value)
69
+
70
+ @pytest.mark.parametrize(
71
+ "source,operator,value",
72
+ [
73
+ # Negative floats
74
+ ("-3.14", "-", 3.14),
75
+ ("-0.5", "-", 0.5),
76
+ ("-123.456", "-", 123.456),
77
+ ("-0.0", "-", 0.0),
78
+ ("-.5", "-", 0.5),
79
+ ("-.25", "-", 0.25),
80
+ # Negative floats with underscores wrapping the positive number
81
+ ("-_3.14_", "-", 3.14),
82
+ ("-_0.5_", "-", 0.5),
83
+ ("-_.25_", "-", 0.25),
84
+ # Underscore-wrapped negative floats (entire expression)
85
+ # ("_-3.14_", "-", 3.14),
86
+ # ("_-0.5_", "-", 0.5),
87
+ # ("_-.25_", "-", 0.25),
88
+ ],
89
+ )
90
+ def test_negative_float_expressions(self, source: str, operator: str, value: float) -> None:
91
+ """Test parsing negative float expressions.
92
+
93
+ Args:
94
+ source: The source code containing a negative float.
95
+ operator: The expected operator (should be "-").
96
+ value: The expected positive float value.
97
+ """
98
+ parser = Parser()
99
+
100
+ program = parser.parse(source)
101
+
102
+ assert len(parser.errors) == 0
103
+ assert_program_statements(parser, program)
104
+
105
+ statement = program.statements[0]
106
+ assert isinstance(statement, ExpressionStatement)
107
+ assert statement.expression is not None
108
+
109
+ # Check that it's a prefix expression
110
+ assert isinstance(statement.expression, PrefixExpression)
111
+ prefix_exp = statement.expression
112
+
113
+ # Check the operator
114
+ assert prefix_exp.operator == operator
115
+
116
+ # Check the right expression (should be the positive number)
117
+ assert prefix_exp.right is not None
118
+ assert_literal_expression(prefix_exp.right, value)
119
+
120
+ @pytest.mark.parametrize(
121
+ "source,operator,value",
122
+ [
123
+ # Boolean negation
124
+ ("not Yes", "not", True),
125
+ ("not No", "not", False),
126
+ # Boolean negation with underscores
127
+ ("not _Yes_", "not", True),
128
+ ("not _No_", "not", False),
129
+ # Underscore-wrapped entire negation expression
130
+ # ("_not True_", "not", True),
131
+ # ("_not False_", "not", False),
132
+ # Case insensitive NOT
133
+ ("NOT Yes", "not", True),
134
+ ("NOT No", "not", False),
135
+ ("Not Yes", "not", True),
136
+ ("Not No", "not", False),
137
+ ("nOt Yes", "not", True),
138
+ ("nOt No", "not", False),
139
+ # Case insensitive boolean values with negation
140
+ ("not yes", "not", True),
141
+ ("not no", "not", False),
142
+ ("NOT YES", "not", True),
143
+ ("NOT NO", "not", False),
144
+ ],
145
+ )
146
+ def test_boolean_negation_expressions(self, source: str, operator: str, value: bool) -> None:
147
+ """Test parsing boolean negation expressions.
148
+
149
+ Args:
150
+ source: The source code containing a negated boolean.
151
+ operator: The expected operator (should be "not").
152
+ value: The expected boolean value being negated.
153
+ """
154
+ parser = Parser()
155
+
156
+ program = parser.parse(source)
157
+
158
+ assert len(parser.errors) == 0
159
+ assert_program_statements(parser, program)
160
+
161
+ statement = program.statements[0]
162
+ assert isinstance(statement, ExpressionStatement)
163
+ assert statement.expression is not None
164
+
165
+ # Check that it's a prefix expression
166
+ assert isinstance(statement.expression, PrefixExpression)
167
+ prefix_exp = statement.expression
168
+
169
+ # Check the operator (should be lowercase "not")
170
+ assert prefix_exp.operator == operator
171
+
172
+ # Check the right expression (should be the boolean value)
173
+ assert prefix_exp.right is not None
174
+ assert_literal_expression(prefix_exp.right, value)
175
+
176
+ def test_multiple_prefix_expressions(self) -> None:
177
+ """Test parsing multiple prefix expressions in sequence."""
178
+ source = "-42. not Yes. -3.14. not No."
179
+ parser = Parser()
180
+
181
+ program = parser.parse(source)
182
+
183
+ assert len(parser.errors) == 0
184
+ assert len(program.statements) == 4
185
+
186
+ # First statement: -42
187
+ statement = program.statements[0]
188
+ assert isinstance(statement, ExpressionStatement)
189
+ assert statement.expression is not None
190
+ assert isinstance(statement.expression, PrefixExpression)
191
+ assert statement.expression.operator == "-"
192
+ assert statement.expression.right is not None
193
+ assert_literal_expression(statement.expression.right, 42)
194
+
195
+ # Second statement: not True
196
+ statement = program.statements[1]
197
+ assert isinstance(statement, ExpressionStatement)
198
+ assert statement.expression is not None
199
+ assert isinstance(statement.expression, PrefixExpression)
200
+ assert statement.expression.operator == "not"
201
+ assert statement.expression.right is not None
202
+ assert_literal_expression(statement.expression.right, True)
203
+
204
+ # Third statement: -3.14
205
+ statement = program.statements[2]
206
+ assert isinstance(statement, ExpressionStatement)
207
+ assert statement.expression is not None
208
+ assert isinstance(statement.expression, PrefixExpression)
209
+ assert statement.expression.operator == "-"
210
+ assert statement.expression.right is not None
211
+ assert_literal_expression(statement.expression.right, 3.14)
212
+
213
+ # Fourth statement: not False
214
+ statement = program.statements[3]
215
+ assert isinstance(statement, ExpressionStatement)
216
+ assert statement.expression is not None
217
+ assert isinstance(statement.expression, PrefixExpression)
218
+ assert statement.expression.operator == "not"
219
+ assert statement.expression.right is not None
220
+ assert_literal_expression(statement.expression.right, False)
221
+
222
+ def test_prefix_expression_with_identifier(self) -> None:
223
+ """Test prefix expressions with identifiers."""
224
+ source = "-x. not `is valid`."
225
+ parser = Parser()
226
+
227
+ program = parser.parse(source)
228
+
229
+ assert len(parser.errors) == 0
230
+ assert len(program.statements) == 2
231
+
232
+ # First statement: -x
233
+ statement = program.statements[0]
234
+ assert isinstance(statement, ExpressionStatement)
235
+ assert statement.expression is not None
236
+ assert isinstance(statement.expression, PrefixExpression)
237
+ assert statement.expression.operator == "-"
238
+ assert statement.expression.right is not None
239
+ assert_literal_expression(statement.expression.right, "x")
240
+
241
+ # Second statement: not `is valid`
242
+ statement = program.statements[1]
243
+ assert isinstance(statement, ExpressionStatement)
244
+ assert statement.expression is not None
245
+ assert isinstance(statement.expression, PrefixExpression)
246
+ assert statement.expression.operator == "not"
247
+ assert statement.expression.right is not None
248
+ assert_literal_expression(statement.expression.right, "is valid")
249
+
250
+ def test_double_negation(self) -> None:
251
+ """Test parsing double negation expressions."""
252
+ source = "--42. not not Yes."
253
+ parser = Parser()
254
+
255
+ program = parser.parse(source)
256
+
257
+ assert len(parser.errors) == 0
258
+ assert len(program.statements) == 2
259
+
260
+ # First statement: --42 (negative of negative 42)
261
+ statement = program.statements[0]
262
+ assert isinstance(statement, ExpressionStatement)
263
+ assert statement.expression is not None
264
+ assert isinstance(statement.expression, PrefixExpression)
265
+ assert statement.expression.operator == "-"
266
+
267
+ # The right side should be another prefix expression
268
+ assert statement.expression.right is not None
269
+ assert isinstance(statement.expression.right, PrefixExpression)
270
+ assert statement.expression.right.operator == "-"
271
+ assert statement.expression.right.right is not None
272
+ assert_literal_expression(statement.expression.right.right, 42)
273
+
274
+ # Second statement: not not True
275
+ statement = program.statements[1]
276
+ assert isinstance(statement, ExpressionStatement)
277
+ assert statement.expression is not None
278
+ assert isinstance(statement.expression, PrefixExpression)
279
+ assert statement.expression.operator == "not"
280
+
281
+ # The right side should be another prefix expression
282
+ assert statement.expression.right is not None
283
+ assert isinstance(statement.expression.right, PrefixExpression)
284
+ assert statement.expression.right.operator == "not"
285
+ assert statement.expression.right.right is not None
286
+ assert_literal_expression(statement.expression.right.right, True)
287
+
288
+ def test_boolean_negation_with_identifiers(self) -> None:
289
+ """Test parsing boolean negation with identifier expressions."""
290
+ test_cases = [
291
+ ("not x", "not", "x"),
292
+ ("not isValid", "not", "isValid"),
293
+ ("not `is valid`", "not", "is valid"),
294
+ ("not foo", "not", "foo"),
295
+ ("NOT bar", "not", "bar"),
296
+ ]
297
+
298
+ for source, expected_operator, expected_identifier in test_cases:
299
+ parser = Parser()
300
+
301
+ program = parser.parse(source)
302
+
303
+ assert len(parser.errors) == 0, f"Parser errors for '{source}': {parser.errors}"
304
+ assert_program_statements(parser, program)
305
+
306
+ statement = program.statements[0]
307
+ assert isinstance(statement, ExpressionStatement)
308
+ assert statement.expression is not None
309
+
310
+ # Check it's a prefix expression
311
+ prefix_exp = statement.expression
312
+ assert isinstance(prefix_exp, PrefixExpression)
313
+ assert prefix_exp.operator == expected_operator
314
+
315
+ # Check the right side is an identifier
316
+ assert prefix_exp.right is not None
317
+ assert isinstance(prefix_exp.right, Identifier)
318
+ assert prefix_exp.right.value == expected_identifier
319
+
320
+ def test_boolean_negation_with_non_boolean_literals(self) -> None:
321
+ """Test that using 'not' with non-boolean literals parses successfully.
322
+
323
+ Note: Since this language follows Java's approach where 'not' only accepts
324
+ boolean values, type checking would happen at a later semantic analysis stage,
325
+ not during parsing. The parser accepts these syntactically valid constructs.
326
+ """
327
+ test_cases = [
328
+ ("not 42", "not", 42),
329
+ ("not 0", "not", 0),
330
+ ("not 3.14", "not", 3.14),
331
+ ("not 0.0", "not", 0.0),
332
+ # ("not -5", "not", -5), # This creates nested prefix expressions: not (- 5)
333
+ ("NOT 123", "not", 123),
334
+ # String literals - uncomment when string parsing is implemented
335
+ # ("not \"hello\"", "not", "hello"),
336
+ # ("not 'world'", "not", "world"),
337
+ # ("not \"\"", "not", ""),
338
+ ]
339
+
340
+ for source, expected_operator, expected_value in test_cases:
341
+ parser = Parser()
342
+
343
+ program = parser.parse(source)
344
+
345
+ # These should parse successfully - type errors would be caught in semantic analysis
346
+ assert len(parser.errors) == 0, f"Parser errors for '{source}': {parser.errors}"
347
+ assert_program_statements(parser, program)
348
+
349
+ statement = program.statements[0]
350
+ assert isinstance(statement, ExpressionStatement)
351
+ assert statement.expression is not None
352
+
353
+ # Check it's a prefix expression
354
+ prefix_exp = statement.expression
355
+ assert isinstance(prefix_exp, PrefixExpression)
356
+ assert prefix_exp.operator == expected_operator
357
+
358
+ # The right side should be parsed as the appropriate literal
359
+ assert prefix_exp.right is not None
360
+ assert_literal_expression(prefix_exp.right, expected_value)
361
+
362
+ def test_boolean_negation_with_nested_prefix(self) -> None:
363
+ """Test that 'not -5' creates nested prefix expressions."""
364
+ source = "not -5"
365
+ parser = Parser()
366
+
367
+ program = parser.parse(source)
368
+
369
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
370
+ assert_program_statements(parser, program)
371
+
372
+ statement = program.statements[0]
373
+ assert isinstance(statement, ExpressionStatement)
374
+ assert statement.expression is not None
375
+
376
+ # Check outer prefix expression (not)
377
+ outer_prefix = statement.expression
378
+ assert isinstance(outer_prefix, PrefixExpression)
379
+ assert outer_prefix.operator == "not"
380
+
381
+ # Check inner prefix expression (-)
382
+ assert outer_prefix.right is not None
383
+ inner_prefix = outer_prefix.right
384
+ assert isinstance(inner_prefix, PrefixExpression)
385
+ assert inner_prefix.operator == "-"
386
+
387
+ # Check the innermost literal (5)
388
+ assert inner_prefix.right is not None
389
+ assert_literal_expression(inner_prefix.right, 5)
390
+
391
+ def test_prefix_expression_string_representation(self) -> None:
392
+ """Test the string representation of prefix expressions."""
393
+ # Test cases with expected string representations
394
+ test_cases = [
395
+ ("-42", "(-_42_)"),
396
+ ("not Yes", "(not _Yes_)"),
397
+ ("-3.14", "(-_3.14_)"),
398
+ ("not No", "(not _No_)"),
399
+ ("--5", "(-(-_5_))"),
400
+ ("not not Yes", "(not (not _Yes_))"),
401
+ ]
402
+
403
+ for source, expected in test_cases:
404
+ parser = Parser()
405
+ program = parser.parse(source)
406
+
407
+ assert len(parser.errors) == 0
408
+ assert len(program.statements) == 1
409
+
410
+ statement = program.statements[0]
411
+ assert isinstance(statement, ExpressionStatement)
412
+ assert statement.expression is not None
413
+
414
+ # Check string representation
415
+ assert str(statement.expression) == expected
@@ -0,0 +1,13 @@
1
+ from machine_dialect.ast import Program
2
+ from machine_dialect.parser import Parser
3
+
4
+
5
+ class TestParser:
6
+ def test_parse_program(self) -> None:
7
+ source: str = "Set `X` to 5."
8
+ parser: Parser = Parser()
9
+
10
+ program: Program = parser.parse(source)
11
+
12
+ assert program is not None
13
+ assert isinstance(program, Program)
@@ -0,0 +1,89 @@
1
+ """Tests for return statement parsing."""
2
+
3
+ from machine_dialect.ast import ReturnStatement
4
+ from machine_dialect.parser import Parser
5
+
6
+
7
+ class TestReturnStatements:
8
+ """Test parsing of return statements."""
9
+
10
+ def test_give_back_statement(self) -> None:
11
+ """Test parsing 'give back' return statement."""
12
+ source = "give back 42"
13
+ parser = Parser()
14
+
15
+ program = parser.parse(source, check_semantics=False)
16
+
17
+ assert len(parser.errors) == 0, f"Parser had errors: {parser.errors}"
18
+ assert len(program.statements) == 1
19
+
20
+ statement = program.statements[0]
21
+ assert isinstance(statement, ReturnStatement)
22
+ assert statement.token.literal == "give back"
23
+
24
+ def test_gives_back_statement(self) -> None:
25
+ """Test parsing 'gives back' return statement."""
26
+ source = "gives back true"
27
+ parser = Parser()
28
+
29
+ program = parser.parse(source, check_semantics=False)
30
+
31
+ assert len(parser.errors) == 0, f"Parser had errors: {parser.errors}"
32
+ assert len(program.statements) == 1
33
+
34
+ statement = program.statements[0]
35
+ assert isinstance(statement, ReturnStatement)
36
+ assert statement.token.literal == "gives back"
37
+
38
+ def test_multiple_return_statements(self) -> None:
39
+ """Test parsing multiple return statements."""
40
+ source = """
41
+ give back 1.
42
+ gives back 2.
43
+ """
44
+ parser = Parser()
45
+
46
+ program = parser.parse(source, check_semantics=False)
47
+
48
+ assert len(parser.errors) == 0, f"Parser had errors: {parser.errors}"
49
+ assert len(program.statements) == 2
50
+
51
+ # First statement
52
+ statement1 = program.statements[0]
53
+ assert isinstance(statement1, ReturnStatement)
54
+ assert statement1.token.literal == "give back"
55
+
56
+ # Second statement
57
+ statement2 = program.statements[1]
58
+ assert isinstance(statement2, ReturnStatement)
59
+ assert statement2.token.literal == "gives back"
60
+
61
+ def test_return_with_set_statement(self) -> None:
62
+ """Test parsing return statement followed by set statement."""
63
+ source = """
64
+ Define `x` as Whole Number.
65
+ give back 42.
66
+ Set `x` to 10
67
+ """
68
+ parser = Parser()
69
+
70
+ program = parser.parse(source, check_semantics=False)
71
+
72
+ assert len(parser.errors) == 0, f"Parser had errors: {parser.errors}"
73
+ assert len(program.statements) == 3
74
+
75
+ # First statement should be define
76
+ from machine_dialect.ast import DefineStatement, SetStatement
77
+
78
+ statement1 = program.statements[0]
79
+ assert isinstance(statement1, DefineStatement)
80
+
81
+ # Second statement should be return
82
+ statement2 = program.statements[1]
83
+ assert isinstance(statement2, ReturnStatement)
84
+ assert statement2.token.literal == "give back"
85
+
86
+ # Third statement should be set
87
+ statement3 = program.statements[2]
88
+ assert isinstance(statement3, SetStatement)
89
+ assert statement3.token.literal == "Set"
@@ -0,0 +1,152 @@
1
+ import pytest
2
+
3
+ from machine_dialect.ast import Identifier, Program, SetStatement
4
+ from machine_dialect.lexer import TokenType
5
+ from machine_dialect.parser import Parser
6
+
7
+
8
+ class TestSetStatements:
9
+ def test_parse_set_integer(self) -> None:
10
+ source: str = "Set `X` to 1"
11
+ parser: Parser = Parser()
12
+
13
+ program: Program = parser.parse(source)
14
+
15
+ assert program is not None
16
+ assert len(program.statements) == 1
17
+
18
+ statement = program.statements[0]
19
+ assert isinstance(statement, SetStatement)
20
+ assert statement.token.literal == "Set"
21
+
22
+ assert statement.name is not None
23
+ assert isinstance(statement.name, Identifier)
24
+ assert statement.name.value == "X"
25
+ assert statement.name.token.literal == "X"
26
+
27
+ def test_parse_set_float(self) -> None:
28
+ source: str = "Set `price` to 3.14"
29
+ parser: Parser = Parser()
30
+
31
+ program: Program = parser.parse(source)
32
+
33
+ assert program is not None
34
+ assert len(program.statements) == 1
35
+
36
+ statement = program.statements[0]
37
+ assert isinstance(statement, SetStatement)
38
+ assert statement.token.literal == "Set"
39
+
40
+ assert statement.name is not None
41
+ assert isinstance(statement.name, Identifier)
42
+ assert statement.name.value == "price"
43
+ assert statement.name.token.literal == "price"
44
+
45
+ def test_parse_set_string(self) -> None:
46
+ source: str = 'Set `Z` to "Hello, World!"'
47
+ parser: Parser = Parser()
48
+
49
+ program: Program = parser.parse(source)
50
+
51
+ assert program is not None
52
+ assert len(program.statements) == 1
53
+
54
+ statement = program.statements[0]
55
+ assert isinstance(statement, SetStatement)
56
+ assert statement.token.literal == "Set"
57
+
58
+ assert statement.name is not None
59
+ assert isinstance(statement.name, Identifier)
60
+ assert statement.name.value == "Z"
61
+ assert statement.name.token.literal == "Z"
62
+
63
+ def test_parse_multiple_set_statements(self) -> None:
64
+ source: str = "\n".join(
65
+ [
66
+ "Set `X` to 1.",
67
+ "Set `price` to 3.14.",
68
+ 'Set `Z` to "Hello, World!".',
69
+ ]
70
+ )
71
+
72
+ parser: Parser = Parser()
73
+
74
+ program: Program = parser.parse(source)
75
+
76
+ assert program is not None
77
+ assert len(program.statements) == 3
78
+
79
+ # Check first statement
80
+ statement1 = program.statements[0]
81
+ assert isinstance(statement1, SetStatement)
82
+ assert statement1.name is not None
83
+ assert statement1.name.value == "X"
84
+ assert statement1.name.token.type == TokenType.MISC_IDENT
85
+ assert statement1.name.token.literal == "X"
86
+
87
+ # Check second statement
88
+ statement2 = program.statements[1]
89
+ assert isinstance(statement2, SetStatement)
90
+ assert statement2.name is not None
91
+ assert statement2.name.value == "price"
92
+ assert statement2.name.token.type == TokenType.MISC_IDENT
93
+ assert statement2.name.token.literal == "price"
94
+
95
+ # Check third statement
96
+ statement3 = program.statements[2]
97
+ assert isinstance(statement3, SetStatement)
98
+ assert statement3.name is not None
99
+ assert statement3.name.value == "Z"
100
+ assert statement3.name.token.type == TokenType.MISC_IDENT
101
+ assert statement3.name.token.literal == "Z"
102
+
103
+ def test_set_statement_string_representation(self) -> None:
104
+ source: str = "Set `X` to 1"
105
+ parser: Parser = Parser()
106
+
107
+ program: Program = parser.parse(source)
108
+
109
+ assert program is not None
110
+ statement = program.statements[0]
111
+ assert isinstance(statement, SetStatement)
112
+
113
+ # Test the string representation
114
+ program_str = str(program)
115
+ assert program_str is not None # Should have some string representation
116
+
117
+ @pytest.mark.parametrize(
118
+ "source",
119
+ [
120
+ "Set `foo` to 1 if `certain_condition`, else 0",
121
+ "Set `foo` to 1 if `certain_condition`, otherwise 0",
122
+ "Set `foo` to 1 when `certain_condition`, else 0",
123
+ "Set `foo` to 1 when `certain_condition`, otherwise 0",
124
+ "Set `foo` to 1 whenever `certain_condition`, else 0",
125
+ "Set `foo` to 1 whenever `certain_condition`, otherwise 0",
126
+ "Set `foo` to 1 if `certain_condition`; else 0",
127
+ "Set `foo` to 1 if `certain_condition`; otherwise 0",
128
+ "Set `foo` to 1 when `certain_condition`; else 0",
129
+ "Set `foo` to 1 when `certain_condition`; otherwise 0",
130
+ "Set `foo` to 1 whenever `certain_condition`; else 0",
131
+ "Set `foo` to 1 whenever `certain_condition`; otherwise 0",
132
+ ],
133
+ )
134
+ def test_parse_ternary_expressions(self, source: str) -> None:
135
+ parser: Parser = Parser()
136
+
137
+ program: Program = parser.parse(source)
138
+
139
+ assert program is not None
140
+ assert len(program.statements) == 1
141
+
142
+ statement = program.statements[0]
143
+ assert isinstance(statement, SetStatement)
144
+ assert statement.token.literal == "Set"
145
+
146
+ assert statement.name is not None
147
+ assert isinstance(statement.name, Identifier)
148
+ assert statement.name.value == "foo"
149
+ assert statement.name.token.literal == "foo"
150
+
151
+ # The value should be a ternary/conditional expression
152
+ assert statement.value is not None