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,62 @@
1
+ from machine_dialect.ast import Identifier, Program, ReturnStatement, SetStatement, WholeNumberLiteral
2
+ from machine_dialect.lexer import Token, TokenType
3
+
4
+
5
+ class TestASTStringRepresentation:
6
+ def test_set_statement_string(self) -> None:
7
+ program: Program = Program(
8
+ statements=[
9
+ SetStatement(
10
+ token=Token(TokenType.KW_SET, "Set", 1, 0),
11
+ name=Identifier(token=Token(TokenType.MISC_IDENT, "my_var", 1, 4), value="my_var"),
12
+ value=Identifier(
13
+ token=Token(TokenType.MISC_IDENT, "other_var", 1, 15),
14
+ value="other_var",
15
+ ),
16
+ )
17
+ ]
18
+ )
19
+
20
+ program_str = str(program)
21
+
22
+ assert program_str == "Set `my_var` to `other_var`.\n"
23
+
24
+ def test_return_statement_string(self) -> None:
25
+ program: Program = Program(
26
+ statements=[
27
+ ReturnStatement(
28
+ token=Token(TokenType.KW_RETURN, "Give back", 1, 0),
29
+ return_value=Identifier(token=Token(TokenType.MISC_IDENT, "my_var", 1, 7), value="my_var"),
30
+ )
31
+ ]
32
+ )
33
+
34
+ program_str = str(program)
35
+
36
+ assert program_str == "\nGive back `my_var`.\n"
37
+
38
+ def test_multiple_statements_string(self) -> None:
39
+ program: Program = Program(
40
+ statements=[
41
+ SetStatement(
42
+ token=Token(TokenType.KW_SET, "Set", 1, 0),
43
+ name=Identifier(token=Token(TokenType.MISC_IDENT, "x", 1, 4), value="x"),
44
+ value=WholeNumberLiteral(token=Token(TokenType.LIT_WHOLE_NUMBER, "_42_", 1, 11), value=42),
45
+ ),
46
+ SetStatement(
47
+ token=Token(TokenType.KW_SET, "Set", 2, 0),
48
+ name=Identifier(token=Token(TokenType.MISC_IDENT, "result", 2, 4), value="result"),
49
+ value=Identifier(token=Token(TokenType.MISC_IDENT, "x", 2, 11), value="x"),
50
+ ),
51
+ ReturnStatement(
52
+ token=Token(TokenType.KW_RETURN, "Give back", 3, 0),
53
+ return_value=Identifier(token=Token(TokenType.MISC_IDENT, "result", 3, 7), value="result"),
54
+ ),
55
+ ]
56
+ )
57
+
58
+ program_str = str(program)
59
+
60
+ # The Program concatenates statements with ".\n" and adds final ".\n"
61
+ expected = "Set `x` to _42_.\nSet `result` to `x`.\n\nGive back `result`.\n"
62
+ assert program_str == expected
@@ -0,0 +1,29 @@
1
+ from machine_dialect.ast import YesNoLiteral
2
+ from machine_dialect.lexer import Token, TokenType
3
+
4
+
5
+ class TestBooleanLiteral:
6
+ def test_boolean_literal_true_display(self) -> None:
7
+ """Test that YesNoLiteral displays Yes with underscores."""
8
+ token = Token(TokenType.LIT_YES, "Yes", 1, 0)
9
+ literal = YesNoLiteral(token, True)
10
+
11
+ assert str(literal) == "_Yes_"
12
+
13
+ def test_boolean_literal_false_display(self) -> None:
14
+ """Test that YesNoLiteral displays No with underscores."""
15
+ token = Token(TokenType.LIT_NO, "No", 1, 0)
16
+ literal = YesNoLiteral(token, False)
17
+
18
+ assert str(literal) == "_No_"
19
+
20
+ def test_boolean_literal_value(self) -> None:
21
+ """Test that YesNoLiteral stores the correct boolean value."""
22
+ true_token = Token(TokenType.LIT_YES, "Yes", 1, 0)
23
+ true_literal = YesNoLiteral(true_token, True)
24
+
25
+ false_token = Token(TokenType.LIT_NO, "No", 1, 0)
26
+ false_literal = YesNoLiteral(false_token, False)
27
+
28
+ assert true_literal.value is True
29
+ assert false_literal.value is False
@@ -0,0 +1,138 @@
1
+ """Test HIR generation for collection operations."""
2
+
3
+ from machine_dialect.ast.expressions import CollectionAccessExpression, Identifier
4
+ from machine_dialect.ast.literals import UnorderedListLiteral, WholeNumberLiteral
5
+ from machine_dialect.ast.statements import CollectionMutationStatement
6
+ from machine_dialect.lexer import Token, TokenType
7
+
8
+
9
+ class TestCollectionHIR:
10
+ """Test HIR generation for collection operations."""
11
+
12
+ def test_list_literal_to_hir(self) -> None:
13
+ """Test that list literals convert to HIR properly."""
14
+ token = Token(TokenType.PUNCT_DASH, "-", 1, 1)
15
+ from machine_dialect.ast.expressions import Expression
16
+
17
+ elements: list[Expression] = [
18
+ WholeNumberLiteral(token, 1),
19
+ WholeNumberLiteral(token, 2),
20
+ WholeNumberLiteral(token, 3),
21
+ ]
22
+
23
+ unordered = UnorderedListLiteral(token, elements)
24
+ hir = unordered.to_hir()
25
+
26
+ assert isinstance(hir, UnorderedListLiteral)
27
+ assert len(hir.elements) == 3
28
+ assert all(isinstance(elem, WholeNumberLiteral) for elem in hir.elements)
29
+
30
+ def test_collection_access_ordinal_to_hir(self) -> None:
31
+ """Test that ordinal access converts to zero-based index."""
32
+ token = Token(TokenType.KW_FIRST, "first", 1, 1)
33
+ collection = Identifier(token, "items")
34
+
35
+ # Test "first" -> 0
36
+ access = CollectionAccessExpression(token, collection, "first", "ordinal")
37
+ hir = access.to_hir()
38
+
39
+ assert hir.accessor == 0
40
+ assert hir.access_type == "numeric" # Changed from ordinal to numeric
41
+
42
+ # Test "second" -> 1
43
+ access = CollectionAccessExpression(token, collection, "second", "ordinal")
44
+ hir = access.to_hir()
45
+
46
+ assert hir.accessor == 1
47
+ assert hir.access_type == "numeric"
48
+
49
+ # Test "third" -> 2
50
+ access = CollectionAccessExpression(token, collection, "third", "ordinal")
51
+ hir = access.to_hir()
52
+
53
+ assert hir.accessor == 2
54
+ assert hir.access_type == "numeric"
55
+
56
+ def test_collection_access_numeric_to_hir(self) -> None:
57
+ """Test that numeric access converts from 1-based to 0-based."""
58
+ token = Token(TokenType.KW_ITEM, "item", 1, 1)
59
+ collection = Identifier(token, "items")
60
+
61
+ # Test numeric index: 1 -> 0
62
+ access = CollectionAccessExpression(token, collection, 1, "numeric")
63
+ hir = access.to_hir()
64
+
65
+ assert hir.accessor == 0 # 1-based to 0-based
66
+ assert hir.access_type == "numeric"
67
+
68
+ # Test larger index: 877 -> 876
69
+ access = CollectionAccessExpression(token, collection, 877, "numeric")
70
+ hir = access.to_hir()
71
+
72
+ assert hir.accessor == 876
73
+ assert hir.access_type == "numeric"
74
+
75
+ def test_collection_access_last_to_hir(self) -> None:
76
+ """Test that 'last' remains as special case."""
77
+ token = Token(TokenType.KW_LAST, "last", 1, 1)
78
+ collection = Identifier(token, "items")
79
+
80
+ access = CollectionAccessExpression(token, collection, "last", "ordinal")
81
+ hir = access.to_hir()
82
+
83
+ # "last" should remain as "last" for special handling in MIR
84
+ assert hir.accessor == "last"
85
+ assert hir.access_type == "ordinal" # Keeps ordinal type for "last"
86
+
87
+ def test_collection_mutation_ordinal_to_hir(self) -> None:
88
+ """Test that mutation statements convert ordinals properly."""
89
+ token = Token(TokenType.KW_SET, "Set", 1, 1)
90
+ collection = Identifier(token, "items")
91
+ value = WholeNumberLiteral(token, 10)
92
+
93
+ # Test "Set the first item" -> index 0
94
+ mutation = CollectionMutationStatement(token, "set", collection, value, "first", "ordinal")
95
+ hir = mutation.to_hir()
96
+
97
+ assert hir.position == 0
98
+ assert hir.position_type == "numeric" # Changed from ordinal
99
+
100
+ # Test "Set the second item" -> index 1
101
+ mutation = CollectionMutationStatement(token, "set", collection, value, "second", "ordinal")
102
+ hir = mutation.to_hir()
103
+
104
+ assert hir.position == 1
105
+ assert hir.position_type == "numeric"
106
+
107
+ def test_collection_mutation_numeric_to_hir(self) -> None:
108
+ """Test that mutation statements convert numeric indices."""
109
+ token = Token(TokenType.KW_SET, "Set", 1, 1)
110
+ collection = Identifier(token, "items")
111
+ value = WholeNumberLiteral(token, 20)
112
+
113
+ # Test "Set item 1" -> index 0
114
+ mutation = CollectionMutationStatement(token, "set", collection, value, 1, "numeric")
115
+ hir = mutation.to_hir()
116
+
117
+ assert hir.position == 0 # 1-based to 0-based
118
+ assert hir.position_type == "numeric"
119
+
120
+ # Test "Set item 42" -> index 41
121
+ mutation = CollectionMutationStatement(token, "set", collection, value, 42, "numeric")
122
+ hir = mutation.to_hir()
123
+
124
+ assert hir.position == 41
125
+ assert hir.position_type == "numeric"
126
+
127
+ def test_collection_mutation_insert_to_hir(self) -> None:
128
+ """Test that insert operations convert positions properly."""
129
+ token = Token(TokenType.KW_INSERT, "Insert", 1, 1)
130
+ collection = Identifier(token, "items")
131
+ value = WholeNumberLiteral(token, 15)
132
+
133
+ # Test "Insert at position 2" -> index 1
134
+ mutation = CollectionMutationStatement(token, "insert", collection, value, 2, "numeric")
135
+ hir = mutation.to_hir()
136
+
137
+ assert hir.position == 1 # 1-based to 0-based
138
+ assert hir.position_type == "numeric"
@@ -0,0 +1,142 @@
1
+ from machine_dialect.ast import (
2
+ BlockStatement,
3
+ DefineStatement,
4
+ Identifier,
5
+ SetStatement,
6
+ StringLiteral,
7
+ WholeNumberLiteral,
8
+ )
9
+ from machine_dialect.lexer import Token, TokenType
10
+
11
+
12
+ class TestDefineStatement:
13
+ """Test DefineStatement AST node."""
14
+
15
+ def test_simple_definition(self) -> None:
16
+ """Test basic variable definition without default."""
17
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
18
+ name = Identifier(Token(TokenType.MISC_IDENT, "count", 1, 8), "count")
19
+ type_spec = ["Whole Number"]
20
+
21
+ stmt = DefineStatement(token, name, type_spec)
22
+
23
+ assert str(stmt) == "Define `count` as Whole Number."
24
+ assert stmt.initial_value is None
25
+ assert stmt.type_spec == ["Whole Number"]
26
+
27
+ def test_definition_with_default(self) -> None:
28
+ """Test variable definition with default value."""
29
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
30
+ name = Identifier(Token(TokenType.MISC_IDENT, "message", 1, 8), "message")
31
+ type_spec = ["Text"]
32
+ default = StringLiteral(Token(TokenType.LIT_TEXT, '"Hello"', 1, 30), "Hello")
33
+
34
+ stmt = DefineStatement(token, name, type_spec, default)
35
+
36
+ assert str(stmt) == 'Define `message` as Text (default: _"Hello"_).'
37
+ assert stmt.initial_value == default
38
+
39
+ def test_union_type_definition(self) -> None:
40
+ """Test definition with union types."""
41
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
42
+ name = Identifier(Token(TokenType.MISC_IDENT, "value", 1, 8), "value")
43
+ type_spec = ["Whole Number", "Text", "Yes/No"]
44
+
45
+ stmt = DefineStatement(token, name, type_spec)
46
+
47
+ assert str(stmt) == "Define `value` as Whole Number or Text or Yes/No."
48
+
49
+ def test_desugar_without_default(self) -> None:
50
+ """Test desugaring when no default value."""
51
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
52
+ name = Identifier(Token(TokenType.MISC_IDENT, "x", 1, 8), "x")
53
+ type_spec = ["Whole Number"]
54
+
55
+ stmt = DefineStatement(token, name, type_spec)
56
+ desugared = stmt.desugar()
57
+
58
+ # Should return self when no default
59
+ assert desugared is stmt
60
+
61
+ def test_desugar_with_default(self) -> None:
62
+ """Test desugaring with default value."""
63
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
64
+ name = Identifier(Token(TokenType.MISC_IDENT, "count", 1, 8), "count")
65
+ type_spec = ["Whole Number"]
66
+ default = WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "0", 1, 30), 0)
67
+
68
+ stmt = DefineStatement(token, name, type_spec, default)
69
+ desugared = stmt.desugar()
70
+
71
+ # Should return a BlockStatement with Define and Set
72
+ assert isinstance(desugared, BlockStatement)
73
+ assert len(desugared.statements) == 2
74
+
75
+ # First statement should be Define without default
76
+ define_stmt = desugared.statements[0]
77
+ assert isinstance(define_stmt, DefineStatement)
78
+ assert define_stmt.initial_value is None
79
+ assert define_stmt.name.value == "count"
80
+ assert define_stmt.type_spec == ["Whole Number"]
81
+
82
+ # Second statement should be Set
83
+ set_stmt = desugared.statements[1]
84
+ assert isinstance(set_stmt, SetStatement)
85
+ assert set_stmt.name is not None
86
+ assert set_stmt.name.value == "count"
87
+ assert set_stmt.value == default
88
+
89
+ def test_multiple_types_string_representation(self) -> None:
90
+ """Test string representation with multiple types."""
91
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
92
+ name = Identifier(Token(TokenType.MISC_IDENT, "data", 1, 8), "data")
93
+ type_spec = ["Number", "Text", "Yes/No", "Empty"]
94
+
95
+ stmt = DefineStatement(token, name, type_spec)
96
+
97
+ assert str(stmt) == "Define `data` as Number or Text or Yes/No or Empty."
98
+
99
+ def test_all_type_names(self) -> None:
100
+ """Test that all supported type names work correctly."""
101
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
102
+
103
+ type_names = [
104
+ "Text",
105
+ "Whole Number",
106
+ "Float",
107
+ "Number",
108
+ "Yes/No",
109
+ "URL",
110
+ "Date",
111
+ "DateTime",
112
+ "Time",
113
+ "List",
114
+ "Empty",
115
+ ]
116
+
117
+ for type_name in type_names:
118
+ name = Identifier(Token(TokenType.MISC_IDENT, "var", 1, 8), "var")
119
+ stmt = DefineStatement(token, name, [type_name])
120
+ assert str(stmt) == f"Define `var` as {type_name}."
121
+
122
+ def test_desugar_preserves_name_reference(self) -> None:
123
+ """Test that desugaring preserves the same name reference."""
124
+ token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
125
+ name = Identifier(Token(TokenType.MISC_IDENT, "test", 1, 8), "test")
126
+ type_spec = ["Text"]
127
+ default = StringLiteral(Token(TokenType.LIT_TEXT, '"value"', 1, 30), "value")
128
+
129
+ stmt = DefineStatement(token, name, type_spec, default)
130
+ desugared = stmt.desugar()
131
+
132
+ assert isinstance(desugared, BlockStatement)
133
+ define_stmt = desugared.statements[0]
134
+ set_stmt = desugared.statements[1]
135
+
136
+ # Both statements should reference the same variable name
137
+ assert isinstance(define_stmt, DefineStatement)
138
+ assert isinstance(set_stmt, SetStatement)
139
+ assert isinstance(define_stmt.name, Identifier)
140
+ assert isinstance(set_stmt.name, Identifier)
141
+ assert define_stmt.name.value == "test"
142
+ assert set_stmt.name.value == "test"