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,311 @@
1
+ from machine_dialect.ast import DefineStatement
2
+ from machine_dialect.parser import Parser
3
+
4
+
5
+ class TestDefineStatements:
6
+ """Test parsing of Define statements."""
7
+
8
+ def test_parse_simple_define(self) -> None:
9
+ """Test parsing basic Define statement."""
10
+ source = "Define `count` as Whole Number."
11
+ parser = Parser()
12
+
13
+ # Debug: Add print before parsing
14
+ from machine_dialect.lexer import Lexer
15
+
16
+ lexer = Lexer(source)
17
+ tokens = []
18
+ while True:
19
+ tok = lexer.next_token()
20
+ tokens.append(tok)
21
+ if tok.type.name == "MISC_EOF":
22
+ break
23
+ print(f"Tokens: {[(t.type.name, t.literal) for t in tokens]}")
24
+
25
+ program = parser.parse(source)
26
+
27
+ # Debug: Print any parser errors
28
+ if parser.errors:
29
+ for error in parser.errors:
30
+ print(f"Parser error: {error}")
31
+
32
+ assert len(program.statements) == 1
33
+ stmt = program.statements[0]
34
+ assert isinstance(stmt, DefineStatement)
35
+ assert stmt.name.value == "count"
36
+ assert stmt.type_spec == ["Whole Number"]
37
+ assert stmt.initial_value is None
38
+
39
+ def test_parse_define_with_default(self) -> None:
40
+ """Test parsing Define with default value."""
41
+ source = 'Define `message` as Text (default: _"Hello World"_).'
42
+
43
+ # Debug: Print tokens first
44
+ from machine_dialect.lexer import Lexer
45
+
46
+ lexer = Lexer(source)
47
+ tokens = []
48
+ while True:
49
+ tok = lexer.next_token()
50
+ tokens.append(tok)
51
+ if tok.type.name == "MISC_EOF":
52
+ break
53
+ print(f"Tokens: {[(t.type.name, t.literal) for t in tokens]}")
54
+
55
+ parser = Parser()
56
+ program = parser.parse(source)
57
+
58
+ # Debug: Print statements
59
+ print(f"Got {len(program.statements)} statements:")
60
+ for i, stmt in enumerate(program.statements):
61
+ print(f" {i}: {type(stmt).__name__}: {stmt}")
62
+
63
+ assert len(program.statements) == 1
64
+ stmt = program.statements[0]
65
+ assert isinstance(stmt, DefineStatement)
66
+ assert stmt.name.value == "message"
67
+ assert stmt.type_spec == ["Text"]
68
+ assert stmt.initial_value is not None
69
+ assert str(stmt.initial_value) == '_"Hello World"_'
70
+
71
+ def test_parse_define_with_integer_default(self) -> None:
72
+ """Test parsing Define with integer default."""
73
+ source = "Define `age` as Whole Number (default: _25_)."
74
+ parser = Parser()
75
+ program = parser.parse(source)
76
+
77
+ assert len(program.statements) == 1
78
+ stmt = program.statements[0]
79
+ assert isinstance(stmt, DefineStatement)
80
+ assert stmt.name.value == "age"
81
+ assert stmt.type_spec == ["Whole Number"]
82
+ assert str(stmt.initial_value) == "_25_"
83
+
84
+ def test_parse_define_with_boolean_default(self) -> None:
85
+ """Test parsing Define with boolean default."""
86
+ source = "Define `is_active` as Yes/No (default: _yes_)."
87
+ parser = Parser()
88
+ program = parser.parse(source)
89
+
90
+ assert len(program.statements) == 1
91
+ stmt = program.statements[0]
92
+ assert isinstance(stmt, DefineStatement)
93
+ assert stmt.name.value == "is_active"
94
+ assert stmt.type_spec == ["Yes/No"]
95
+ assert str(stmt.initial_value) == "_Yes_"
96
+
97
+ def test_parse_union_type(self) -> None:
98
+ """Test parsing Define with union types."""
99
+ source = "Define `value` as Whole Number or Text."
100
+ parser = Parser()
101
+ program = parser.parse(source)
102
+
103
+ assert len(program.statements) == 1
104
+ stmt = program.statements[0]
105
+ assert isinstance(stmt, DefineStatement)
106
+ assert stmt.name.value == "value"
107
+ assert stmt.type_spec == ["Whole Number", "Text"]
108
+
109
+ def test_parse_multiple_union_types(self) -> None:
110
+ """Test parsing Define with multiple union types."""
111
+ source = "Define `data` as Number or Yes/No or Text or Empty."
112
+
113
+ # Debug: Check tokens
114
+ from machine_dialect.lexer import Lexer
115
+
116
+ lexer = Lexer(source)
117
+ tokens = []
118
+ while True:
119
+ tok = lexer.next_token()
120
+ tokens.append(tok)
121
+ if tok.type.name == "MISC_EOF":
122
+ break
123
+ print(f"Tokens: {[(t.type.name, t.literal) for t in tokens]}")
124
+
125
+ parser = Parser()
126
+ program = parser.parse(source)
127
+
128
+ assert len(program.statements) == 1
129
+ stmt = program.statements[0]
130
+ assert isinstance(stmt, DefineStatement)
131
+ print(f"Got type_spec: {stmt.type_spec}")
132
+ assert stmt.type_spec == ["Number", "Yes/No", "Text", "Empty"]
133
+
134
+ def test_parse_various_type_names(self) -> None:
135
+ """Test parsing various type names."""
136
+ test_cases = [
137
+ ("Define `a` as Text.", ["Text"]),
138
+ ("Define `b` as Whole Number.", ["Whole Number"]),
139
+ ("Define `c` as Float.", ["Float"]),
140
+ ("Define `d` as Number.", ["Number"]),
141
+ ("Define `e` as Yes/No.", ["Yes/No"]),
142
+ ("Define `f` as URL.", ["URL"]),
143
+ ("Define `g` as Date.", ["Date"]),
144
+ ("Define `h` as DateTime.", ["DateTime"]),
145
+ ("Define `i` as Time.", ["Time"]),
146
+ ("Define `j` as List.", ["List"]),
147
+ ("Define `k` as Empty.", ["Empty"]),
148
+ ]
149
+
150
+ for source, expected_types in test_cases:
151
+ parser = Parser()
152
+ program = parser.parse(source)
153
+ assert len(program.statements) == 1
154
+ stmt = program.statements[0]
155
+ assert isinstance(stmt, DefineStatement)
156
+ assert stmt.type_spec == expected_types
157
+
158
+ def test_error_missing_variable_name(self) -> None:
159
+ """Test error when variable name is missing."""
160
+ source = "Define as Whole Number."
161
+ parser = Parser()
162
+ _ = parser.parse(source)
163
+
164
+ assert len(parser.errors) > 0
165
+ # The error message or structure may vary, just check we got errors
166
+
167
+ def test_error_missing_as_keyword(self) -> None:
168
+ """Test error when 'as' keyword is missing."""
169
+ source = "Define `count` Whole Number."
170
+ parser = Parser()
171
+ _ = parser.parse(source)
172
+
173
+ assert len(parser.errors) > 0
174
+
175
+ def test_error_missing_type(self) -> None:
176
+ """Test error when type is missing."""
177
+ source = "Define `count` as."
178
+ parser = Parser()
179
+ _ = parser.parse(source)
180
+
181
+ assert len(parser.errors) > 0
182
+
183
+ def test_error_invalid_default_syntax(self) -> None:
184
+ """Test error with invalid default syntax."""
185
+ source = "Define `x` as Whole Number (default _5_)." # Missing colon
186
+ parser = Parser()
187
+ _ = parser.parse(source)
188
+
189
+ assert len(parser.errors) > 0
190
+
191
+ def test_define_with_stopwords(self) -> None:
192
+ """Test that stopwords are properly skipped."""
193
+ source = "Define the `count` as a Whole Number."
194
+ parser = Parser()
195
+ program = parser.parse(source)
196
+
197
+ assert len(program.statements) == 1
198
+ stmt = program.statements[0]
199
+ assert isinstance(stmt, DefineStatement)
200
+ assert stmt.name.value == "count"
201
+ assert stmt.type_spec == ["Whole Number"]
202
+
203
+ def test_multiple_define_statements(self) -> None:
204
+ """Test parsing multiple Define statements."""
205
+ source = """
206
+ Define `name` as Text.
207
+ Define `age` as Whole Number.
208
+ Define `active` as Yes/No (default: _yes_).
209
+ """
210
+ parser = Parser()
211
+ program = parser.parse(source)
212
+
213
+ assert len(program.statements) == 3
214
+
215
+ # Check first statement
216
+ stmt1 = program.statements[0]
217
+ assert isinstance(stmt1, DefineStatement)
218
+ assert stmt1.name.value == "name"
219
+ assert stmt1.type_spec == ["Text"]
220
+ assert stmt1.initial_value is None
221
+
222
+ # Check second statement
223
+ stmt2 = program.statements[1]
224
+ assert isinstance(stmt2, DefineStatement)
225
+ assert stmt2.name.value == "age"
226
+ assert stmt2.type_spec == ["Whole Number"]
227
+ assert stmt2.initial_value is None
228
+
229
+ # Check third statement
230
+ stmt3 = program.statements[2]
231
+ assert isinstance(stmt3, DefineStatement)
232
+ assert stmt3.name.value == "active"
233
+ assert stmt3.type_spec == ["Yes/No"]
234
+ assert stmt3.initial_value is not None
235
+
236
+ def test_complex_nested_default_expressions(self) -> None:
237
+ """Test parsing Define with complex nested default expressions."""
238
+ # Test arithmetic expression as default
239
+ source1 = "Define `result` as Number (default: _70_)."
240
+ parser1 = Parser()
241
+ program1 = parser1.parse(source1)
242
+ assert len(program1.statements) == 1
243
+ stmt1 = program1.statements[0]
244
+ assert isinstance(stmt1, DefineStatement)
245
+ assert stmt1.name.value == "result"
246
+ assert stmt1.type_spec == ["Number"]
247
+ assert stmt1.initial_value is not None
248
+
249
+ # Test string literal as default
250
+ source2 = 'Define `value` as Text (default: _"Hello World"_).'
251
+ parser2 = Parser()
252
+ program2 = parser2.parse(source2)
253
+ assert len(program2.statements) == 1
254
+ stmt2 = program2.statements[0]
255
+ assert isinstance(stmt2, DefineStatement)
256
+ assert stmt2.initial_value is not None
257
+
258
+ # Test boolean expression as default
259
+ source3 = "Define `flag` as Yes/No (default: _yes_)."
260
+ parser3 = Parser()
261
+ program3 = parser3.parse(source3)
262
+ assert len(program3.statements) == 1
263
+ stmt3 = program3.statements[0]
264
+ assert isinstance(stmt3, DefineStatement)
265
+ assert stmt3.type_spec == ["Yes/No"]
266
+ assert stmt3.initial_value is not None
267
+
268
+ def test_error_recovery_paths(self) -> None:
269
+ """Test error recovery when parsing malformed Define statements."""
270
+ # Missing closing parenthesis in default
271
+ source1 = "Define `x` as Number (default: _5_. Define `y` as Text."
272
+ parser1 = Parser()
273
+ _ = parser1.parse(source1)
274
+ # Should have errors but still parse the second Define
275
+ assert len(parser1.errors) > 0
276
+ # May still have some statements parsed
277
+
278
+ # Malformed type specification
279
+ source2 = "Define `z` as or Text."
280
+ parser2 = Parser()
281
+ _ = parser2.parse(source2)
282
+ assert len(parser2.errors) > 0
283
+
284
+ # Multiple errors in one statement
285
+ source3 = "Define as (default: )."
286
+ parser3 = Parser()
287
+ _ = parser3.parse(source3)
288
+ assert len(parser3.errors) > 0
289
+
290
+ def test_edge_case_union_types(self) -> None:
291
+ """Test edge cases for union type specifications."""
292
+ # Very long union type list
293
+ source1 = "Define `data` as Text or Number or Yes/No or Date or Time or URL or List or Empty."
294
+ parser1 = Parser()
295
+ program1 = parser1.parse(source1)
296
+ assert len(program1.statements) == 1
297
+ stmt1 = program1.statements[0]
298
+ assert isinstance(stmt1, DefineStatement)
299
+ assert len(stmt1.type_spec) == 8
300
+ assert "Text" in stmt1.type_spec
301
+ assert "Empty" in stmt1.type_spec
302
+
303
+ # Union type with default value
304
+ source2 = "Define `flexible` as Text or Number (default: _42_)."
305
+ parser2 = Parser()
306
+ program2 = parser2.parse(source2)
307
+ assert len(program2.statements) == 1
308
+ stmt2 = program2.statements[0]
309
+ assert isinstance(stmt2, DefineStatement)
310
+ assert stmt2.type_spec == ["Text", "Number"]
311
+ assert stmt2.initial_value is not None
@@ -0,0 +1,115 @@
1
+ """Tests for dictionary keys/values extraction parsing."""
2
+
3
+ from machine_dialect.ast.dict_extraction import DictExtraction
4
+ from machine_dialect.ast.expressions import Identifier
5
+ from machine_dialect.ast.statements import SetStatement
6
+ from machine_dialect.lexer.tokens import Token, TokenType
7
+ from machine_dialect.parser.parser import Parser
8
+
9
+
10
+ class TestDictExtraction:
11
+ """Test parsing of dictionary extraction expressions."""
12
+
13
+ def test_parse_names_extraction(self) -> None:
14
+ """Test parsing 'the names of `dict`'."""
15
+ source = "Set `result` to the names of `person`."
16
+ parser = Parser()
17
+ program = parser.parse(source)
18
+ assert len(program.statements) == 1
19
+ stmt = program.statements[0]
20
+ assert isinstance(stmt, SetStatement) # Type assertion for MyPy
21
+ expr = stmt.value
22
+ assert isinstance(expr, DictExtraction)
23
+ assert expr.extract_type == "names"
24
+ assert isinstance(expr.dictionary, Identifier)
25
+ assert expr.dictionary.value == "person"
26
+ assert str(expr) == "the names of `person`"
27
+
28
+ def test_parse_contents_extraction(self) -> None:
29
+ """Test parsing 'the contents of `dict`'."""
30
+ source = "Set `result` to the contents of `config`."
31
+ parser = Parser()
32
+ program = parser.parse(source)
33
+
34
+ assert len(program.statements) == 1
35
+ stmt = program.statements[0]
36
+ assert isinstance(stmt, SetStatement) # Type assertion for MyPy
37
+ expr = stmt.value
38
+
39
+ assert isinstance(expr, DictExtraction)
40
+ assert expr.extract_type == "contents"
41
+ assert isinstance(expr.dictionary, Identifier)
42
+ assert expr.dictionary.value == "config"
43
+ assert str(expr) == "the contents of `config`"
44
+
45
+ def test_parse_names_in_set_statement(self) -> None:
46
+ """Test parsing 'Set `names` to the names of `person`.'"""
47
+ source = "Set `my_names` to the names of `person`."
48
+ parser = Parser()
49
+ program = parser.parse(source)
50
+
51
+ assert len(program.statements) == 1
52
+ stmt = program.statements[0]
53
+ assert stmt.__class__.__name__ == "SetStatement"
54
+ assert isinstance(stmt, SetStatement) # Type assertion for MyPy
55
+
56
+ # Check the value is a DictExtraction
57
+ assert isinstance(stmt.value, DictExtraction)
58
+ assert stmt.value.extract_type == "names"
59
+ dict_val = stmt.value.dictionary
60
+ assert isinstance(dict_val, Identifier)
61
+ assert dict_val.value == "person"
62
+
63
+ def test_parse_contents_in_set_statement(self) -> None:
64
+ """Test parsing 'Set `values` to the contents of `dict`.'"""
65
+ source = "Set `my_values` to the contents of `settings`."
66
+ parser = Parser()
67
+ program = parser.parse(source)
68
+
69
+ assert len(program.statements) == 1
70
+ stmt = program.statements[0]
71
+ assert stmt.__class__.__name__ == "SetStatement"
72
+ assert isinstance(stmt, SetStatement) # Type assertion for MyPy
73
+
74
+ # Check the value is a DictExtraction
75
+ assert isinstance(stmt.value, DictExtraction)
76
+ assert stmt.value.extract_type == "contents"
77
+ dict_val = stmt.value.dictionary
78
+ assert isinstance(dict_val, Identifier)
79
+ assert dict_val.value == "settings"
80
+
81
+ def test_parse_invalid_extraction_missing_dict(self) -> None:
82
+ """Test error when dictionary is missing."""
83
+ source = "Set `result` to the names of."
84
+ parser = Parser()
85
+ program = parser.parse(source)
86
+
87
+ # The parser should handle the missing dictionary gracefully
88
+ assert len(program.statements) == 1
89
+ stmt = program.statements[0]
90
+ # The value might be an error expression or partial parse
91
+ assert stmt is not None
92
+
93
+ def test_dict_extraction_to_hir(self) -> None:
94
+ """Test that DictExtraction converts to HIR properly."""
95
+ token = Token(TokenType.MISC_STOPWORD, "the", 1, 1)
96
+ dict_ident = Identifier(Token(TokenType.MISC_IDENT, "person", 1, 5), "person")
97
+
98
+ extraction = DictExtraction(token, dict_ident, "names")
99
+ hir = extraction.to_hir()
100
+
101
+ assert isinstance(hir, DictExtraction)
102
+ assert hir.extract_type == "names"
103
+ assert isinstance(hir.dictionary, Identifier)
104
+
105
+ def test_dict_extraction_desugar(self) -> None:
106
+ """Test that DictExtraction desugars properly."""
107
+ token = Token(TokenType.MISC_STOPWORD, "the", 1, 1)
108
+ dict_ident = Identifier(Token(TokenType.MISC_IDENT, "person", 1, 5), "person")
109
+
110
+ extraction = DictExtraction(token, dict_ident, "contents")
111
+ desugared = extraction.desugar()
112
+
113
+ assert isinstance(desugared, DictExtraction)
114
+ assert desugared.extract_type == "contents"
115
+ assert isinstance(desugared.dictionary, Identifier)
@@ -0,0 +1,155 @@
1
+ """Tests for parsing the empty literal."""
2
+
3
+ import pytest
4
+
5
+ from machine_dialect.ast import EmptyLiteral, ExpressionStatement
6
+ from machine_dialect.parser import Parser
7
+
8
+
9
+ class TestEmptyLiteral:
10
+ """Test parsing of the empty literal."""
11
+
12
+ def test_parse_empty_literal(self) -> None:
13
+ """Test parsing the empty keyword as a literal."""
14
+ source = "empty"
15
+ parser = Parser()
16
+ program = parser.parse(source, check_semantics=False)
17
+
18
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
19
+ assert len(program.statements) == 1
20
+
21
+ statement = program.statements[0]
22
+ assert isinstance(statement, ExpressionStatement)
23
+ assert isinstance(statement.expression, EmptyLiteral)
24
+ assert statement.expression.value is None
25
+ assert str(statement.expression) == "empty"
26
+
27
+ def test_empty_in_set_statement(self) -> None:
28
+ """Test using empty in a set statement."""
29
+ source = """Define `result` as Empty.
30
+ Set `result` to empty."""
31
+ parser = Parser()
32
+ program = parser.parse(source, check_semantics=False)
33
+
34
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
35
+ assert len(program.statements) == 2
36
+
37
+ from machine_dialect.ast import DefineStatement, SetStatement
38
+
39
+ # Check Define statement
40
+ assert isinstance(program.statements[0], DefineStatement)
41
+
42
+ # Check Set statement
43
+ statement = program.statements[1]
44
+ assert isinstance(statement, SetStatement)
45
+ assert isinstance(statement.value, EmptyLiteral)
46
+
47
+ def test_empty_in_comparison(self) -> None:
48
+ """Test using empty in comparison expressions."""
49
+ test_cases = [
50
+ ("x equals empty", "`x`", "equals", "empty"),
51
+ ("`value` is not empty", "`value`", "is not", "empty"),
52
+ ("result is strictly equal to empty", "`result`", "is strictly equal to", "empty"),
53
+ ]
54
+
55
+ for source, expected_left, expected_op, expected_right in test_cases:
56
+ parser = Parser()
57
+ program = parser.parse(source, check_semantics=False)
58
+
59
+ assert len(parser.errors) == 0, f"Parser errors for '{source}': {parser.errors}"
60
+ assert len(program.statements) == 1
61
+
62
+ statement = program.statements[0]
63
+ assert isinstance(statement, ExpressionStatement)
64
+
65
+ from machine_dialect.ast import InfixExpression
66
+
67
+ expr = statement.expression
68
+ assert isinstance(expr, InfixExpression)
69
+ assert str(expr.left) == expected_left
70
+ assert expr.operator == expected_op
71
+ assert str(expr.right) == expected_right
72
+
73
+ def test_empty_in_if_condition(self) -> None:
74
+ """Test using empty in if statement conditions."""
75
+ source = """
76
+ Define `value` as Whole Number or Empty.
77
+ Define `result` as Whole Number.
78
+ if `value` equals empty then:
79
+ > Set `result` to _0_.
80
+ """
81
+ parser = Parser()
82
+ program = parser.parse(source, check_semantics=False)
83
+
84
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
85
+ assert len(program.statements) == 3
86
+
87
+ from machine_dialect.ast import DefineStatement, IfStatement, InfixExpression
88
+
89
+ # Check Define statements
90
+ assert isinstance(program.statements[0], DefineStatement)
91
+ assert isinstance(program.statements[1], DefineStatement)
92
+
93
+ # Check If statement
94
+ if_stmt = program.statements[2]
95
+ assert isinstance(if_stmt, IfStatement)
96
+ assert isinstance(if_stmt.condition, InfixExpression)
97
+ assert str(if_stmt.condition.right) == "empty"
98
+
99
+ def test_empty_in_return_statement(self) -> None:
100
+ """Test using empty in a return statement."""
101
+ source = "give back empty."
102
+ parser = Parser()
103
+ program = parser.parse(source, check_semantics=False)
104
+
105
+ assert len(parser.errors) == 0, f"Parser errors: {parser.errors}"
106
+ assert len(program.statements) == 1
107
+
108
+ from machine_dialect.ast import ReturnStatement
109
+
110
+ statement = program.statements[0]
111
+ assert isinstance(statement, ReturnStatement)
112
+ assert isinstance(statement.return_value, EmptyLiteral)
113
+
114
+ def test_empty_not_confused_with_identifier(self) -> None:
115
+ """Test that 'empty' is recognized as a keyword, not an identifier."""
116
+ # Test that 'empty' is parsed as EmptyLiteral
117
+ parser1 = Parser()
118
+ program1 = parser1.parse("empty", check_semantics=False)
119
+ assert len(parser1.errors) == 0
120
+ stmt1 = program1.statements[0]
121
+ assert isinstance(stmt1, ExpressionStatement)
122
+ assert isinstance(stmt1.expression, EmptyLiteral)
123
+
124
+ # Test that similar words are parsed as identifiers
125
+ parser2 = Parser()
126
+ program2 = parser2.parse("empties", check_semantics=False)
127
+ assert len(parser2.errors) == 0
128
+
129
+ from machine_dialect.ast import Identifier
130
+
131
+ stmt2 = program2.statements[0]
132
+ assert isinstance(stmt2, ExpressionStatement)
133
+ assert isinstance(stmt2.expression, Identifier)
134
+
135
+ @pytest.mark.parametrize(
136
+ "source",
137
+ [
138
+ "Empty", # Different case
139
+ "EMPTY", # All caps
140
+ "eMpTy", # Mixed case
141
+ ],
142
+ )
143
+ def test_empty_case_insensitive(self, source: str) -> None:
144
+ """Test that empty keyword is case-insensitive."""
145
+ parser = Parser()
146
+ program = parser.parse(source, check_semantics=False)
147
+
148
+ assert len(parser.errors) == 0, f"Parser errors for '{source}': {parser.errors}"
149
+ assert len(program.statements) == 1
150
+
151
+ statement = program.statements[0]
152
+ assert isinstance(statement, ExpressionStatement)
153
+ assert isinstance(statement.expression, EmptyLiteral)
154
+ # String representation should always be lowercase
155
+ assert str(statement.expression) == "empty"