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,322 @@
1
+ """Integration tests for list and dictionary features."""
2
+
3
+ import pytest
4
+
5
+ from machine_dialect.parser.parser import Parser
6
+
7
+
8
+ class TestListFeatures:
9
+ """Test list (array) features end-to-end."""
10
+
11
+ def test_one_based_indexing_literal(self) -> None:
12
+ """Test that item _1_ accesses the first element (index 0)."""
13
+ source = """
14
+ Set `numbers` to:
15
+ - _10_.
16
+ - _20_.
17
+ - _30_.
18
+
19
+ Set `first` to item _1_ of `numbers`.
20
+ Set `second` to item _2_ of `numbers`.
21
+ Set `third` to item _3_ of `numbers`.
22
+ """
23
+ parser = Parser()
24
+ program = parser.parse(source, check_semantics=False)
25
+
26
+ # Should parse successfully
27
+ assert len(program.statements) == 4
28
+ # Don't check for errors since we're just testing syntax
29
+
30
+ def test_one_based_indexing_expression(self) -> None:
31
+ """Test that expression-based indices are handled correctly."""
32
+ source = """
33
+ Set `idx` to _2_.
34
+ Set `numbers` to:
35
+ - _100_.
36
+ - _200_.
37
+ - _300_.
38
+
39
+ Set `value` to item `idx` of `numbers`.
40
+ """
41
+ parser = Parser()
42
+ program = parser.parse(source, check_semantics=False)
43
+
44
+ # Should parse successfully
45
+ assert len(program.statements) == 3
46
+ # Don't check for errors since we're just testing syntax
47
+
48
+ def test_array_mutations_with_one_based_index(self) -> None:
49
+ """Test array mutations with one-based indexing."""
50
+ source = """
51
+ Set `list` to:
52
+ - _"a"_.
53
+ - _"b"_.
54
+ - _"c"_.
55
+
56
+ Set item _1_ of `list` to _"A"_.
57
+ Remove item _2_ from `list`.
58
+ Insert _"X"_ at position _1_ in `list`.
59
+ """
60
+ parser = Parser()
61
+ program = parser.parse(source, check_semantics=False)
62
+
63
+ assert len(program.statements) == 4
64
+ # Don't check for errors since we're just testing syntax
65
+
66
+ def test_clear_operation(self) -> None:
67
+ """Test the Clear operation on lists."""
68
+ source = """
69
+ Set `items` to:
70
+ - _1_.
71
+ - _2_.
72
+ - _3_.
73
+
74
+ Clear `items`.
75
+ """
76
+ parser = Parser()
77
+ program = parser.parse(source, check_semantics=False)
78
+
79
+ assert len(program.statements) == 2
80
+ # Don't check for errors since we're just testing syntax
81
+
82
+ def test_ordinal_access(self) -> None:
83
+ """Test ordinal access (first, second, third, last)."""
84
+ source = """
85
+ Set `colors` to:
86
+ - _"red"_.
87
+ - _"green"_.
88
+ - _"blue"_.
89
+ - _"yellow"_.
90
+
91
+ Set `f` to the first item of `colors`.
92
+ Set `s` to the second item of `colors`.
93
+ Set `t` to the third item of `colors`.
94
+ Set `l` to the last item of `colors`.
95
+ """
96
+ parser = Parser()
97
+ program = parser.parse(source, check_semantics=False)
98
+
99
+ assert len(program.statements) == 5
100
+ # Don't check for errors since we're just testing syntax
101
+
102
+
103
+ class TestDictionaryFeatures:
104
+ """Test dictionary (named list) features end-to-end."""
105
+
106
+ def test_named_list_creation(self) -> None:
107
+ """Test creating a named list (dictionary)."""
108
+ source = """
109
+ Set `person` to:
110
+ - _"name"_: _"Alice"_.
111
+ - _"age"_: _30_.
112
+ - _"email"_: _"alice@example.com"_.
113
+ - _"active"_: _yes_.
114
+ """
115
+ parser = Parser()
116
+ program = parser.parse(source, check_semantics=False)
117
+
118
+ assert len(program.statements) == 1
119
+ # Don't check for errors since we're just testing syntax
120
+
121
+ def test_dictionary_keys_extraction(self) -> None:
122
+ """Test extracting keys from a dictionary."""
123
+ source = """
124
+ Set `config` to:
125
+ - _"host"_: _"localhost"_.
126
+ - _"port"_: _8080_.
127
+ - _"debug"_: _yes_.
128
+
129
+ Set `keys` to the names of `config`.
130
+ """
131
+ parser = Parser()
132
+ program = parser.parse(source, check_semantics=False)
133
+
134
+ assert len(program.statements) == 2
135
+ # Don't check for errors since we're just testing syntax
136
+
137
+ def test_dictionary_values_extraction(self) -> None:
138
+ """Test extracting values from a dictionary."""
139
+ source = """
140
+ Set `user` to:
141
+ - _"username"_: _"johndoe"_.
142
+ - _"email"_: _"john@example.com"_.
143
+ - _"premium"_: _yes_.
144
+
145
+ Set `values` to the contents of `user`.
146
+ """
147
+ parser = Parser()
148
+ program = parser.parse(source, check_semantics=False)
149
+
150
+ assert len(program.statements) == 2
151
+ # Don't check for errors since we're just testing syntax
152
+
153
+ def test_possessive_property_access(self) -> None:
154
+ """Test possessive syntax for property access."""
155
+ source = """
156
+ Set `person` to:
157
+ - _"name"_: _"Bob"_.
158
+ - _"age"_: _25_.
159
+ - _"city"_: _"New York"_.
160
+
161
+ Set `user_name` to `person`'s _"name"_.
162
+ Set `user_age` to `person`'s _"age"_.
163
+ Set `user_city` to `person`'s _"city"_.
164
+ """
165
+ parser = Parser()
166
+ program = parser.parse(source, check_semantics=False)
167
+
168
+ assert len(program.statements) == 4
169
+ # Don't check for errors since we're just testing syntax
170
+
171
+ def test_possessive_property_mutation(self) -> None:
172
+ """Test modifying properties using possessive syntax."""
173
+ source = """
174
+ Set `config` to:
175
+ - _"host"_: _"localhost"_.
176
+ - _"port"_: _8080_.
177
+
178
+ Set `config`'s _"port"_ to _9000_.
179
+ """
180
+ parser = Parser()
181
+ program = parser.parse(source, check_semantics=False)
182
+
183
+ # This should parse, though the exact handling may vary
184
+ assert len(program.statements) == 2
185
+ # Don't check for errors since we're just testing syntax
186
+
187
+ def test_dictionary_operations(self) -> None:
188
+ """Test various dictionary operations."""
189
+ source = """
190
+ Set `data` to:
191
+ - _"key1"_: _"value1"_.
192
+ - _"key2"_: _"value2"_.
193
+
194
+ Add _"key3"_ to `data` with value _"value3"_.
195
+ Remove _"key1"_ from `data`.
196
+ """
197
+ parser = Parser()
198
+ program = parser.parse(source, check_semantics=False)
199
+
200
+ assert len(program.statements) == 3
201
+ # Don't check for errors since we're just testing syntax
202
+
203
+
204
+ class TestNestedCollections:
205
+ """Test combinations of lists and dictionaries."""
206
+
207
+ @pytest.mark.skip(
208
+ reason="Nested collection structures not yet supported by parser - requires complex grammar changes"
209
+ )
210
+ def test_list_of_dictionaries(self) -> None:
211
+ """Test creating a list containing dictionaries."""
212
+ source = """
213
+ Set `users` to:
214
+ - - _"name"_: _"Alice"_.
215
+ - _"age"_: _30_.
216
+ - - _"name"_: _"Bob"_.
217
+ - _"age"_: _25_.
218
+
219
+ Set `first_user` to the first item of `users`.
220
+ Set `first_name` to `first_user`'s _"name"_.
221
+ """
222
+ parser = Parser()
223
+ program = parser.parse(source, check_semantics=False)
224
+
225
+ # Should parse the nested structure
226
+ assert len(program.statements) == 3
227
+
228
+ @pytest.mark.skip(
229
+ reason="Nested collection structures not yet supported by parser - requires complex grammar changes"
230
+ )
231
+ def test_dictionary_with_list_values(self) -> None:
232
+ """Test dictionary with lists as values."""
233
+ source = """
234
+ Set `data` to:
235
+ - _"numbers"_:
236
+ - _1_.
237
+ - _2_.
238
+ - _3_.
239
+ - _"names"_:
240
+ - _"Alice"_.
241
+ - _"Bob"_.
242
+
243
+ Set `nums` to `data`'s _"numbers"_.
244
+ Set `first_num` to the first item of `nums`.
245
+ """
246
+ parser = Parser()
247
+ program = parser.parse(source, check_semantics=False)
248
+
249
+ # Should handle nested structures
250
+ assert len(program.statements) == 3
251
+
252
+ def test_extraction_and_access_combination(self) -> None:
253
+ """Test combining extraction with list access."""
254
+ source = """
255
+ Set `config` to:
256
+ - _"timeout"_: _30_.
257
+ - _"retries"_: _3_.
258
+ - _"debug"_: _no_.
259
+
260
+ Set `keys` to the names of `config`.
261
+ Set `values` to the contents of `config`.
262
+
263
+ Set `first_key` to the first item of `keys`.
264
+ Set `first_value` to the first item of `values`.
265
+ """
266
+ parser = Parser()
267
+ program = parser.parse(source, check_semantics=False)
268
+
269
+ assert len(program.statements) == 5
270
+ # Don't check for errors since we're just testing syntax
271
+
272
+
273
+ class TestIndexingEdgeCases:
274
+ """Test edge cases and error handling for collection access."""
275
+
276
+ def test_large_index_access(self) -> None:
277
+ """Test accessing elements with large indices."""
278
+ source = """
279
+ Set `data` to:
280
+ - _"a"_
281
+ - _"b"_
282
+ - _"c"_
283
+
284
+ Set `value` to item _877_ of `data`.
285
+ """
286
+ parser = Parser()
287
+ program = parser.parse(source, check_semantics=False)
288
+
289
+ # Should parse (runtime will handle out-of-bounds)
290
+ assert len(program.statements) == 2
291
+
292
+ def test_last_item_access(self) -> None:
293
+ """Test accessing the last item of collections."""
294
+ source = """
295
+ Set `nums` to:
296
+ - _1_.
297
+ - _2_.
298
+ - _3_.
299
+ - _4_.
300
+ - _5_.
301
+
302
+ Set `last_num` to the last item of `nums`.
303
+ """
304
+ parser = Parser()
305
+ program = parser.parse(source, check_semantics=False)
306
+
307
+ assert len(program.statements) == 2
308
+ # Don't check for errors since we're just testing syntax
309
+
310
+ def test_empty_collection_operations(self) -> None:
311
+ """Test operations on empty collections."""
312
+ source = """
313
+ Define `empty_list` as an unordered list.
314
+ Define `empty_dict` as a named list.
315
+ Set `val` to the first item of `empty_list`.
316
+ Set `keys` to the names of `empty_dict`.
317
+ """
318
+ parser = Parser()
319
+ program = parser.parse(source)
320
+
321
+ # Should parse even if runtime would fail
322
+ assert len(program.statements) == 4
@@ -0,0 +1,21 @@
1
+ """Type system for Machine Dialect™ compile-time type checking."""
2
+
3
+ from .type_system import (
4
+ TYPE_DISPLAY_NAMES,
5
+ MDType,
6
+ TypeSpec,
7
+ check_type_compatibility,
8
+ get_type_from_name,
9
+ get_type_from_value,
10
+ is_assignable_to,
11
+ )
12
+
13
+ __all__ = [
14
+ "TYPE_DISPLAY_NAMES",
15
+ "MDType",
16
+ "TypeSpec",
17
+ "check_type_compatibility",
18
+ "get_type_from_name",
19
+ "get_type_from_value",
20
+ "is_assignable_to",
21
+ ]
@@ -0,0 +1 @@
1
+ """Tests for the Machine Dialect™ type system."""
@@ -0,0 +1,230 @@
1
+ """Tests for the Machine Dialect™ compile-time type system."""
2
+
3
+ from machine_dialect.ast import (
4
+ EmptyLiteral,
5
+ FloatLiteral,
6
+ StringLiteral,
7
+ URLLiteral,
8
+ WholeNumberLiteral,
9
+ YesNoLiteral,
10
+ )
11
+ from machine_dialect.lexer import Token, TokenType
12
+ from machine_dialect.type_checking import (
13
+ MDType,
14
+ TypeSpec,
15
+ check_type_compatibility,
16
+ get_type_from_name,
17
+ get_type_from_value,
18
+ is_assignable_to,
19
+ )
20
+
21
+
22
+ class TestMDType:
23
+ """Test the MDType enumeration and type name mappings."""
24
+
25
+ def test_type_from_name(self) -> None:
26
+ """Test converting type names to MDType enum."""
27
+ assert get_type_from_name("Text") == MDType.TEXT
28
+ assert get_type_from_name("Whole Number") == MDType.WHOLE_NUMBER
29
+ assert get_type_from_name("Float") == MDType.FLOAT
30
+ assert get_type_from_name("Number") == MDType.NUMBER
31
+ assert get_type_from_name("Yes/No") == MDType.YES_NO
32
+ assert get_type_from_name("URL") == MDType.URL
33
+ assert get_type_from_name("Empty") == MDType.EMPTY
34
+ assert get_type_from_name("Any") == MDType.ANY
35
+
36
+ def test_type_from_invalid_name(self) -> None:
37
+ """Test that invalid type names return None."""
38
+ assert get_type_from_name("Invalid") is None
39
+ assert get_type_from_name("") is None
40
+ assert get_type_from_name("text") is None # Case sensitive
41
+
42
+
43
+ class TestTypeSpec:
44
+ """Test TypeSpec class for union type handling."""
45
+
46
+ def test_single_type_spec(self) -> None:
47
+ """Test TypeSpec with a single type."""
48
+ spec = TypeSpec(["Whole Number"])
49
+ assert spec.allows_type(MDType.WHOLE_NUMBER)
50
+ assert not spec.allows_type(MDType.TEXT)
51
+ assert str(spec) == "Whole Number"
52
+
53
+ def test_union_type_spec(self) -> None:
54
+ """Test TypeSpec with union types."""
55
+ spec = TypeSpec(["Whole Number", "Text", "Yes/No"])
56
+ assert spec.allows_type(MDType.WHOLE_NUMBER)
57
+ assert spec.allows_type(MDType.TEXT)
58
+ assert spec.allows_type(MDType.YES_NO)
59
+ assert not spec.allows_type(MDType.FLOAT)
60
+ assert str(spec) == "Whole Number or Text or Yes/No"
61
+
62
+ def test_number_type_allows_subtypes(self) -> None:
63
+ """Test that Number type allows Whole Number and Float."""
64
+ spec = TypeSpec(["Number"])
65
+ assert spec.allows_type(MDType.NUMBER)
66
+ assert spec.allows_type(MDType.WHOLE_NUMBER)
67
+ assert spec.allows_type(MDType.FLOAT)
68
+ assert not spec.allows_type(MDType.TEXT)
69
+
70
+ def test_any_type_allows_everything(self) -> None:
71
+ """Test that Any type allows all types."""
72
+ spec = TypeSpec(["Any"])
73
+ assert spec.allows_type(MDType.TEXT)
74
+ assert spec.allows_type(MDType.WHOLE_NUMBER)
75
+ assert spec.allows_type(MDType.FLOAT)
76
+ assert spec.allows_type(MDType.YES_NO)
77
+ assert spec.allows_type(MDType.EMPTY)
78
+
79
+
80
+ class TestGetTypeFromValue:
81
+ """Test determining types from AST literal values."""
82
+
83
+ def test_whole_number_literal(self) -> None:
84
+ """Test type detection for WholeNumberLiteral."""
85
+ token = Token(TokenType.LIT_WHOLE_NUMBER, "42", 1, 1)
86
+ literal = WholeNumberLiteral(token, 42)
87
+ assert get_type_from_value(literal) == MDType.WHOLE_NUMBER
88
+
89
+ def test_float_literal(self) -> None:
90
+ """Test type detection for FloatLiteral."""
91
+ token = Token(TokenType.LIT_FLOAT, "3.14", 1, 1)
92
+ literal = FloatLiteral(token, 3.14)
93
+ assert get_type_from_value(literal) == MDType.FLOAT
94
+
95
+ def test_string_literal(self) -> None:
96
+ """Test type detection for StringLiteral."""
97
+ token = Token(TokenType.LIT_TEXT, "hello", 1, 1)
98
+ literal = StringLiteral(token, "hello")
99
+ assert get_type_from_value(literal) == MDType.TEXT
100
+
101
+ def test_url_literal(self) -> None:
102
+ """Test type detection for URLLiteral."""
103
+ token = Token(TokenType.LIT_URL, "https://example.com", 1, 1)
104
+ literal = URLLiteral(token, "https://example.com")
105
+ assert get_type_from_value(literal) == MDType.URL
106
+
107
+ def test_yes_no_literal(self) -> None:
108
+ """Test type detection for YesNoLiteral."""
109
+ token = Token(TokenType.LIT_YES, "yes", 1, 1)
110
+ literal = YesNoLiteral(token, True)
111
+ assert get_type_from_value(literal) == MDType.YES_NO
112
+
113
+ def test_empty_literal(self) -> None:
114
+ """Test type detection for EmptyLiteral."""
115
+ token = Token(TokenType.KW_EMPTY, "empty", 1, 1)
116
+ literal = EmptyLiteral(token)
117
+ assert get_type_from_value(literal) == MDType.EMPTY
118
+
119
+ def test_string_that_looks_like_url(self) -> None:
120
+ """Test that strings starting with http are detected as URLs."""
121
+ token = Token(TokenType.LIT_TEXT, "https://example.com", 1, 1)
122
+ literal = StringLiteral(token, "https://example.com")
123
+ assert get_type_from_value(literal) == MDType.URL
124
+
125
+ def test_python_types(self) -> None:
126
+ """Test type detection for raw Python values."""
127
+ assert get_type_from_value(42) == MDType.WHOLE_NUMBER
128
+ assert get_type_from_value(3.14) == MDType.FLOAT
129
+ assert get_type_from_value("hello") == MDType.TEXT
130
+ assert get_type_from_value(True) == MDType.YES_NO
131
+ assert get_type_from_value(None) == MDType.EMPTY
132
+
133
+
134
+ class TestTypeAssignability:
135
+ """Test type assignability rules."""
136
+
137
+ def test_exact_match_allowed(self) -> None:
138
+ """Test that exact type matches are allowed."""
139
+ assert is_assignable_to(MDType.WHOLE_NUMBER, MDType.WHOLE_NUMBER)
140
+ assert is_assignable_to(MDType.TEXT, MDType.TEXT)
141
+ assert is_assignable_to(MDType.FLOAT, MDType.FLOAT)
142
+
143
+ def test_any_accepts_all(self) -> None:
144
+ """Test that Any type accepts all values."""
145
+ assert is_assignable_to(MDType.TEXT, MDType.ANY)
146
+ assert is_assignable_to(MDType.WHOLE_NUMBER, MDType.ANY)
147
+ assert is_assignable_to(MDType.EMPTY, MDType.ANY)
148
+
149
+ def test_number_accepts_numeric(self) -> None:
150
+ """Test that Number accepts Whole Number and Float."""
151
+ assert is_assignable_to(MDType.WHOLE_NUMBER, MDType.NUMBER)
152
+ assert is_assignable_to(MDType.FLOAT, MDType.NUMBER)
153
+ assert not is_assignable_to(MDType.TEXT, MDType.NUMBER)
154
+
155
+ def test_empty_assignable_to_any_type(self) -> None:
156
+ """Test that Empty can be assigned to any type."""
157
+ assert is_assignable_to(MDType.EMPTY, MDType.TEXT)
158
+ assert is_assignable_to(MDType.EMPTY, MDType.WHOLE_NUMBER)
159
+ assert is_assignable_to(MDType.EMPTY, MDType.YES_NO)
160
+
161
+ def test_no_implicit_conversions(self) -> None:
162
+ """Test that no implicit conversions are allowed (per spec)."""
163
+ assert not is_assignable_to(MDType.WHOLE_NUMBER, MDType.TEXT)
164
+ assert not is_assignable_to(MDType.TEXT, MDType.WHOLE_NUMBER)
165
+ assert not is_assignable_to(MDType.WHOLE_NUMBER, MDType.YES_NO)
166
+ assert not is_assignable_to(MDType.FLOAT, MDType.WHOLE_NUMBER)
167
+
168
+
169
+ class TestTypeCompatibility:
170
+ """Test type compatibility checking with TypeSpec."""
171
+
172
+ def test_compatible_single_type(self) -> None:
173
+ """Test compatibility with single type spec."""
174
+ spec = TypeSpec(["Whole Number"])
175
+ is_compatible, error = check_type_compatibility(MDType.WHOLE_NUMBER, spec)
176
+ assert is_compatible
177
+ assert error is None
178
+
179
+ def test_incompatible_single_type(self) -> None:
180
+ """Test incompatibility with single type spec."""
181
+ spec = TypeSpec(["Whole Number"])
182
+ is_compatible, error = check_type_compatibility(MDType.TEXT, spec)
183
+ assert not is_compatible
184
+ assert error == "Cannot assign Text value to variable of type Whole Number"
185
+
186
+ def test_compatible_union_type(self) -> None:
187
+ """Test compatibility with union type spec."""
188
+ spec = TypeSpec(["Whole Number", "Text"])
189
+
190
+ # Both types should be compatible
191
+ is_compatible, error = check_type_compatibility(MDType.WHOLE_NUMBER, spec)
192
+ assert is_compatible
193
+ assert error is None
194
+
195
+ is_compatible, error = check_type_compatibility(MDType.TEXT, spec)
196
+ assert is_compatible
197
+ assert error is None
198
+
199
+ def test_incompatible_union_type(self) -> None:
200
+ """Test incompatibility with union type spec."""
201
+ spec = TypeSpec(["Whole Number", "Text"])
202
+ is_compatible, error = check_type_compatibility(MDType.YES_NO, spec)
203
+ assert not is_compatible
204
+ assert error == "Cannot assign Yes/No value to variable of type Whole Number or Text"
205
+
206
+ def test_number_type_compatibility(self) -> None:
207
+ """Test Number type accepts both integers and floats."""
208
+ spec = TypeSpec(["Number"])
209
+
210
+ # Whole Number should be compatible
211
+ is_compatible, error = check_type_compatibility(MDType.WHOLE_NUMBER, spec)
212
+ assert is_compatible
213
+ assert error is None
214
+
215
+ # Float should be compatible
216
+ is_compatible, error = check_type_compatibility(MDType.FLOAT, spec)
217
+ assert is_compatible
218
+ assert error is None
219
+
220
+ # Text should not be compatible
221
+ is_compatible, error = check_type_compatibility(MDType.TEXT, spec)
222
+ assert not is_compatible
223
+
224
+ def test_empty_compatible_with_all(self) -> None:
225
+ """Test that Empty is compatible with all types."""
226
+ for type_name in ["Text", "Whole Number", "Float", "Yes/No", "URL"]:
227
+ spec = TypeSpec([type_name])
228
+ is_compatible, error = check_type_compatibility(MDType.EMPTY, spec)
229
+ assert is_compatible
230
+ assert error is None