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,181 @@
1
+ """Tests specifically for MD101: Statement Termination rule."""
2
+
3
+ import pytest
4
+
5
+ from machine_dialect.ast import ExpressionStatement, ReturnStatement, SetStatement
6
+ from machine_dialect.lexer import Token, TokenType
7
+ from machine_dialect.linter import Linter
8
+ from machine_dialect.linter.rules.base import Context
9
+ from machine_dialect.linter.rules.statement_termination import StatementTerminationRule
10
+ from machine_dialect.linter.violations import ViolationSeverity
11
+
12
+
13
+ class TestMD101StatementTermination:
14
+ """Test MD101: Statement Termination rule."""
15
+
16
+ def test_rule_metadata(self) -> None:
17
+ """Test that MD101 has correct metadata."""
18
+ rule = StatementTerminationRule()
19
+ assert rule.rule_id == "MD101"
20
+ assert "period" in rule.description.lower()
21
+ assert "statement" in rule.description.lower()
22
+
23
+ @pytest.mark.parametrize(
24
+ ("source", "expected_violations"),
25
+ [
26
+ # Valid cases - no violations
27
+ ("42.", 0),
28
+ ("True.", 0),
29
+ ("not False.", 0),
30
+ ("-123.", 0),
31
+ ("Set `x` to 42.", 0),
32
+ ("give back 42.", 0),
33
+ ("gives back True.", 0),
34
+ # Valid cases - no period needed at EOF
35
+ ("42", 0),
36
+ ("True", 0),
37
+ ("not False", 0),
38
+ ("-123", 0),
39
+ # Multiple statements
40
+ ("42. True.", 0),
41
+ # Note: These now generate parse errors, not MD101 violations
42
+ # because periods are mandatory (except at EOF) at the parser level
43
+ ("42\nTrue.", 0), # Parse error, not MD101
44
+ ("42.\nTrue", 0), # Valid: second line at EOF
45
+ ("42\nTrue", 0), # Parse error, not MD101
46
+ ],
47
+ )
48
+ def test_full_integration(self, source: str, expected_violations: int) -> None:
49
+ """Test MD101 through the full linter integration."""
50
+ linter = Linter()
51
+ violations = linter.lint(source)
52
+
53
+ # Filter for MD101 violations only
54
+ md101_violations = [v for v in violations if v.rule_id == "MD101"]
55
+ assert len(md101_violations) == expected_violations
56
+
57
+ # All MD101 violations should be STYLE severity
58
+ for violation in md101_violations:
59
+ assert violation.severity == ViolationSeverity.STYLE
60
+ assert "period" in violation.message.lower()
61
+
62
+ def test_statements_with_complex_content(self) -> None:
63
+ """Test MD101 with complex statement content."""
64
+ rule = StatementTerminationRule()
65
+
66
+ # Test with expression that has operators
67
+ token = Token(TokenType.OP_MINUS, "-", line=1, position=0)
68
+ node = ExpressionStatement(token=token, expression=None)
69
+
70
+ # Source: "-42 + 5" (no period, at EOF - valid)
71
+ context = Context("test.md", "-42 + 5")
72
+ violations = rule.check(node, context)
73
+ assert len(violations) == 0 # No period needed at EOF
74
+
75
+ # Source: "-42 + 5." (with period)
76
+ context = Context("test.md", "-42 + 5.")
77
+ violations = rule.check(node, context)
78
+ assert len(violations) == 0
79
+
80
+ def test_set_statements(self) -> None:
81
+ """Test MD101 specifically with Set statements."""
82
+ rule = StatementTerminationRule()
83
+
84
+ # Create a Set statement node
85
+ token = Token(TokenType.KW_SET, "Set", line=1, position=0)
86
+ node = SetStatement(token=token)
87
+
88
+ # Test without period (at EOF - valid)
89
+ context = Context("test.md", "Set `x` to 42")
90
+ violations = rule.check(node, context)
91
+ assert len(violations) == 0 # No period needed at EOF
92
+
93
+ # Test with period
94
+ context = Context("test.md", "Set `x` to 42.")
95
+ violations = rule.check(node, context)
96
+ assert len(violations) == 0
97
+
98
+ def test_return_statements(self) -> None:
99
+ """Test MD101 specifically with Return statements."""
100
+ rule = StatementTerminationRule()
101
+
102
+ # Create a Return statement node
103
+ token = Token(TokenType.KW_RETURN, "give back", line=1, position=0)
104
+ node = ReturnStatement(token=token)
105
+
106
+ # Test without period (at EOF - valid)
107
+ context = Context("test.md", "give back 42")
108
+ violations = rule.check(node, context)
109
+ assert len(violations) == 0 # No period needed at EOF
110
+
111
+ # Test with period
112
+ context = Context("test.md", "give back 42.")
113
+ violations = rule.check(node, context)
114
+ assert len(violations) == 0
115
+
116
+ def test_multiline_source(self) -> None:
117
+ """Test MD101 with multiline source code."""
118
+ rule = StatementTerminationRule()
119
+
120
+ source = """42.
121
+ True
122
+ -5.
123
+ not False"""
124
+
125
+ # Test line 2 (True without period)
126
+ token = Token(TokenType.LIT_YES, "True", line=2, position=0)
127
+ node = ExpressionStatement(token=token, expression=None)
128
+ context = Context("test.md", source)
129
+ violations = rule.check(node, context)
130
+ assert len(violations) == 1
131
+ assert violations[0].line == 2
132
+
133
+ # Test line 4 (not False without period, at EOF - valid)
134
+ token = Token(TokenType.KW_NEGATION, "not", line=4, position=0)
135
+ node = ExpressionStatement(token=token, expression=None)
136
+ violations = rule.check(node, context)
137
+ assert len(violations) == 0 # No period needed at EOF
138
+
139
+ def test_fix_suggestion(self) -> None:
140
+ """Test that MD101 provides fix suggestions."""
141
+ rule = StatementTerminationRule()
142
+
143
+ # Test with a statement not at EOF
144
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=0)
145
+ node = ExpressionStatement(token=token, expression=None)
146
+ context = Context("test.md", "42\nTrue.") # Not at EOF
147
+
148
+ violations = rule.check(node, context)
149
+ assert len(violations) == 1
150
+ assert violations[0].fix_suggestion is not None
151
+ assert "period" in violations[0].fix_suggestion.lower()
152
+
153
+ def test_edge_cases(self) -> None:
154
+ """Test MD101 edge cases."""
155
+ rule = StatementTerminationRule()
156
+
157
+ # Empty line
158
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=0)
159
+ node = ExpressionStatement(token=token, expression=None)
160
+ context = Context("test.md", "")
161
+ violations = rule.check(node, context)
162
+ assert len(violations) == 0 # No crash on empty source
163
+
164
+ # Line number out of bounds
165
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=10, position=0)
166
+ node = ExpressionStatement(token=token, expression=None)
167
+ context = Context("test.md", "42")
168
+ violations = rule.check(node, context)
169
+ assert len(violations) == 0 # No crash on invalid line number
170
+
171
+ def test_disabled_rule(self) -> None:
172
+ """Test that MD101 can be disabled."""
173
+ # Test with MD101 disabled
174
+ config = {"rules": {"MD101": False}}
175
+ linter = Linter(config)
176
+
177
+ source = "42" # Missing period
178
+ violations = linter.lint(source)
179
+
180
+ # Should have no violations since MD101 is disabled
181
+ assert len(violations) == 0
@@ -0,0 +1,81 @@
1
+ """Tests for the main linter functionality."""
2
+
3
+ from pathlib import Path
4
+
5
+ from machine_dialect.linter import Linter, Violation, ViolationSeverity
6
+
7
+
8
+ class TestLinter:
9
+ """Test the main Linter class."""
10
+
11
+ def test_linter_initialization(self) -> None:
12
+ """Test that linter initializes with default rules."""
13
+ linter = Linter()
14
+ assert len(linter.rules) > 0
15
+ assert any(rule.rule_id == "MD101" for rule in linter.rules)
16
+
17
+ def test_linter_with_config(self) -> None:
18
+ """Test linter initialization with configuration."""
19
+ config = {"rules": {"MD101": False}}
20
+ linter = Linter(config)
21
+
22
+ # MD101 should not be in the rules list
23
+ assert not any(rule.rule_id == "MD101" for rule in linter.rules)
24
+
25
+ def test_lint_parse_errors(self) -> None:
26
+ """Test that parse errors are reported as violations."""
27
+ source = "* 42" # Invalid - no prefix parse function for *
28
+
29
+ linter = Linter()
30
+ violations = linter.lint(source)
31
+
32
+ assert len(violations) > 0
33
+ assert any(v.rule_id == "parse-error" for v in violations)
34
+ assert any(v.severity == ViolationSeverity.ERROR for v in violations)
35
+
36
+ def test_lint_stops_on_parse_errors(self) -> None:
37
+ """Test that linter doesn't run rules when there are parse errors."""
38
+ source = "* 42" # Parse error
39
+
40
+ # Even with missing period, we should only get parse errors
41
+ linter = Linter()
42
+ violations = linter.lint(source)
43
+
44
+ # All violations should be parse errors
45
+ assert all(v.rule_id == "parse-error" for v in violations)
46
+
47
+ def test_lint_file(self, tmp_path: Path) -> None:
48
+ """Test linting a file from disk."""
49
+ # Create a temporary file
50
+ test_file = tmp_path / "test.md"
51
+ test_file.write_text("42.")
52
+
53
+ linter = Linter()
54
+ violations = linter.lint_file(str(test_file))
55
+
56
+ # Should have no violations
57
+ assert len(violations) == 0
58
+
59
+ def test_add_rule(self) -> None:
60
+ """Test adding custom rules to the linter."""
61
+ from machine_dialect.ast import ASTNode
62
+ from machine_dialect.linter.rules.base import Context, Rule
63
+
64
+ class CustomRule(Rule):
65
+ @property
66
+ def rule_id(self) -> str:
67
+ return "CUSTOM001"
68
+
69
+ @property
70
+ def description(self) -> str:
71
+ return "Custom test rule"
72
+
73
+ def check(self, node: ASTNode, context: Context) -> list[Violation]:
74
+ return []
75
+
76
+ linter = Linter()
77
+ initial_count = len(linter.rules)
78
+
79
+ linter.add_rule(CustomRule())
80
+ assert len(linter.rules) == initial_count + 1
81
+ assert any(rule.rule_id == "CUSTOM001" for rule in linter.rules)
@@ -0,0 +1,110 @@
1
+ """Tests for individual linting rules."""
2
+
3
+ from machine_dialect.ast import ExpressionStatement, WholeNumberLiteral
4
+ from machine_dialect.lexer import Token, TokenType
5
+ from machine_dialect.linter.rules.base import Context
6
+ from machine_dialect.linter.rules.statement_termination import StatementTerminationRule
7
+ from machine_dialect.linter.violations import ViolationSeverity
8
+
9
+
10
+ class TestStatementTerminationRule:
11
+ """Test the statement termination rule."""
12
+
13
+ def test_rule_properties(self) -> None:
14
+ """Test rule ID and description."""
15
+ rule = StatementTerminationRule()
16
+ assert rule.rule_id == "MD101"
17
+ assert "period" in rule.description.lower()
18
+
19
+ def test_valid_statement_with_period(self) -> None:
20
+ """Test that statements ending with periods pass."""
21
+ rule = StatementTerminationRule()
22
+
23
+ # Create a mock expression statement
24
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=1)
25
+ node = ExpressionStatement(token=token, expression=WholeNumberLiteral(token=token, value=42))
26
+
27
+ # Create context with source that has a period
28
+ context = Context("test.md", "42.")
29
+
30
+ violations = rule.check(node, context)
31
+ assert len(violations) == 0
32
+
33
+ def test_missing_period(self) -> None:
34
+ """Test that statements without periods at EOF are valid."""
35
+ rule = StatementTerminationRule()
36
+
37
+ # Create a mock expression statement
38
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=1)
39
+ node = ExpressionStatement(token=token, expression=WholeNumberLiteral(token=token, value=42))
40
+
41
+ # Create context with source that lacks a period (at EOF - valid)
42
+ context = Context("test.md", "42")
43
+
44
+ violations = rule.check(node, context)
45
+ assert len(violations) == 0 # No period needed at EOF
46
+
47
+ # Test with statement not at EOF (should have violation)
48
+ context = Context("test.md", "42\n100.")
49
+ violations = rule.check(node, context)
50
+ assert len(violations) == 1
51
+ assert violations[0].rule_id == "MD101"
52
+ assert violations[0].severity == ViolationSeverity.STYLE
53
+ assert violations[0].line == 1
54
+
55
+ def test_multiple_statements_on_line(self) -> None:
56
+ """Test handling of multiple statements on one line."""
57
+ rule = StatementTerminationRule()
58
+
59
+ # First statement in "42. 100"
60
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=1)
61
+ node = ExpressionStatement(token=token, expression=WholeNumberLiteral(token=token, value=42))
62
+
63
+ context = Context("test.md", "42. 100")
64
+
65
+ # First statement has a period, so no violation
66
+ violations = rule.check(node, context)
67
+ assert len(violations) == 0
68
+
69
+ def test_statement_with_whitespace(self) -> None:
70
+ """Test statements with trailing whitespace."""
71
+ rule = StatementTerminationRule()
72
+
73
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=1)
74
+ node = ExpressionStatement(token=token, expression=WholeNumberLiteral(token=token, value=42))
75
+
76
+ # Test with trailing whitespace but no period (at EOF - valid)
77
+ context = Context("test.md", "42 ")
78
+
79
+ violations = rule.check(node, context)
80
+ assert len(violations) == 0 # No period needed at EOF
81
+
82
+ def test_non_statement_nodes(self) -> None:
83
+ """Test that non-statement nodes are ignored."""
84
+ rule = StatementTerminationRule()
85
+
86
+ # Test with a literal node (not a statement)
87
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", line=1, position=1)
88
+ node = WholeNumberLiteral(token=token, value=42)
89
+
90
+ context = Context("test.md", "42")
91
+
92
+ # Should not check non-statement nodes
93
+ violations = rule.check(node, context)
94
+ assert len(violations) == 0
95
+
96
+ def test_rule_enabled_by_default(self) -> None:
97
+ """Test that rules are enabled by default."""
98
+ rule = StatementTerminationRule()
99
+ assert rule.is_enabled({})
100
+ assert rule.is_enabled({"rules": {}})
101
+
102
+ def test_rule_can_be_disabled(self) -> None:
103
+ """Test that rules can be disabled in config."""
104
+ rule = StatementTerminationRule()
105
+
106
+ config = {"rules": {"MD101": False}}
107
+ assert not rule.is_enabled(config)
108
+
109
+ config = {"rules": {"MD101": True}}
110
+ assert rule.is_enabled(config)
@@ -0,0 +1,71 @@
1
+ """Tests for the violations module."""
2
+
3
+ from machine_dialect.linter.violations import Violation, ViolationSeverity
4
+
5
+
6
+ class TestViolation:
7
+ """Test the Violation class."""
8
+
9
+ def test_violation_creation(self) -> None:
10
+ """Test creating a violation."""
11
+ violation = Violation(
12
+ rule_id="TEST001",
13
+ message="Test violation",
14
+ severity=ViolationSeverity.WARNING,
15
+ line=10,
16
+ column=5,
17
+ )
18
+
19
+ assert violation.rule_id == "TEST001"
20
+ assert violation.message == "Test violation"
21
+ assert violation.severity == ViolationSeverity.WARNING
22
+ assert violation.line == 10
23
+ assert violation.column == 5
24
+ assert violation.node is None
25
+ assert violation.fix_suggestion is None
26
+
27
+ def test_violation_with_optional_fields(self) -> None:
28
+ """Test violation with optional fields."""
29
+ violation = Violation(
30
+ rule_id="TEST002",
31
+ message="Test with suggestion",
32
+ severity=ViolationSeverity.STYLE,
33
+ line=1,
34
+ column=0,
35
+ fix_suggestion="Add a period",
36
+ )
37
+
38
+ assert violation.fix_suggestion == "Add a period"
39
+
40
+ def test_violation_string_representation(self) -> None:
41
+ """Test string representation of violations."""
42
+ violation = Violation(
43
+ rule_id="MD101",
44
+ message="Missing period",
45
+ severity=ViolationSeverity.STYLE,
46
+ line=5,
47
+ column=10,
48
+ )
49
+
50
+ str_repr = str(violation)
51
+ assert "5:10" in str_repr
52
+ assert "style" in str_repr
53
+ assert "MD101" in str_repr
54
+ assert "Missing period" in str_repr
55
+
56
+
57
+ class TestViolationSeverity:
58
+ """Test the ViolationSeverity enum."""
59
+
60
+ def test_severity_values(self) -> None:
61
+ """Test that severity levels have expected values."""
62
+ assert ViolationSeverity.ERROR.value == "error"
63
+ assert ViolationSeverity.WARNING.value == "warning"
64
+ assert ViolationSeverity.INFO.value == "info"
65
+ assert ViolationSeverity.STYLE.value == "style"
66
+
67
+ def test_all_severities_defined(self) -> None:
68
+ """Test that all expected severities are defined."""
69
+ expected_severities = {"ERROR", "WARNING", "INFO", "STYLE"}
70
+ actual_severities = {s.name for s in ViolationSeverity}
71
+ assert expected_severities == actual_severities
@@ -0,0 +1,51 @@
1
+ """Violation representation for the Machine Dialect™ linter.
2
+
3
+ This module defines the Violation class used to represent linting issues
4
+ found in Machine Dialect™ code.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from enum import Enum
9
+
10
+ from machine_dialect.ast import ASTNode
11
+
12
+
13
+ class ViolationSeverity(Enum):
14
+ """Severity levels for linting violations."""
15
+
16
+ ERROR = "error"
17
+ WARNING = "warning"
18
+ INFO = "info"
19
+ STYLE = "style"
20
+
21
+
22
+ @dataclass
23
+ class Violation:
24
+ """Represents a linting violation found in Machine Dialect™ code.
25
+
26
+ Attributes:
27
+ rule_id: Unique identifier for the rule that was violated.
28
+ message: Human-readable description of the violation.
29
+ severity: The severity level of the violation.
30
+ line: Line number where the violation occurred.
31
+ column: Column number where the violation occurred.
32
+ node: The AST node associated with the violation (optional).
33
+ fix_suggestion: Suggested fix for the violation (optional).
34
+ """
35
+
36
+ rule_id: str
37
+ message: str
38
+ severity: ViolationSeverity
39
+ line: int
40
+ column: int
41
+ node: ASTNode | None = None
42
+ fix_suggestion: str | None = None
43
+
44
+ def __str__(self) -> str:
45
+ """Return a formatted string representation of the violation.
46
+
47
+ Returns:
48
+ A string in the format: "line:column severity rule_id: message"
49
+ """
50
+ location = f"{self.line}:{self.column}"
51
+ return f"{location} {self.severity.value} {self.rule_id}: {self.message}"
@@ -0,0 +1,69 @@
1
+ """Machine Dialect™ MIR (Medium-level Intermediate Representation).
2
+
3
+ This module provides a Three-Address Code (TAC) based intermediate representation
4
+ with Static Single Assignment (SSA) support for Machine Dialect™ compilation.
5
+
6
+ The MIR sits between the HIR (desugared AST) and the final code generation
7
+ targets (bytecode and LLVM IR).
8
+ """
9
+
10
+ from .basic_block import CFG, BasicBlock
11
+ from .hir_to_mir import HIRToMIRLowering, lower_to_mir
12
+ from .mir_function import MIRFunction
13
+ from .mir_instructions import (
14
+ BinaryOp,
15
+ Call,
16
+ ConditionalJump,
17
+ Copy,
18
+ Jump,
19
+ Label,
20
+ LoadConst,
21
+ LoadVar,
22
+ MIRInstruction,
23
+ Phi,
24
+ Return,
25
+ StoreVar,
26
+ UnaryOp,
27
+ )
28
+ from .mir_module import MIRModule
29
+ from .mir_types import MIRType
30
+ from .mir_values import Constant, FunctionRef, MIRValue, Temp, Variable
31
+ from .optimization_config import OptimizationConfig
32
+ from .optimization_pipeline import OptimizationLevel, OptimizationPipeline, PipelineBuilder
33
+ from .optimize_mir import optimize_mir, optimize_mir_simple
34
+ from .pass_manager import PassManager
35
+
36
+ __all__ = [
37
+ "CFG",
38
+ "BasicBlock",
39
+ "BinaryOp",
40
+ "Call",
41
+ "ConditionalJump",
42
+ "Constant",
43
+ "Copy",
44
+ "FunctionRef",
45
+ "HIRToMIRLowering",
46
+ "Jump",
47
+ "Label",
48
+ "LoadConst",
49
+ "LoadVar",
50
+ "MIRFunction",
51
+ "MIRInstruction",
52
+ "MIRModule",
53
+ "MIRType",
54
+ "MIRValue",
55
+ "OptimizationConfig",
56
+ "OptimizationLevel",
57
+ "OptimizationPipeline",
58
+ "PassManager",
59
+ "Phi",
60
+ "PipelineBuilder",
61
+ "Return",
62
+ "StoreVar",
63
+ "Temp",
64
+ "UnaryOp",
65
+ "Variable",
66
+ "lower_to_mir",
67
+ "optimize_mir",
68
+ "optimize_mir_simple",
69
+ ]
@@ -0,0 +1,20 @@
1
+ """MIR analysis passes."""
2
+
3
+ from machine_dialect.mir.analyses.alias_analysis import AliasAnalysis, AliasInfo
4
+ from machine_dialect.mir.analyses.dominance_analysis import DominanceAnalysis
5
+ from machine_dialect.mir.analyses.escape_analysis import EscapeAnalysis, EscapeInfo
6
+ from machine_dialect.mir.analyses.loop_analysis import Loop, LoopAnalysis, LoopInfo
7
+ from machine_dialect.mir.analyses.use_def_chains import UseDefChains, UseDefChainsAnalysis
8
+
9
+ __all__ = [
10
+ "AliasAnalysis",
11
+ "AliasInfo",
12
+ "DominanceAnalysis",
13
+ "EscapeAnalysis",
14
+ "EscapeInfo",
15
+ "Loop",
16
+ "LoopAnalysis",
17
+ "LoopInfo",
18
+ "UseDefChains",
19
+ "UseDefChainsAnalysis",
20
+ ]