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,189 @@
1
+ """Error message generation for semantic analysis.
2
+
3
+ This module provides helpful error messages with context and suggestions
4
+ for semantic analysis errors.
5
+ """
6
+
7
+ from difflib import SequenceMatcher
8
+
9
+
10
+ class ErrorMessageGenerator:
11
+ """Generates helpful error messages with context and suggestions."""
12
+
13
+ @staticmethod
14
+ def undefined_variable(var_name: str, line: int, position: int, similar_vars: list[str] | None = None) -> str:
15
+ """Generate error message for undefined variable.
16
+
17
+ Args:
18
+ var_name: Variable name that's undefined
19
+ line: Line number
20
+ position: Column position
21
+ similar_vars: List of similar variable names
22
+
23
+ Returns:
24
+ Formatted error message
25
+ """
26
+ base_msg = f"Variable '{var_name}' is not defined"
27
+
28
+ # Add line/position info
29
+ location = f" at line {line}, column {position}"
30
+
31
+ # Add suggestions if similar variables exist
32
+ suggestion = ""
33
+ if similar_vars:
34
+ if len(similar_vars) == 1:
35
+ suggestion = f"\nDid you mean '{similar_vars[0]}'?"
36
+ else:
37
+ vars_list = "', '".join(similar_vars[:3])
38
+ suggestion = f"\nDid you mean one of: '{vars_list}'?"
39
+
40
+ # Add hint for defining variable
41
+ hint = f"\nHint: Add 'Define `{var_name}` as Type.' before using it"
42
+
43
+ return base_msg + location + suggestion + hint
44
+
45
+ @staticmethod
46
+ def type_mismatch(
47
+ var_name: str,
48
+ expected_types: list[str],
49
+ actual_type: str,
50
+ line: int,
51
+ position: int,
52
+ value_repr: str | None = None,
53
+ ) -> str:
54
+ """Generate error message for type mismatch.
55
+
56
+ Args:
57
+ var_name: Variable name
58
+ expected_types: List of expected type names
59
+ actual_type: Actual type found
60
+ line: Line number
61
+ position: Column position
62
+ value_repr: String representation of the value
63
+
64
+ Returns:
65
+ Formatted error message
66
+ """
67
+ location = f"Error at line {line}, column {position}:"
68
+
69
+ if len(expected_types) == 1:
70
+ expected_str = expected_types[0]
71
+ main_msg = f"Cannot set {expected_str} variable '{var_name}' to {actual_type} value"
72
+ else:
73
+ expected_str = " or ".join(expected_types)
74
+ main_msg = f"Cannot set '{var_name}' to {actual_type} value"
75
+
76
+ if value_repr:
77
+ main_msg += f" {value_repr}"
78
+
79
+ type_info = f"\nExpected: {expected_str}\nActual: {actual_type}"
80
+
81
+ # Add conversion hint if applicable
82
+ hint = ""
83
+ if actual_type == "Whole Number" and "Float" in expected_types:
84
+ try:
85
+ # Try to convert value_repr to int first to ensure proper formatting
86
+ if value_repr and value_repr.isdigit():
87
+ hint = f"\nHint: Use _{value_repr}.0_ to make it a Float"
88
+ else:
89
+ hint = f"\nHint: Use _{value_repr or 'value'}.0_ to make it a Float"
90
+ except Exception:
91
+ hint = "\nHint: Use _value.0_ to make it a Float"
92
+ elif actual_type == "Float" and "Whole Number" in expected_types:
93
+ hint = "\nHint: Float values cannot be assigned to Whole Number variables"
94
+
95
+ return f"{location}\n{main_msg}{type_info}{hint}"
96
+
97
+ @staticmethod
98
+ def redefinition(
99
+ var_name: str, new_line: int, new_position: int, original_line: int, original_position: int
100
+ ) -> str:
101
+ """Generate error message for variable redefinition.
102
+
103
+ Args:
104
+ var_name: Variable name
105
+ new_line: Line of redefinition attempt
106
+ new_position: Column of redefinition attempt
107
+ original_line: Line of original definition
108
+ original_position: Column of original definition
109
+
110
+ Returns:
111
+ Formatted error message
112
+ """
113
+ location = f"Error at line {new_line}, column {new_position}:"
114
+ main_msg = f"Variable '{var_name}' is already defined"
115
+ original = f"\nOriginal definition at line {original_line}, column {original_position}"
116
+ hint = "\nHint: Variables cannot be redefined. Use 'Set' to change the value"
117
+
118
+ return f"{location}\n{main_msg}{original}{hint}"
119
+
120
+ @staticmethod
121
+ def uninitialized_use(var_name: str, line: int, position: int, definition_line: int) -> str:
122
+ """Generate error message for using uninitialized variable.
123
+
124
+ Args:
125
+ var_name: Variable name
126
+ line: Line where used
127
+ position: Column where used
128
+ definition_line: Line where defined
129
+
130
+ Returns:
131
+ Formatted error message
132
+ """
133
+ location = f"Error at line {line}, column {position}:"
134
+ main_msg = f"Variable '{var_name}' is used before being initialized"
135
+ definition = f"\nVariable was defined at line {definition_line}"
136
+ hint = f"\nHint: Add 'Set `{var_name}` to value.' before using it"
137
+
138
+ return f"{location}\n{main_msg}{definition}{hint}"
139
+
140
+ @staticmethod
141
+ def invalid_type(type_name: str, line: int, position: int, valid_types: list[str]) -> str:
142
+ """Generate error message for invalid type name.
143
+
144
+ Args:
145
+ type_name: Invalid type name
146
+ line: Line number
147
+ position: Column position
148
+ valid_types: List of valid type names
149
+
150
+ Returns:
151
+ Formatted error message
152
+ """
153
+ location = f"Error at line {line}, column {position}:"
154
+ main_msg = f"Unknown type '{type_name}'"
155
+
156
+ # Find similar valid types
157
+ similar = ErrorMessageGenerator._find_similar(type_name, valid_types)
158
+ suggestion = ""
159
+ if similar:
160
+ if len(similar) == 1:
161
+ suggestion = f"\nDid you mean '{similar[0]}'?"
162
+ else:
163
+ suggestion = f"\nDid you mean one of: {', '.join(similar[:3])}"
164
+
165
+ valid_list = "\nValid types: " + ", ".join(sorted(valid_types))
166
+
167
+ return f"{location}\n{main_msg}{suggestion}{valid_list}"
168
+
169
+ @staticmethod
170
+ def _find_similar(name: str, candidates: list[str], threshold: float = 0.6) -> list[str]:
171
+ """Find similar names using edit distance.
172
+
173
+ Args:
174
+ name: Name to match
175
+ candidates: List of candidate names
176
+ threshold: Similarity threshold (0-1)
177
+
178
+ Returns:
179
+ List of similar names
180
+ """
181
+ similarities = []
182
+ for candidate in candidates:
183
+ ratio = SequenceMatcher(None, name.lower(), candidate.lower()).ratio()
184
+ if ratio >= threshold:
185
+ similarities.append((candidate, ratio))
186
+
187
+ # Sort by similarity and return names only
188
+ similarities.sort(key=lambda x: x[1], reverse=True)
189
+ return [name for name, _ in similarities[:5]]
@@ -0,0 +1 @@
1
+ """Tests for semantic analysis components."""
@@ -0,0 +1,364 @@
1
+ """Tests for semantic analyzer functionality.
2
+
3
+ This module tests the semantic analysis capabilities including type checking,
4
+ variable usage validation, and error detection.
5
+ """
6
+
7
+ from machine_dialect.errors import MDNameError, MDTypeError
8
+ from machine_dialect.parser import Parser
9
+ from machine_dialect.semantic.analyzer import SemanticAnalyzer
10
+
11
+
12
+ class TestSemanticAnalyzer:
13
+ """Test semantic analysis functionality."""
14
+
15
+ def test_undefined_variable_error(self) -> None:
16
+ """Test error for using undefined variable."""
17
+ source = """
18
+ Set `x` to _5_.
19
+ """
20
+ parser = Parser()
21
+ program = parser.parse(source, check_semantics=False)
22
+
23
+ analyzer = SemanticAnalyzer()
24
+ _, errors = analyzer.analyze(program)
25
+
26
+ assert len(errors) == 1
27
+ assert isinstance(errors[0], MDNameError)
28
+ assert "not defined" in str(errors[0])
29
+ assert "Define" in str(errors[0]) # Should suggest Define
30
+
31
+ def test_type_mismatch_error(self) -> None:
32
+ """Test error for type mismatch."""
33
+ source = """
34
+ Define `age` as Whole Number.
35
+ Set `age` to _"twenty"_.
36
+ """
37
+ parser = Parser()
38
+ program = parser.parse(source, check_semantics=False)
39
+
40
+ analyzer = SemanticAnalyzer()
41
+ _, errors = analyzer.analyze(program)
42
+
43
+ assert len(errors) == 1
44
+ assert isinstance(errors[0], MDTypeError)
45
+ assert "Whole Number" in str(errors[0])
46
+ assert "Text" in str(errors[0])
47
+
48
+ def test_redefinition_error(self) -> None:
49
+ """Test error for variable redefinition."""
50
+ source = """
51
+ Define `x` as Whole Number.
52
+ Define `x` as Text.
53
+ """
54
+ parser = Parser()
55
+ program = parser.parse(source, check_semantics=False)
56
+
57
+ analyzer = SemanticAnalyzer()
58
+ _, errors = analyzer.analyze(program)
59
+
60
+ assert len(errors) == 1
61
+ assert isinstance(errors[0], MDNameError)
62
+ assert "already defined" in str(errors[0])
63
+
64
+ def test_valid_union_type_assignment(self) -> None:
65
+ """Test valid assignment to union type."""
66
+ source = """
67
+ Define `value` as Whole Number or Text.
68
+ Set `value` to _42_.
69
+ Set `value` to _"hello"_.
70
+ """
71
+ parser = Parser()
72
+ program = parser.parse(source, check_semantics=False)
73
+
74
+ analyzer = SemanticAnalyzer()
75
+ _, errors = analyzer.analyze(program)
76
+
77
+ assert len(errors) == 0
78
+
79
+ def test_invalid_union_type_assignment(self) -> None:
80
+ """Test invalid assignment to union type."""
81
+ source = """
82
+ Define `value` as Whole Number or Text.
83
+ Set `value` to _yes_.
84
+ """
85
+ parser = Parser()
86
+ program = parser.parse(source, check_semantics=False)
87
+
88
+ analyzer = SemanticAnalyzer()
89
+ _, errors = analyzer.analyze(program)
90
+
91
+ assert len(errors) == 1
92
+ assert isinstance(errors[0], MDTypeError)
93
+ assert "Yes/No" in str(errors[0])
94
+ assert "Whole Number" in str(errors[0]) or "Text" in str(errors[0])
95
+
96
+ def test_default_value_type_check(self) -> None:
97
+ """Test type checking for default values."""
98
+ source = """
99
+ Define `count` as Whole Number (default: _"zero"_).
100
+ """
101
+ parser = Parser()
102
+ program = parser.parse(source, check_semantics=False)
103
+
104
+ analyzer = SemanticAnalyzer()
105
+ _, errors = analyzer.analyze(program)
106
+
107
+ assert len(errors) == 1
108
+ assert isinstance(errors[0], MDTypeError)
109
+ assert "Default value" in str(errors[0])
110
+
111
+ def test_uninitialized_variable_use(self) -> None:
112
+ """Test error for using uninitialized variable."""
113
+ source = """
114
+ Define `x` as Whole Number.
115
+ Define `y` as Whole Number.
116
+ Set `y` to `x`.
117
+ """
118
+ parser = Parser()
119
+ program = parser.parse(source, check_semantics=False)
120
+
121
+ analyzer = SemanticAnalyzer()
122
+ _, errors = analyzer.analyze(program)
123
+
124
+ assert len(errors) == 1
125
+ assert "before being initialized" in str(errors[0])
126
+
127
+ def test_number_type_accepts_int_and_float(self) -> None:
128
+ """Test that Number type accepts both Whole Number and Float."""
129
+ source = """
130
+ Define `value` as Number.
131
+ Set `value` to _42_.
132
+ Set `value` to _3.14_.
133
+ """
134
+ parser = Parser()
135
+ program = parser.parse(source, check_semantics=False)
136
+
137
+ analyzer = SemanticAnalyzer()
138
+ _, errors = analyzer.analyze(program)
139
+
140
+ assert len(errors) == 0
141
+
142
+ def test_valid_default_value(self) -> None:
143
+ """Test valid default value with matching type."""
144
+ source = """
145
+ Define `count` as Whole Number (default: _0_).
146
+ Define `name` as Text (default: _"John"_).
147
+ Define `active` as Yes/No (default: _yes_).
148
+ """
149
+ parser = Parser()
150
+ program = parser.parse(source, check_semantics=False)
151
+
152
+ analyzer = SemanticAnalyzer()
153
+ _, errors = analyzer.analyze(program)
154
+
155
+ assert len(errors) == 0
156
+
157
+ def test_empty_type_compatibility(self) -> None:
158
+ """Test that Empty type works with nullable types."""
159
+ source = """
160
+ Define `optional` as Text or Empty.
161
+ Set `optional` to _"text"_.
162
+ Set `optional` to empty.
163
+ """
164
+ parser = Parser()
165
+ program = parser.parse(source, check_semantics=False)
166
+
167
+ analyzer = SemanticAnalyzer()
168
+ _, errors = analyzer.analyze(program)
169
+
170
+ assert len(errors) == 0
171
+
172
+ def test_invalid_type_name(self) -> None:
173
+ """Test error for invalid type name."""
174
+ source = """
175
+ Define `x` as String.
176
+ """
177
+ parser = Parser()
178
+ parser.parse(source, check_semantics=False)
179
+
180
+ # Parser already catches invalid type names and creates an ErrorStatement
181
+ # So semantic analyzer won't see it
182
+ assert len(parser.errors) == 1
183
+ assert "String" in str(parser.errors[0])
184
+
185
+ def test_expression_type_inference(self) -> None:
186
+ """Test type inference for expressions."""
187
+ source = """
188
+ Define `sum` as Whole Number.
189
+ Define `a` as Whole Number.
190
+ Define `b` as Whole Number.
191
+ Set `a` to _5_.
192
+ Set `b` to _10_.
193
+ Set `sum` to `a` + `b`.
194
+ """
195
+ parser = Parser()
196
+ program = parser.parse(source, check_semantics=False)
197
+
198
+ analyzer = SemanticAnalyzer()
199
+ _, errors = analyzer.analyze(program)
200
+
201
+ # Should be valid - no errors
202
+ assert len(errors) == 0
203
+
204
+ def test_comparison_returns_boolean(self) -> None:
205
+ """Test that comparison operators return Yes/No type."""
206
+ source = """
207
+ Define `result` as Yes/No.
208
+ Define `x` as Whole Number.
209
+ Set `x` to _5_.
210
+ Set `result` to `x` > _3_.
211
+ """
212
+ parser = Parser()
213
+ program = parser.parse(source, check_semantics=False)
214
+
215
+ analyzer = SemanticAnalyzer()
216
+ _, errors = analyzer.analyze(program)
217
+
218
+ assert len(errors) == 0
219
+
220
+ def test_division_returns_float(self) -> None:
221
+ """Test that division always returns Float type."""
222
+ source = """
223
+ Define `result` as Float.
224
+ Define `x` as Whole Number.
225
+ Define `y` as Whole Number.
226
+ Set `x` to _10_.
227
+ Set `y` to _3_.
228
+ Set `result` to `x` / `y`.
229
+ """
230
+ parser = Parser()
231
+ program = parser.parse(source, check_semantics=False)
232
+
233
+ analyzer = SemanticAnalyzer()
234
+ _, errors = analyzer.analyze(program)
235
+
236
+ assert len(errors) == 0
237
+
238
+ def test_bitwise_operators_type_inference(self) -> None:
239
+ """Test that bitwise operators return Whole Number type."""
240
+ source = """
241
+ Define `a` as Whole Number.
242
+ Define `b` as Whole Number.
243
+ Define `result` as Whole Number.
244
+ Set `a` to _5_.
245
+ Set `b` to _3_.
246
+ """
247
+ # Note: We can't fully test bitwise operators without parser support
248
+ # but the type inference is ready when the parser supports them
249
+ parser = Parser()
250
+ program = parser.parse(source, check_semantics=False)
251
+
252
+ analyzer = SemanticAnalyzer()
253
+ _, errors = analyzer.analyze(program)
254
+
255
+ assert len(errors) == 0
256
+
257
+ def test_arguments_expression_type(self) -> None:
258
+ """Test that Arguments expression returns None for type."""
259
+ from machine_dialect.ast.expressions import Arguments
260
+ from machine_dialect.lexer import Token, TokenType
261
+
262
+ analyzer = SemanticAnalyzer()
263
+
264
+ # Create an Arguments expression
265
+ args = Arguments(Token(TokenType.DELIM_LPAREN, "(", 1, 1))
266
+
267
+ # Type inference should return None for Arguments
268
+ type_info = analyzer._infer_expression_type(args)
269
+ assert type_info is None
270
+
271
+ def test_function_return_type_inference(self) -> None:
272
+ """Test that function calls can infer return type from symbol table."""
273
+ # This would require parsing function definitions and calls
274
+ # For now, we just test that the infrastructure is in place
275
+ from machine_dialect.ast import Identifier
276
+ from machine_dialect.ast.call_expression import CallExpression
277
+ from machine_dialect.lexer import Token, TokenType
278
+
279
+ analyzer = SemanticAnalyzer()
280
+
281
+ # Manually add a function to the symbol table with return type
282
+ analyzer.symbol_table.define("my_func", ["Function"], 1, 1)
283
+ func_info = analyzer.symbol_table.lookup("my_func")
284
+ if func_info:
285
+ func_info.return_type = "Text" # Set return type
286
+
287
+ # Create a call expression
288
+ func_name = Identifier(Token(TokenType.MISC_IDENT, "my_func", 1, 1), "my_func")
289
+ call_expr = CallExpression(Token(TokenType.KW_USE, "use", 1, 1), func_name, None)
290
+
291
+ # Type inference should return Text
292
+ type_info = analyzer._infer_expression_type(call_expr)
293
+ assert type_info is not None
294
+ assert type_info.type_name == "Text"
295
+
296
+ def test_function_definition_with_return_type(self) -> None:
297
+ """Test that function definitions store return type information."""
298
+ from machine_dialect.ast import Identifier
299
+ from machine_dialect.ast.program import Program
300
+ from machine_dialect.ast.statements import (
301
+ ActionStatement,
302
+ BlockStatement,
303
+ )
304
+ from machine_dialect.lexer import Token, TokenType
305
+
306
+ # Create an AST for: Action "add_one" with input `x` as Whole Number, output `result` as Whole Number
307
+ name = Identifier(Token(TokenType.MISC_IDENT, "add_one", 1, 8), "add_one")
308
+ action = ActionStatement(Token(TokenType.KW_ACTION, "action", 1, 1), name)
309
+
310
+ # Create input parameter
311
+ from machine_dialect.ast.statements import Output, Parameter
312
+
313
+ param_name = Identifier(Token(TokenType.MISC_IDENT, "x", 1, 20), "x")
314
+ input_param = Parameter(Token(TokenType.MISC_IDENT, "x", 1, 20), param_name, type_name="Whole Number")
315
+ action.inputs = [input_param]
316
+
317
+ # Create output parameter
318
+ output_name = Identifier(Token(TokenType.MISC_IDENT, "result", 1, 40), "result")
319
+ output_param = Output(Token(TokenType.MISC_IDENT, "result", 1, 40), output_name, type_name="Whole Number")
320
+ action.outputs = [output_param]
321
+
322
+ # Create body (empty for simplicity)
323
+ action.body = BlockStatement(Token(TokenType.DELIM_LBRACE, "{", 2, 1))
324
+ action.body.statements = []
325
+
326
+ # Create program
327
+ program = Program([action])
328
+
329
+ # Analyze
330
+ analyzer = SemanticAnalyzer()
331
+ _, errors = analyzer.analyze(program)
332
+
333
+ # Should have no errors
334
+ assert len(errors) == 0
335
+
336
+ # Function should be defined with return type
337
+ func_info = analyzer.symbol_table.lookup("add_one")
338
+ assert func_info is not None
339
+ assert func_info.type_spec == ["Function"]
340
+ assert func_info.return_type == "Whole Number"
341
+
342
+ def test_scoped_definitions(self) -> None:
343
+ """Test variable scoping - variables defined in inner scope not accessible in outer scope."""
344
+ source = """
345
+ Define `x` as Whole Number.
346
+ Set `x` to _5_.
347
+
348
+ If _yes_ then:
349
+ > Define `y` as Text.
350
+ > Set `y` to _"hello"_.
351
+
352
+ Set `y` to _"world"_.
353
+ """
354
+ parser = Parser()
355
+ program = parser.parse(source, check_semantics=False)
356
+
357
+ analyzer = SemanticAnalyzer()
358
+ _, errors = analyzer.analyze(program)
359
+
360
+ # Should have one error for accessing 'y' outside its scope
361
+ assert len(errors) == 1
362
+ assert isinstance(errors[0], MDNameError)
363
+ assert "not defined" in str(errors[0])
364
+ assert "y" in str(errors[0])
@@ -0,0 +1,104 @@
1
+ """Tests for error message generation.
2
+
3
+ This module tests the generation of helpful error messages with suggestions.
4
+ """
5
+
6
+ from machine_dialect.semantic.error_messages import ErrorMessageGenerator
7
+
8
+
9
+ class TestErrorMessages:
10
+ """Test error message generation."""
11
+
12
+ def test_undefined_variable_message(self) -> None:
13
+ """Test undefined variable error message."""
14
+ msg = ErrorMessageGenerator.undefined_variable("counter", 5, 10, similar_vars=["count", "counter1"])
15
+
16
+ assert "Variable 'counter' is not defined" in msg
17
+ assert "line 5, column 10" in msg
18
+ assert "Did you mean" in msg
19
+ assert "count" in msg
20
+ assert "Define `counter` as Type" in msg
21
+
22
+ def test_type_mismatch_message(self) -> None:
23
+ """Test type mismatch error message."""
24
+ msg = ErrorMessageGenerator.type_mismatch("age", ["Whole Number"], "Text", 8, 5, '"twenty"')
25
+
26
+ assert "Cannot set Whole Number variable 'age'" in msg
27
+ assert "Text value" in msg
28
+ assert "Expected: Whole Number" in msg
29
+ assert "Actual: Text" in msg
30
+
31
+ def test_type_mismatch_union_message(self) -> None:
32
+ """Test type mismatch error message for union types."""
33
+ msg = ErrorMessageGenerator.type_mismatch("id", ["Whole Number", "Text"], "Yes/No", 10, 3, "yes")
34
+
35
+ assert "Cannot set 'id'" in msg
36
+ assert "Yes/No value" in msg
37
+ assert "Expected: Whole Number or Text" in msg
38
+ assert "Actual: Yes/No" in msg
39
+
40
+ def test_redefinition_message(self) -> None:
41
+ """Test redefinition error message."""
42
+ msg = ErrorMessageGenerator.redefinition("x", 10, 1, 5, 1)
43
+
44
+ assert "Variable 'x' is already defined" in msg
45
+ assert "line 10, column 1" in msg
46
+ assert "Original definition at line 5" in msg
47
+ assert "Use 'Set' to change the value" in msg
48
+
49
+ def test_uninitialized_use_message(self) -> None:
50
+ """Test uninitialized use error message."""
51
+ msg = ErrorMessageGenerator.uninitialized_use("data", 15, 8, 3)
52
+
53
+ assert "Variable 'data' is used before being initialized" in msg
54
+ assert "line 15, column 8" in msg
55
+ assert "defined at line 3" in msg
56
+ assert "Set `data` to value" in msg
57
+
58
+ def test_invalid_type_message(self) -> None:
59
+ """Test invalid type error message."""
60
+ msg = ErrorMessageGenerator.invalid_type("Str", 5, 15, ["Text", "Whole Number", "Float", "Yes/No"])
61
+
62
+ assert "Unknown type 'Str'" in msg
63
+ assert "Valid types:" in msg
64
+ # The similarity detection may not suggest Text for "Str" with default threshold
65
+
66
+ def test_undefined_without_suggestions(self) -> None:
67
+ """Test undefined variable message without suggestions."""
68
+ msg = ErrorMessageGenerator.undefined_variable("xyz", 3, 7, similar_vars=None)
69
+
70
+ assert "Variable 'xyz' is not defined" in msg
71
+ assert "line 3, column 7" in msg
72
+ assert "Did you mean" not in msg
73
+ assert "Define `xyz` as Type" in msg
74
+
75
+ def test_type_conversion_hints(self) -> None:
76
+ """Test type mismatch messages include conversion hints."""
77
+ # Whole Number to Float hint
78
+ msg = ErrorMessageGenerator.type_mismatch("price", ["Float"], "Whole Number", 5, 10, "42")
79
+ assert "Use _42.0_ to make it a Float" in msg
80
+
81
+ # Float to Whole Number hint
82
+ msg = ErrorMessageGenerator.type_mismatch("count", ["Whole Number"], "Float", 6, 10, "3.14")
83
+ assert "Float values cannot be assigned to Whole Number variables" in msg
84
+
85
+ def test_similar_name_detection(self) -> None:
86
+ """Test similar name detection algorithm."""
87
+ similar = ErrorMessageGenerator._find_similar(
88
+ "count", ["counter", "amount", "count1", "total", "cnt"], threshold=0.6
89
+ )
90
+
91
+ # Should find similar names
92
+ assert "count1" in similar
93
+ assert "counter" in similar
94
+
95
+ # Should be ordered by similarity
96
+ assert similar.index("count1") < similar.index("cnt")
97
+
98
+ def test_invalid_type_with_multiple_suggestions(self) -> None:
99
+ """Test invalid type message with multiple suggestions."""
100
+ msg = ErrorMessageGenerator.invalid_type("Num", 7, 20, ["Number", "Whole Number", "Float", "Text", "Yes/No"])
101
+
102
+ assert "Unknown type 'Num'" in msg
103
+ assert "Did you mean one of:" in msg or "Did you mean 'Number'?" in msg
104
+ assert "Valid types:" in msg
@@ -0,0 +1,10 @@
1
+ """Edge case tests for Machine Dialectâ„¢ collection operations.
2
+
3
+ This package contains comprehensive edge case testing for:
4
+ - Empty collection operations
5
+ - Boundary access conditions
6
+ - Invalid operations
7
+ - Type mixing scenarios
8
+ - Nested structures
9
+ - Named list (dictionary) edge cases
10
+ """