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,667 @@
1
+ #!/usr/bin/env python3
2
+ """Machine Dialect™ CLI.
3
+
4
+ Main command-line interface for the Machine Dialect™ language.
5
+ Provides commands for compiling, running, and interacting with Machine Dialect™ programs.
6
+ """
7
+
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ import click
12
+
13
+ from machine_dialect.compiler import Compiler, CompilerConfig
14
+ from machine_dialect.repl.repl import REPL
15
+
16
+
17
+ @click.group()
18
+ @click.version_option(version="0.1.0", prog_name="Machine Dialect™")
19
+ def cli() -> None:
20
+ """Machine Dialect™ - Natural language programming in Markdown."""
21
+ pass
22
+
23
+
24
+ @cli.command()
25
+ @click.argument("source_file", type=click.Path(exists=True))
26
+ @click.option(
27
+ "-o",
28
+ "--output",
29
+ type=click.Path(),
30
+ help="Output file path (default: source.mdbc)",
31
+ )
32
+ @click.option(
33
+ "-d",
34
+ "--disassemble",
35
+ is_flag=True,
36
+ help="Show disassembly after compilation",
37
+ )
38
+ @click.option(
39
+ "-v",
40
+ "--verbose",
41
+ is_flag=True,
42
+ help="Show detailed compilation information",
43
+ )
44
+ @click.option(
45
+ "-m",
46
+ "--module-name",
47
+ type=str,
48
+ help="Name for the compiled module (default: source file name)",
49
+ )
50
+ @click.option(
51
+ "--dump-mir",
52
+ is_flag=True,
53
+ help="Dump MIR representation after generation",
54
+ )
55
+ @click.option(
56
+ "--show-cfg",
57
+ type=click.Path(),
58
+ help="Export control flow graph to DOT file",
59
+ )
60
+ @click.option(
61
+ "--opt-report",
62
+ is_flag=True,
63
+ help="Show optimization report after compilation",
64
+ )
65
+ @click.option(
66
+ "--mir-phase",
67
+ is_flag=True,
68
+ help="Stop after MIR generation (no bytecode)",
69
+ )
70
+ @click.option(
71
+ "--opt-level",
72
+ type=click.Choice(["0", "1", "2", "3"]),
73
+ default="2",
74
+ help="Optimization level (0=none, 3=aggressive)",
75
+ )
76
+ def compile(
77
+ source_file: str,
78
+ output: str | None,
79
+ disassemble: bool,
80
+ verbose: bool,
81
+ module_name: str | None,
82
+ dump_mir: bool,
83
+ show_cfg: str | None,
84
+ opt_report: bool,
85
+ mir_phase: bool,
86
+ opt_level: str,
87
+ ) -> None:
88
+ """Compile a Machine Dialect™ source file to bytecode."""
89
+ # Create compiler configuration from CLI options
90
+ config = CompilerConfig.from_cli_options(
91
+ opt_level=opt_level,
92
+ dump_mir=dump_mir,
93
+ show_cfg=show_cfg,
94
+ opt_report=opt_report,
95
+ verbose=verbose or disassemble,
96
+ mir_phase=mir_phase,
97
+ output=output,
98
+ module_name=module_name,
99
+ )
100
+
101
+ # Create compiler instance
102
+ compiler = Compiler(config)
103
+
104
+ # Compile the file
105
+ success = compiler.compile_file(source_file, output)
106
+
107
+ # Exit with appropriate code
108
+ sys.exit(0 if success else 1)
109
+
110
+
111
+ @cli.command()
112
+ @click.argument("file", type=click.Path(exists=True))
113
+ @click.option(
114
+ "-d",
115
+ "--debug",
116
+ is_flag=True,
117
+ help="Enable debug mode",
118
+ )
119
+ def run(file: str, debug: bool) -> None:
120
+ """Run a Machine Dialect™ file (source .md or compiled .mdbc)."""
121
+ from pathlib import Path
122
+
123
+ file_path = Path(file)
124
+ extension = file_path.suffix.lower()
125
+
126
+ if extension == ".md":
127
+ # Run source file with MIR interpreter
128
+ _run_interpreted(file_path, debug)
129
+ elif extension == ".mdbc":
130
+ # Run compiled bytecode with Rust VM
131
+ _run_compiled(file, debug)
132
+ else:
133
+ click.echo(f"Error: Unsupported file type '{extension}'", err=True)
134
+ click.echo("Supported extensions: .md (source), .mdbc (compiled bytecode)", err=True)
135
+ sys.exit(1)
136
+
137
+
138
+ def _run_interpreted(source_path: Path, debug: bool) -> None:
139
+ """Run source file with MIR interpreter."""
140
+ from machine_dialect.compiler.config import CompilerConfig
141
+ from machine_dialect.compiler.context import CompilationContext
142
+ from machine_dialect.compiler.phases.hir_generation import HIRGenerationPhase
143
+ from machine_dialect.compiler.phases.mir_generation import MIRGenerationPhase
144
+ from machine_dialect.mir.mir_interpreter import MIRInterpreter
145
+ from machine_dialect.parser.parser import Parser
146
+
147
+ try:
148
+ # Read source file
149
+ with open(source_path, encoding="utf-8") as f:
150
+ source = f.read()
151
+
152
+ if debug:
153
+ click.echo(f"Interpreting {source_path}...")
154
+
155
+ # Parse to AST
156
+ parser = Parser()
157
+ ast = parser.parse(source)
158
+ if ast is None:
159
+ # Check if parser has errors and display them
160
+ if parser.has_errors():
161
+ for error in parser.errors:
162
+ click.echo(f"Parse error: {error}", err=True)
163
+ else:
164
+ click.echo("Error: Failed to parse source", err=True)
165
+ sys.exit(1)
166
+
167
+ # Generate HIR
168
+ config = CompilerConfig(verbose=debug)
169
+ context = CompilationContext(source_path=source_path, config=config, source_content=source)
170
+ hir_phase = HIRGenerationPhase()
171
+ hir = hir_phase.run(context, ast)
172
+ if hir is None:
173
+ click.echo("Error: Failed to generate HIR", err=True)
174
+ sys.exit(1)
175
+
176
+ # Generate MIR
177
+ mir_phase = MIRGenerationPhase()
178
+ mir_module = mir_phase.run(context, hir)
179
+ if mir_module is None:
180
+ click.echo("Error: Failed to generate MIR", err=True)
181
+ sys.exit(1)
182
+
183
+ # TODO: Fix optimizer bug where it removes necessary variable stores in SSA form
184
+ # The optimizer incorrectly identifies stores like `n_minus_1.0 = t6` as dead code
185
+ # and removes them, but keeps the corresponding loads like `t10 = n_minus_1.0`,
186
+ # causing "Undefined variable" errors at runtime.
187
+ #
188
+ # Example of the bug:
189
+ # BEFORE optimization:
190
+ # n_minus_1.0 = t6 # This gets removed
191
+ # t10 = n_minus_1.0 # This stays, causing error
192
+ #
193
+ # Temporarily disable optimization until the dead code elimination pass
194
+ # is fixed to properly track SSA variable usage across basic blocks.
195
+ #
196
+ # To reproduce the bug, uncomment the line below and run:
197
+ # python -m machine_dialect run benchmark/fibonacci.md
198
+ #
199
+ # mir_module, _ = optimize_mir(mir_module, optimization_level=2)
200
+
201
+ # Interpret MIR
202
+ interpreter = MIRInterpreter()
203
+ result = interpreter.interpret_module(mir_module, trace=debug)
204
+
205
+ # Display result only in debug mode
206
+ if debug and result is not None:
207
+ click.echo(f"Result: {result}")
208
+
209
+ # Display output if any
210
+ output = interpreter.get_output()
211
+ if output:
212
+ for line in output:
213
+ click.echo(line)
214
+
215
+ except Exception as e:
216
+ click.echo(f"Runtime error: {e}", err=True)
217
+ if debug:
218
+ import traceback
219
+
220
+ traceback.print_exc()
221
+ sys.exit(1)
222
+
223
+
224
+ def _run_compiled(bytecode_file: str, debug: bool) -> None:
225
+ """Run compiled bytecode with Rust VM."""
226
+ try:
227
+ # Import the Rust VM module
228
+ # Add the site-packages path to ensure we get the compiled module
229
+ import site
230
+ import sys as _sys
231
+
232
+ site_packages = site.getsitepackages()
233
+ for path in site_packages:
234
+ if path not in _sys.path:
235
+ _sys.path.insert(0, path)
236
+
237
+ import machine_dialect_vm
238
+
239
+ # Create VM instance
240
+ vm = machine_dialect_vm.RustVM()
241
+
242
+ # Enable debug mode if requested
243
+ if debug:
244
+ vm.set_debug(True)
245
+ click.echo("Debug mode enabled")
246
+
247
+ # Load and execute bytecode
248
+ if debug:
249
+ click.echo(f"Loading bytecode from: {bytecode_file}")
250
+ vm.load_bytecode(bytecode_file)
251
+
252
+ if debug:
253
+ click.echo("Executing bytecode...")
254
+ result = vm.execute()
255
+
256
+ # Display result only in debug mode
257
+ if debug and result is not None:
258
+ click.echo(f"Result: {result}")
259
+
260
+ if debug:
261
+ click.echo(f"Instructions executed: {vm.instruction_count()}")
262
+
263
+ except ImportError as e:
264
+ click.echo("Error: Rust VM module not found", err=True)
265
+ click.echo("Please build the Rust VM first:", err=True)
266
+ click.echo(" ./build_vm.sh", err=True)
267
+ click.echo(f"Details: {e}", err=True)
268
+ sys.exit(1)
269
+ except Exception as e:
270
+ click.echo(f"Runtime error: {e}", err=True)
271
+ sys.exit(1)
272
+
273
+
274
+ @cli.command()
275
+ @click.option(
276
+ "--tokens",
277
+ is_flag=True,
278
+ help="Run in token debug mode",
279
+ )
280
+ @click.option(
281
+ "--ast",
282
+ is_flag=True,
283
+ help="Run in AST mode (show AST instead of evaluating)",
284
+ )
285
+ def shell(tokens: bool, ast: bool) -> None:
286
+ """Start an interactive Machine Dialect™ shell (REPL)."""
287
+ if tokens and ast:
288
+ click.echo("Error: --tokens and --ast flags are not compatible", err=True)
289
+ sys.exit(1)
290
+ repl = REPL(debug_tokens=tokens, show_ast=ast)
291
+ exit_code = repl.run()
292
+ sys.exit(exit_code)
293
+
294
+
295
+ @cli.command()
296
+ @click.argument("bytecode_file", type=click.Path(exists=True))
297
+ def disasm(bytecode_file: str) -> None:
298
+ """Disassemble a compiled bytecode file."""
299
+ # TODO: Implement disassembly for new register-based bytecode
300
+ # The code below will be re-enabled once bytecode generation is implemented
301
+ # try:
302
+ # with open(bytecode_file, "rb") as f:
303
+ # module = deserialize_module(f)
304
+ # except FileNotFoundError:
305
+ # click.echo(f"Error: File '{bytecode_file}' not found", err=True)
306
+ # sys.exit(1)
307
+ # except InvalidMagicError as e:
308
+ # click.echo(f"Error: {e}", err=True)
309
+ # sys.exit(1)
310
+ # except SerializationError as e:
311
+ # click.echo(f"Error loading file: {e}", err=True)
312
+ # sys.exit(1)
313
+ # except Exception as e:
314
+ # click.echo(f"Error loading file: {e}", err=True)
315
+ # sys.exit(1)
316
+
317
+ click.echo("Disassembly not yet implemented for register-based bytecode", err=True)
318
+ sys.exit(1)
319
+
320
+
321
+ @cli.command()
322
+ @click.argument("task", nargs=-1, required=True)
323
+ @click.option(
324
+ "--iterations",
325
+ type=int,
326
+ default=5,
327
+ help="Maximum iterations to attempt (default: 5)",
328
+ )
329
+ @click.option(
330
+ "--api-key",
331
+ help="AI API key (or set in .mdconfig file or MD_AI_API_KEY env var)",
332
+ )
333
+ @click.option(
334
+ "--model",
335
+ help="AI model to use (e.g., gpt-5). If not specified, uses .mdconfig or env vars",
336
+ )
337
+ @click.option(
338
+ "-o",
339
+ "--output",
340
+ type=click.Path(),
341
+ help="Save final code to file",
342
+ )
343
+ @click.option(
344
+ "--save-history",
345
+ type=click.Path(),
346
+ help="Save iteration history to JSON file",
347
+ )
348
+ @click.option(
349
+ "-q",
350
+ "--quiet",
351
+ is_flag=True,
352
+ help="Minimal output",
353
+ )
354
+ def agent(
355
+ task: tuple[str, ...],
356
+ iterations: int,
357
+ api_key: str | None,
358
+ model: str | None,
359
+ output: str | None,
360
+ save_history: str | None,
361
+ quiet: bool,
362
+ ) -> None:
363
+ """Iteratively develop Machine Dialect™ code with AI assistance.
364
+
365
+ The agent will automatically generate, compile, execute, and fix code
366
+ until it successfully completes the task or reaches the iteration limit.
367
+
368
+ Examples:
369
+ machine-dialect agent "calculate the factorial of 5"
370
+ machine-dialect agent "sort a list of numbers" --iterations 3
371
+ machine-dialect agent "check if a number is prime" --output prime.md
372
+ """
373
+ # Join task arguments
374
+ task_description = " ".join(task)
375
+
376
+ if not task_description.strip():
377
+ click.echo("Error: Task description cannot be empty", err=True)
378
+ sys.exit(1)
379
+
380
+ # Load configuration
381
+ from machine_dialect.cfg.config import ConfigLoader
382
+
383
+ loader = ConfigLoader()
384
+ config = loader.load()
385
+
386
+ # Override with command-line arguments if provided
387
+ if api_key:
388
+ config.key = api_key
389
+ if model:
390
+ config.model = model
391
+
392
+ # Check configuration
393
+ if not config.key:
394
+ click.echo("Error: No API key configured", err=True)
395
+ click.echo(loader.get_error_message(), err=True)
396
+ sys.exit(1)
397
+
398
+ if not config.model:
399
+ click.echo("Error: No AI model configured", err=True)
400
+ click.echo(loader.get_error_message(), err=True)
401
+ sys.exit(1)
402
+
403
+ try:
404
+ # Import OpenAI
405
+ try:
406
+ from openai import OpenAI
407
+ except ImportError:
408
+ click.echo("Error: OpenAI library not installed", err=True)
409
+ click.echo("Install with: pip install openai", err=True)
410
+ sys.exit(1)
411
+
412
+ # Initialize client
413
+ client = OpenAI(api_key=config.key)
414
+
415
+ # Create agent
416
+ from machine_dialect.agent import Agent
417
+
418
+ agent = Agent(client, config.model, verbose=not quiet)
419
+
420
+ # Solve the task
421
+ result = agent.solve(task_description, max_iterations=iterations)
422
+
423
+ # Display results
424
+ if not quiet:
425
+ click.echo("\n" + "=" * 60)
426
+
427
+ if result.success:
428
+ click.echo(f"✨ SUCCESS in {result.iterations} iteration(s)!")
429
+ if result.output:
430
+ click.echo(f"Output: {result.output}")
431
+
432
+ # Save code if requested
433
+ if output and result.code:
434
+ with open(output, "w") as f:
435
+ f.write(result.code)
436
+ click.echo(f"\nCode saved to: {output}")
437
+ elif not quiet:
438
+ click.echo("\nFinal code:")
439
+ click.echo("-" * 40)
440
+ click.echo(result.code)
441
+ click.echo("-" * 40)
442
+
443
+ else:
444
+ click.echo(f"❌ Failed after {result.iterations} iterations", err=True)
445
+ if not quiet:
446
+ click.echo("Try with more iterations or a clearer task description.", err=True)
447
+
448
+ # Save history if requested
449
+ if save_history and result.history:
450
+ import json
451
+
452
+ with open(save_history, "w") as f:
453
+ json.dump(result.history, f, indent=2)
454
+ click.echo(f"History saved to: {save_history}")
455
+
456
+ # Exit with appropriate code
457
+ sys.exit(0 if result.success else 1)
458
+
459
+ except Exception as e:
460
+ click.echo(f"Error: {e}", err=True)
461
+ if not quiet:
462
+ import traceback
463
+
464
+ traceback.print_exc()
465
+ sys.exit(1)
466
+
467
+
468
+ @cli.command()
469
+ @click.argument("task", nargs=-1, required=True)
470
+ @click.option(
471
+ "--api-key",
472
+ help="AI API key (or set in .mdconfig file or MD_AI_API_KEY env var)",
473
+ )
474
+ @click.option(
475
+ "--model",
476
+ help="AI model to use (e.g., gpt-5, gpt-5-mini). If not specified, uses .mdconfig or env vars",
477
+ )
478
+ @click.option(
479
+ "-t",
480
+ "--temperature",
481
+ type=float,
482
+ default=0.7,
483
+ help="Sampling temperature 0-2 (default: 0.7)",
484
+ )
485
+ @click.option(
486
+ "-m",
487
+ "--max-tokens",
488
+ type=int,
489
+ default=400,
490
+ help="Maximum tokens to generate (default: 400)",
491
+ )
492
+ @click.option(
493
+ "-o",
494
+ "--output",
495
+ type=click.Path(),
496
+ help="Save generated code to file",
497
+ )
498
+ @click.option(
499
+ "--validate/--no-validate",
500
+ default=True,
501
+ help="Validate generated code syntax (default: validate)",
502
+ )
503
+ @click.option(
504
+ "-v",
505
+ "--verbose",
506
+ is_flag=True,
507
+ help="Show detailed generation information",
508
+ )
509
+ def llm(
510
+ task: tuple[str, ...],
511
+ api_key: str | None,
512
+ model: str | None,
513
+ temperature: float,
514
+ max_tokens: int,
515
+ output: str | None,
516
+ validate: bool,
517
+ verbose: bool,
518
+ ) -> None:
519
+ """Generate Machine Dialect™ code using AI models.
520
+
521
+ Examples:
522
+ machine-dialect llm "calculate the factorial of 5"
523
+ machine-dialect llm "check if age 18 can vote" --output vote_check.md
524
+ machine-dialect llm "sum numbers from 1 to 10" -t 0.1 -m 500
525
+
526
+ Note: Escape backticks in your task description or use single quotes:
527
+ machine-dialect llm 'Sum `r` and `s`, being r=11 and s=45'
528
+ """
529
+ # Join task arguments into a single string
530
+ task_description = " ".join(task)
531
+
532
+ if not task_description.strip():
533
+ click.echo("Error: Task description cannot be empty", err=True)
534
+ sys.exit(1)
535
+
536
+ # Load configuration
537
+ from machine_dialect.cfg.config import ConfigLoader
538
+
539
+ loader = ConfigLoader()
540
+ config = loader.load()
541
+
542
+ # Override with command-line arguments if provided
543
+ if api_key:
544
+ config.key = api_key
545
+ if model:
546
+ config.model = model
547
+
548
+ # Check if configuration is valid
549
+ if not config.key:
550
+ click.echo("Error: No API key configured", err=True)
551
+ click.echo(loader.get_error_message(), err=True)
552
+ sys.exit(1)
553
+
554
+ if not config.model:
555
+ click.echo("Error: No AI model configured", err=True)
556
+ click.echo(loader.get_error_message(), err=True)
557
+ sys.exit(1)
558
+
559
+ try:
560
+ # Import OpenAI and our generator
561
+ try:
562
+ from openai import OpenAI
563
+ except ImportError:
564
+ click.echo("Error: OpenAI library not installed", err=True)
565
+ click.echo("Install with: pip install openai", err=True)
566
+ sys.exit(1)
567
+
568
+ from machine_dialect.cfg import CFGParser
569
+ from machine_dialect.cfg.openai_generation import generate_with_openai
570
+
571
+ if verbose:
572
+ click.echo(f"Model: {config.model}")
573
+ click.echo(f"Task: {task_description}")
574
+ click.echo(f"Temperature: {temperature}")
575
+ click.echo(f"Max tokens: {max_tokens}")
576
+ click.echo("-" * 50)
577
+
578
+ # Initialize OpenAI client
579
+ client = OpenAI(api_key=config.key)
580
+
581
+ # Generate code
582
+ click.echo(f"Generating Machine Dialect™ code with {config.model}...")
583
+
584
+ try:
585
+ generated_code, _token_info = generate_with_openai(
586
+ client=client,
587
+ model=config.model,
588
+ task_description=task_description,
589
+ temperature=temperature,
590
+ max_tokens=max_tokens,
591
+ )
592
+
593
+ # Check what we got
594
+ if not generated_code:
595
+ click.echo("Error: Empty response from API", err=True)
596
+ sys.exit(1)
597
+
598
+ except Exception as e:
599
+ click.echo(f"Error: API call failed - {e}", err=True)
600
+ sys.exit(1)
601
+
602
+ # Only show output if we actually got code
603
+ click.echo("\n" + "=" * 50)
604
+ click.echo("Generated Code:")
605
+ click.echo("=" * 50)
606
+ click.echo(generated_code)
607
+ click.echo("=" * 50)
608
+
609
+ # Validate if requested
610
+ if validate:
611
+ click.echo("\nValidating syntax...")
612
+ parser = CFGParser()
613
+
614
+ try:
615
+ is_valid = parser.validate(generated_code)
616
+ if is_valid:
617
+ click.echo("✓ Generated code is syntactically valid!")
618
+
619
+ if verbose:
620
+ tree = parser.parse(generated_code)
621
+ click.echo("\nAST Preview:")
622
+ ast_str = parser.pretty_print(tree)
623
+ # Show first few lines
624
+ for line in ast_str.split("\n")[:8]:
625
+ click.echo(f" {line}")
626
+ if len(ast_str.split("\n")) > 8:
627
+ click.echo(" ...")
628
+ else:
629
+ click.echo("✗ Generated code has syntax errors", err=True)
630
+ except Exception as e:
631
+ click.echo(f"✗ Validation error: {e}", err=True)
632
+
633
+ # Save to file if requested
634
+ if output:
635
+ try:
636
+ with open(output, "w") as f:
637
+ f.write(generated_code)
638
+ click.echo(f"\n✓ Code saved to: {output}")
639
+ except Exception as e:
640
+ click.echo(f"Error saving file: {e}", err=True)
641
+ sys.exit(1)
642
+
643
+ if verbose:
644
+ click.echo("\n" + "-" * 50)
645
+ click.echo("Generation complete!")
646
+ click.echo("CFG ensures 100% syntactic correctness")
647
+
648
+ except ImportError as e:
649
+ click.echo(f"Error: Missing required module: {e}", err=True)
650
+ click.echo("Make sure Lark is installed: pip install lark", err=True)
651
+ sys.exit(1)
652
+ except Exception as e:
653
+ click.echo(f"Error: {e}", err=True)
654
+ if verbose:
655
+ import traceback
656
+
657
+ traceback.print_exc()
658
+ sys.exit(1)
659
+
660
+
661
+ def main() -> None:
662
+ """Entry point for the CLI."""
663
+ cli()
664
+
665
+
666
+ if __name__ == "__main__":
667
+ main()
@@ -0,0 +1,5 @@
1
+ """Machine Dialect™ AI Agent module."""
2
+
3
+ from machine_dialect.agent.agent import Agent
4
+
5
+ __all__ = ["Agent"]