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.
- machine_dialect/__main__.py +667 -0
- machine_dialect/agent/__init__.py +5 -0
- machine_dialect/agent/agent.py +360 -0
- machine_dialect/ast/__init__.py +95 -0
- machine_dialect/ast/ast_node.py +35 -0
- machine_dialect/ast/call_expression.py +82 -0
- machine_dialect/ast/dict_extraction.py +60 -0
- machine_dialect/ast/expressions.py +439 -0
- machine_dialect/ast/literals.py +309 -0
- machine_dialect/ast/program.py +35 -0
- machine_dialect/ast/statements.py +1433 -0
- machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
- machine_dialect/ast/tests/test_boolean_literal.py +29 -0
- machine_dialect/ast/tests/test_collection_hir.py +138 -0
- machine_dialect/ast/tests/test_define_statement.py +142 -0
- machine_dialect/ast/tests/test_desugar.py +541 -0
- machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
- machine_dialect/cfg/__init__.py +6 -0
- machine_dialect/cfg/config.py +156 -0
- machine_dialect/cfg/examples.py +221 -0
- machine_dialect/cfg/generate_with_ai.py +187 -0
- machine_dialect/cfg/openai_generation.py +200 -0
- machine_dialect/cfg/parser.py +94 -0
- machine_dialect/cfg/tests/__init__.py +1 -0
- machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
- machine_dialect/cfg/tests/test_config.py +188 -0
- machine_dialect/cfg/tests/test_examples.py +391 -0
- machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
- machine_dialect/cfg/tests/test_openai_generation.py +256 -0
- machine_dialect/codegen/__init__.py +5 -0
- machine_dialect/codegen/bytecode_module.py +89 -0
- machine_dialect/codegen/bytecode_serializer.py +300 -0
- machine_dialect/codegen/opcodes.py +101 -0
- machine_dialect/codegen/register_codegen.py +1996 -0
- machine_dialect/codegen/symtab.py +208 -0
- machine_dialect/codegen/tests/__init__.py +1 -0
- machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
- machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
- machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
- machine_dialect/codegen/tests/test_symtab.py +418 -0
- machine_dialect/codegen/vm_serializer.py +621 -0
- machine_dialect/compiler/__init__.py +18 -0
- machine_dialect/compiler/compiler.py +197 -0
- machine_dialect/compiler/config.py +149 -0
- machine_dialect/compiler/context.py +149 -0
- machine_dialect/compiler/phases/__init__.py +19 -0
- machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
- machine_dialect/compiler/phases/codegen.py +40 -0
- machine_dialect/compiler/phases/hir_generation.py +39 -0
- machine_dialect/compiler/phases/mir_generation.py +86 -0
- machine_dialect/compiler/phases/optimization.py +110 -0
- machine_dialect/compiler/phases/parsing.py +39 -0
- machine_dialect/compiler/pipeline.py +143 -0
- machine_dialect/compiler/tests/__init__.py +1 -0
- machine_dialect/compiler/tests/test_compiler.py +568 -0
- machine_dialect/compiler/vm_runner.py +173 -0
- machine_dialect/errors/__init__.py +32 -0
- machine_dialect/errors/exceptions.py +369 -0
- machine_dialect/errors/messages.py +82 -0
- machine_dialect/errors/tests/__init__.py +0 -0
- machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
- machine_dialect/errors/tests/test_name_errors.py +118 -0
- machine_dialect/helpers/__init__.py +0 -0
- machine_dialect/helpers/stopwords.py +225 -0
- machine_dialect/helpers/validators.py +30 -0
- machine_dialect/lexer/__init__.py +9 -0
- machine_dialect/lexer/constants.py +23 -0
- machine_dialect/lexer/lexer.py +907 -0
- machine_dialect/lexer/tests/__init__.py +0 -0
- machine_dialect/lexer/tests/helpers.py +86 -0
- machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
- machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
- machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
- machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
- machine_dialect/lexer/tests/test_comments.py +200 -0
- machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
- machine_dialect/lexer/tests/test_lexer_position.py +113 -0
- machine_dialect/lexer/tests/test_list_tokens.py +282 -0
- machine_dialect/lexer/tests/test_stopwords.py +80 -0
- machine_dialect/lexer/tests/test_strict_equality.py +129 -0
- machine_dialect/lexer/tests/test_token.py +41 -0
- machine_dialect/lexer/tests/test_tokenization.py +294 -0
- machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
- machine_dialect/lexer/tests/test_url_literals.py +169 -0
- machine_dialect/lexer/tokens.py +487 -0
- machine_dialect/linter/__init__.py +10 -0
- machine_dialect/linter/__main__.py +144 -0
- machine_dialect/linter/linter.py +154 -0
- machine_dialect/linter/rules/__init__.py +8 -0
- machine_dialect/linter/rules/base.py +112 -0
- machine_dialect/linter/rules/statement_termination.py +99 -0
- machine_dialect/linter/tests/__init__.py +1 -0
- machine_dialect/linter/tests/mdrules/__init__.py +0 -0
- machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
- machine_dialect/linter/tests/test_linter.py +81 -0
- machine_dialect/linter/tests/test_rules.py +110 -0
- machine_dialect/linter/tests/test_violations.py +71 -0
- machine_dialect/linter/violations.py +51 -0
- machine_dialect/mir/__init__.py +69 -0
- machine_dialect/mir/analyses/__init__.py +20 -0
- machine_dialect/mir/analyses/alias_analysis.py +315 -0
- machine_dialect/mir/analyses/dominance_analysis.py +49 -0
- machine_dialect/mir/analyses/escape_analysis.py +286 -0
- machine_dialect/mir/analyses/loop_analysis.py +272 -0
- machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
- machine_dialect/mir/analyses/type_analysis.py +448 -0
- machine_dialect/mir/analyses/use_def_chains.py +232 -0
- machine_dialect/mir/basic_block.py +385 -0
- machine_dialect/mir/dataflow.py +445 -0
- machine_dialect/mir/debug_info.py +208 -0
- machine_dialect/mir/hir_to_mir.py +1738 -0
- machine_dialect/mir/mir_dumper.py +366 -0
- machine_dialect/mir/mir_function.py +167 -0
- machine_dialect/mir/mir_instructions.py +1877 -0
- machine_dialect/mir/mir_interpreter.py +556 -0
- machine_dialect/mir/mir_module.py +225 -0
- machine_dialect/mir/mir_printer.py +480 -0
- machine_dialect/mir/mir_transformer.py +410 -0
- machine_dialect/mir/mir_types.py +367 -0
- machine_dialect/mir/mir_validation.py +455 -0
- machine_dialect/mir/mir_values.py +268 -0
- machine_dialect/mir/optimization_config.py +233 -0
- machine_dialect/mir/optimization_pass.py +251 -0
- machine_dialect/mir/optimization_pipeline.py +355 -0
- machine_dialect/mir/optimizations/__init__.py +84 -0
- machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
- machine_dialect/mir/optimizations/branch_prediction.py +372 -0
- machine_dialect/mir/optimizations/constant_propagation.py +634 -0
- machine_dialect/mir/optimizations/cse.py +398 -0
- machine_dialect/mir/optimizations/dce.py +288 -0
- machine_dialect/mir/optimizations/inlining.py +551 -0
- machine_dialect/mir/optimizations/jump_threading.py +487 -0
- machine_dialect/mir/optimizations/licm.py +405 -0
- machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
- machine_dialect/mir/optimizations/strength_reduction.py +422 -0
- machine_dialect/mir/optimizations/tail_call.py +207 -0
- machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
- machine_dialect/mir/optimizations/type_narrowing.py +397 -0
- machine_dialect/mir/optimizations/type_specialization.py +447 -0
- machine_dialect/mir/optimizations/type_specific.py +906 -0
- machine_dialect/mir/optimize_mir.py +89 -0
- machine_dialect/mir/pass_manager.py +391 -0
- machine_dialect/mir/profiling/__init__.py +26 -0
- machine_dialect/mir/profiling/profile_collector.py +318 -0
- machine_dialect/mir/profiling/profile_data.py +372 -0
- machine_dialect/mir/profiling/profile_reader.py +272 -0
- machine_dialect/mir/profiling/profile_writer.py +226 -0
- machine_dialect/mir/register_allocation.py +302 -0
- machine_dialect/mir/reporting/__init__.py +17 -0
- machine_dialect/mir/reporting/optimization_reporter.py +314 -0
- machine_dialect/mir/reporting/report_formatter.py +289 -0
- machine_dialect/mir/ssa_construction.py +342 -0
- machine_dialect/mir/tests/__init__.py +1 -0
- machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
- machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
- machine_dialect/mir/tests/test_algebraic_division.py +126 -0
- machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
- machine_dialect/mir/tests/test_basic_block.py +425 -0
- machine_dialect/mir/tests/test_branch_prediction.py +459 -0
- machine_dialect/mir/tests/test_call_lowering.py +168 -0
- machine_dialect/mir/tests/test_collection_lowering.py +604 -0
- machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
- machine_dialect/mir/tests/test_custom_passes.py +166 -0
- machine_dialect/mir/tests/test_debug_info.py +285 -0
- machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
- machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
- machine_dialect/mir/tests/test_double_negation.py +231 -0
- machine_dialect/mir/tests/test_escape_analysis.py +233 -0
- machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
- machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
- machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
- machine_dialect/mir/tests/test_inlining.py +435 -0
- machine_dialect/mir/tests/test_licm.py +472 -0
- machine_dialect/mir/tests/test_mir_dumper.py +313 -0
- machine_dialect/mir/tests/test_mir_instructions.py +445 -0
- machine_dialect/mir/tests/test_mir_module.py +860 -0
- machine_dialect/mir/tests/test_mir_printer.py +387 -0
- machine_dialect/mir/tests/test_mir_types.py +123 -0
- machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
- machine_dialect/mir/tests/test_mir_validation.py +378 -0
- machine_dialect/mir/tests/test_mir_values.py +168 -0
- machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
- machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
- machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
- machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
- machine_dialect/mir/tests/test_pass_manager.py +294 -0
- machine_dialect/mir/tests/test_pass_registration.py +64 -0
- machine_dialect/mir/tests/test_profiling.py +356 -0
- machine_dialect/mir/tests/test_register_allocation.py +307 -0
- machine_dialect/mir/tests/test_report_formatters.py +372 -0
- machine_dialect/mir/tests/test_ssa_construction.py +433 -0
- machine_dialect/mir/tests/test_tail_call.py +236 -0
- machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
- machine_dialect/mir/tests/test_type_narrowing.py +277 -0
- machine_dialect/mir/tests/test_type_specialization.py +421 -0
- machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
- machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
- machine_dialect/mir/type_inference.py +368 -0
- machine_dialect/parser/__init__.py +12 -0
- machine_dialect/parser/enums.py +45 -0
- machine_dialect/parser/parser.py +3655 -0
- machine_dialect/parser/protocols.py +11 -0
- machine_dialect/parser/symbol_table.py +169 -0
- machine_dialect/parser/tests/__init__.py +0 -0
- machine_dialect/parser/tests/helper_functions.py +193 -0
- machine_dialect/parser/tests/test_action_statements.py +334 -0
- machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
- machine_dialect/parser/tests/test_call_statements.py +154 -0
- machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
- machine_dialect/parser/tests/test_collection_mutations.py +264 -0
- machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
- machine_dialect/parser/tests/test_define_integration.py +468 -0
- machine_dialect/parser/tests/test_define_statements.py +311 -0
- machine_dialect/parser/tests/test_dict_extraction.py +115 -0
- machine_dialect/parser/tests/test_empty_literal.py +155 -0
- machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
- machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
- machine_dialect/parser/tests/test_if_empty_block.py +61 -0
- machine_dialect/parser/tests/test_if_statements.py +299 -0
- machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
- machine_dialect/parser/tests/test_infix_expressions.py +680 -0
- machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
- machine_dialect/parser/tests/test_interaction_statements.py +269 -0
- machine_dialect/parser/tests/test_list_literals.py +277 -0
- machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
- machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
- machine_dialect/parser/tests/test_parse_errors.py +114 -0
- machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
- machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
- machine_dialect/parser/tests/test_program.py +13 -0
- machine_dialect/parser/tests/test_return_statements.py +89 -0
- machine_dialect/parser/tests/test_set_statements.py +152 -0
- machine_dialect/parser/tests/test_strict_equality.py +258 -0
- machine_dialect/parser/tests/test_symbol_table.py +217 -0
- machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
- machine_dialect/parser/tests/test_utility_statements.py +423 -0
- machine_dialect/parser/token_buffer.py +159 -0
- machine_dialect/repl/__init__.py +3 -0
- machine_dialect/repl/repl.py +426 -0
- machine_dialect/repl/tests/__init__.py +0 -0
- machine_dialect/repl/tests/test_repl.py +606 -0
- machine_dialect/semantic/__init__.py +12 -0
- machine_dialect/semantic/analyzer.py +906 -0
- machine_dialect/semantic/error_messages.py +189 -0
- machine_dialect/semantic/tests/__init__.py +1 -0
- machine_dialect/semantic/tests/test_analyzer.py +364 -0
- machine_dialect/semantic/tests/test_error_messages.py +104 -0
- machine_dialect/tests/edge_cases/__init__.py +10 -0
- machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
- machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
- machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
- machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
- machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
- machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
- machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
- machine_dialect/tests/integration/test_list_compilation.py +395 -0
- machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
- machine_dialect/type_checking/__init__.py +21 -0
- machine_dialect/type_checking/tests/__init__.py +1 -0
- machine_dialect/type_checking/tests/test_type_system.py +230 -0
- machine_dialect/type_checking/type_system.py +270 -0
- machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
- machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
- machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
- machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
- machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
- machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
- machine_dialect_vm/__init__.pyi +15 -0
@@ -0,0 +1,360 @@
|
|
1
|
+
"""AI Agent for iterative Machine Dialect™ code generation and execution."""
|
2
|
+
|
3
|
+
import tempfile
|
4
|
+
import time
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Any
|
8
|
+
|
9
|
+
from machine_dialect.cfg.openai_generation import generate_with_openai
|
10
|
+
from machine_dialect.compiler import Compiler, CompilerConfig
|
11
|
+
from machine_dialect.compiler.config import OptimizationLevel
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class AgentResult:
|
16
|
+
"""Result from agent execution.
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
success: Whether the task was successfully completed.
|
20
|
+
iterations: Number of iterations taken.
|
21
|
+
code: Final code (if successful).
|
22
|
+
output: Program output (if any).
|
23
|
+
history: Full iteration history.
|
24
|
+
"""
|
25
|
+
|
26
|
+
success: bool
|
27
|
+
iterations: int
|
28
|
+
code: str | None = None
|
29
|
+
output: str | None = None
|
30
|
+
history: list[dict[str, Any]] | None = None
|
31
|
+
|
32
|
+
|
33
|
+
class Agent:
|
34
|
+
"""Iterative AI agent for Machine Dialect™ code generation."""
|
35
|
+
|
36
|
+
def __init__(self, client: Any, model: str = "gpt-5", verbose: bool = True):
|
37
|
+
"""Initialize the agent.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
client: OpenAI client instance.
|
41
|
+
model: Model to use for generation.
|
42
|
+
verbose: Whether to print progress.
|
43
|
+
"""
|
44
|
+
self.client = client
|
45
|
+
self.model = model
|
46
|
+
self.verbose = verbose
|
47
|
+
self.iterations: list[dict[str, Any]] = []
|
48
|
+
self.total_tokens_used = 0
|
49
|
+
|
50
|
+
def solve(self, task: str, max_iterations: int = 5) -> AgentResult:
|
51
|
+
"""Solve a task through iterative code generation.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
task: The task description.
|
55
|
+
max_iterations: Maximum iterations to attempt.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
AgentResult with solution details.
|
59
|
+
"""
|
60
|
+
# Track overall time
|
61
|
+
start_time = time.time()
|
62
|
+
|
63
|
+
if self.verbose:
|
64
|
+
print(f"🤖 Agent starting: {task}")
|
65
|
+
print(f" Model: {self.model}")
|
66
|
+
print(f" Max iterations: {max_iterations}")
|
67
|
+
|
68
|
+
self.iterations = []
|
69
|
+
current_task = task
|
70
|
+
successful_code = None
|
71
|
+
final_output = None
|
72
|
+
|
73
|
+
for i in range(max_iterations):
|
74
|
+
iteration_num = i + 1
|
75
|
+
|
76
|
+
if self.verbose:
|
77
|
+
print(f"\n📍 Iteration {iteration_num}/{max_iterations}")
|
78
|
+
|
79
|
+
# Generate code
|
80
|
+
try:
|
81
|
+
if self.verbose:
|
82
|
+
print(" Generating code...")
|
83
|
+
|
84
|
+
# Time the generation
|
85
|
+
gen_start = time.time()
|
86
|
+
code, token_info = self._generate(current_task)
|
87
|
+
gen_time = time.time() - gen_start
|
88
|
+
|
89
|
+
# Track token usage
|
90
|
+
if token_info:
|
91
|
+
total = token_info.get("total_tokens", 0)
|
92
|
+
prompt = token_info.get("prompt_tokens")
|
93
|
+
completion = token_info.get("completion_tokens")
|
94
|
+
|
95
|
+
if total:
|
96
|
+
self.total_tokens_used += total
|
97
|
+
|
98
|
+
if self.verbose:
|
99
|
+
print(f" ✓ Code generated (CFG-constrained) in {gen_time:.2f}s")
|
100
|
+
|
101
|
+
# Display token info based on what's available
|
102
|
+
if prompt is not None and completion is not None:
|
103
|
+
print(f" 📊 Tokens: {prompt} prompt + {completion} completion = {total} total")
|
104
|
+
elif total:
|
105
|
+
print(f" 📊 Tokens: {total} total")
|
106
|
+
|
107
|
+
if self.total_tokens_used > 0:
|
108
|
+
print(f" 📈 Cumulative: {self.total_tokens_used:,} tokens")
|
109
|
+
|
110
|
+
# Debug: show what was generated
|
111
|
+
print(f" Debug: Generated code: {code!r}")
|
112
|
+
else:
|
113
|
+
if self.verbose:
|
114
|
+
print(" ✓ Code generated (CFG-constrained)")
|
115
|
+
print(f" Debug: Generated code: {code!r}")
|
116
|
+
|
117
|
+
except Exception as e:
|
118
|
+
if self.verbose:
|
119
|
+
print(f" ✗ Generation failed: {e}")
|
120
|
+
|
121
|
+
self.iterations.append(
|
122
|
+
{"iteration": iteration_num, "code": None, "error": str(e), "phase": "generation"}
|
123
|
+
)
|
124
|
+
continue
|
125
|
+
|
126
|
+
# Compile and execute
|
127
|
+
result = self._execute(code)
|
128
|
+
|
129
|
+
# Record iteration
|
130
|
+
self.iterations.append({"iteration": iteration_num, "code": code, "result": result})
|
131
|
+
|
132
|
+
# Check result
|
133
|
+
if result["success"]:
|
134
|
+
if self.verbose:
|
135
|
+
output_msg = result.get("output", "No output")
|
136
|
+
if result.get("instructions"):
|
137
|
+
print(f" ✅ Success! Output: {output_msg}")
|
138
|
+
print(f" 📊 Executed {result['instructions']} instructions")
|
139
|
+
else:
|
140
|
+
print(f" ✅ Success! Output: {output_msg}")
|
141
|
+
|
142
|
+
successful_code = code
|
143
|
+
final_output = result.get("output")
|
144
|
+
|
145
|
+
# Optional: Try to optimize if we have iterations left
|
146
|
+
if iteration_num < max_iterations and self.verbose:
|
147
|
+
if not self._should_optimize(task, code, result):
|
148
|
+
break
|
149
|
+
current_task = f"Optimize this working solution for: {task}\n\nCurrent code:\n{code}"
|
150
|
+
else:
|
151
|
+
break
|
152
|
+
|
153
|
+
else:
|
154
|
+
# Failed - prepare for next iteration
|
155
|
+
error = result.get("error", "Unknown error")
|
156
|
+
phase = result.get("phase", "execution")
|
157
|
+
|
158
|
+
if self.verbose:
|
159
|
+
print(f" ❌ {phase.capitalize()} error: {error}")
|
160
|
+
|
161
|
+
# Build feedback for next iteration
|
162
|
+
current_task = self._build_error_feedback(task, code, error, phase)
|
163
|
+
|
164
|
+
# Calculate total time
|
165
|
+
total_time = time.time() - start_time
|
166
|
+
|
167
|
+
# Print final summary
|
168
|
+
if self.verbose:
|
169
|
+
if self.total_tokens_used > 0:
|
170
|
+
print(f"\n💰 Total: {self.total_tokens_used:,} tokens in {total_time:.2f}s")
|
171
|
+
else:
|
172
|
+
print(f"\n⏱️ Total time: {total_time:.2f}s")
|
173
|
+
|
174
|
+
# Return final result
|
175
|
+
return AgentResult(
|
176
|
+
success=successful_code is not None,
|
177
|
+
iterations=len(self.iterations),
|
178
|
+
code=successful_code,
|
179
|
+
output=final_output,
|
180
|
+
history=self.iterations,
|
181
|
+
)
|
182
|
+
|
183
|
+
def _generate(self, task: str) -> tuple[str, dict[str, int]]:
|
184
|
+
"""Generate code using CFG constraints.
|
185
|
+
|
186
|
+
Args:
|
187
|
+
task: Task description with any feedback.
|
188
|
+
|
189
|
+
Returns:
|
190
|
+
Tuple of (code, token_info) where token_info contains usage stats.
|
191
|
+
"""
|
192
|
+
# Simplify the task description if it's too complex
|
193
|
+
simplified_task = self._simplify_task(task)
|
194
|
+
|
195
|
+
# Use the existing CFG generation
|
196
|
+
return generate_with_openai(
|
197
|
+
self.client,
|
198
|
+
self.model,
|
199
|
+
simplified_task,
|
200
|
+
max_tokens=800,
|
201
|
+
temperature=0.7, # Will be ignored for GPT-5
|
202
|
+
)
|
203
|
+
|
204
|
+
def _simplify_task(self, task: str) -> str:
|
205
|
+
"""Simplify complex task descriptions for better generation.
|
206
|
+
|
207
|
+
Args:
|
208
|
+
task: Original task description.
|
209
|
+
|
210
|
+
Returns:
|
211
|
+
Simplified task description.
|
212
|
+
"""
|
213
|
+
# Remove overly complex instructions and focus on core functionality
|
214
|
+
simplified = task
|
215
|
+
|
216
|
+
# If task is very long, try to extract the essential parts
|
217
|
+
if len(task) > 500:
|
218
|
+
# Look for key phrases that indicate the main task
|
219
|
+
lines = task.split("\n")
|
220
|
+
essential_lines = []
|
221
|
+
|
222
|
+
for line in lines:
|
223
|
+
# Keep lines that describe the main task or errors
|
224
|
+
if any(
|
225
|
+
keyword in line.lower()
|
226
|
+
for keyword in [
|
227
|
+
"generate",
|
228
|
+
"create",
|
229
|
+
"calculate",
|
230
|
+
"implement",
|
231
|
+
"write",
|
232
|
+
"error:",
|
233
|
+
"failed:",
|
234
|
+
"fix",
|
235
|
+
"please",
|
236
|
+
]
|
237
|
+
):
|
238
|
+
essential_lines.append(line)
|
239
|
+
|
240
|
+
if essential_lines:
|
241
|
+
simplified = "\n".join(essential_lines[:10]) # Limit to 10 most relevant lines
|
242
|
+
|
243
|
+
# Add clarification about Machine Dialect™ syntax
|
244
|
+
if "error" not in simplified.lower():
|
245
|
+
simplified += (
|
246
|
+
"\nNote: Use Machine Dialect™ syntax with backticks for variables and underscores for literals."
|
247
|
+
)
|
248
|
+
|
249
|
+
return simplified
|
250
|
+
|
251
|
+
def _execute(self, code: str) -> dict[str, Any]:
|
252
|
+
"""Compile and execute Machine Dialect™ code.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
code: The code to execute.
|
256
|
+
|
257
|
+
Returns:
|
258
|
+
Execution result dictionary.
|
259
|
+
"""
|
260
|
+
temp_path = None
|
261
|
+
|
262
|
+
try:
|
263
|
+
# Compile without optimizations
|
264
|
+
config = CompilerConfig(optimization_level=OptimizationLevel.NONE, verbose=False)
|
265
|
+
compiler = Compiler(config)
|
266
|
+
|
267
|
+
if self.verbose:
|
268
|
+
print(" Compiling...")
|
269
|
+
|
270
|
+
context = compiler.compile_string(code, module_name="agent_task")
|
271
|
+
|
272
|
+
# Check compilation errors
|
273
|
+
if context.has_errors():
|
274
|
+
error_msg = "Compilation failed"
|
275
|
+
if context.errors:
|
276
|
+
error = context.errors[0]
|
277
|
+
# Convert error object to string
|
278
|
+
error_msg = str(error)
|
279
|
+
return {"success": False, "phase": "compilation", "error": error_msg}
|
280
|
+
|
281
|
+
if self.verbose and context.bytecode_module:
|
282
|
+
bytecode_size = len(context.bytecode_module.serialize())
|
283
|
+
print(f" ✓ Compiled successfully ({bytecode_size} bytes)")
|
284
|
+
|
285
|
+
# Save bytecode to temporary file
|
286
|
+
if context.bytecode_module:
|
287
|
+
with tempfile.NamedTemporaryFile(suffix=".mdbc", delete=False) as f:
|
288
|
+
bytecode = context.bytecode_module.serialize()
|
289
|
+
f.write(bytecode)
|
290
|
+
temp_path = f.name
|
291
|
+
else:
|
292
|
+
raise ValueError("Compilation succeeded but no bytecode generated")
|
293
|
+
|
294
|
+
# Execute with Rust VM
|
295
|
+
if self.verbose:
|
296
|
+
print(" Executing bytecode...")
|
297
|
+
|
298
|
+
import machine_dialect_vm
|
299
|
+
|
300
|
+
vm = machine_dialect_vm.RustVM()
|
301
|
+
vm.load_bytecode(temp_path)
|
302
|
+
output = vm.execute()
|
303
|
+
|
304
|
+
# Get instruction count
|
305
|
+
instructions = vm.instruction_count()
|
306
|
+
|
307
|
+
return {
|
308
|
+
"success": True,
|
309
|
+
"phase": "runtime",
|
310
|
+
"output": str(output) if output is not None else "",
|
311
|
+
"instructions": instructions,
|
312
|
+
"bytecode_size": len(bytecode),
|
313
|
+
}
|
314
|
+
|
315
|
+
except ImportError:
|
316
|
+
return {"success": False, "phase": "runtime", "error": "Rust VM not available. Run ./build_vm.sh first."}
|
317
|
+
except Exception as e:
|
318
|
+
return {"success": False, "phase": "runtime", "error": str(e)}
|
319
|
+
finally:
|
320
|
+
# Clean up temp file
|
321
|
+
if temp_path:
|
322
|
+
Path(temp_path).unlink(missing_ok=True)
|
323
|
+
|
324
|
+
def _build_error_feedback(self, original_task: str, code: str, error: str, phase: str) -> str:
|
325
|
+
"""Build task description with error feedback.
|
326
|
+
|
327
|
+
Args:
|
328
|
+
original_task: Original task description.
|
329
|
+
code: Code that failed.
|
330
|
+
error: Error message.
|
331
|
+
phase: Phase where error occurred.
|
332
|
+
|
333
|
+
Returns:
|
334
|
+
Enhanced task description for retry.
|
335
|
+
"""
|
336
|
+
feedback = f"{original_task}\n\n"
|
337
|
+
feedback += f"Previous attempt failed during {phase}:\n"
|
338
|
+
feedback += f"Code:\n```\n{code}\n```\n\n"
|
339
|
+
feedback += f"Error: {error}\n\n"
|
340
|
+
feedback += "Please fix this error and provide a working solution."
|
341
|
+
|
342
|
+
return feedback
|
343
|
+
|
344
|
+
def _should_optimize(self, task: str, code: str, result: dict[str, Any]) -> bool:
|
345
|
+
"""Decide if we should try to optimize working code.
|
346
|
+
|
347
|
+
Args:
|
348
|
+
task: Original task.
|
349
|
+
code: Working code.
|
350
|
+
result: Execution result.
|
351
|
+
|
352
|
+
Returns:
|
353
|
+
Whether to attempt optimization.
|
354
|
+
"""
|
355
|
+
# Simple heuristic: don't optimize if it's already very small
|
356
|
+
if result.get("instructions", 0) < 50:
|
357
|
+
return False
|
358
|
+
|
359
|
+
# Could add more sophisticated logic here
|
360
|
+
return False # For now, stop on first success
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# isort: skip_file
|
2
|
+
from .ast_node import ASTNode
|
3
|
+
from .expressions import (
|
4
|
+
Arguments,
|
5
|
+
CollectionAccessExpression,
|
6
|
+
ConditionalExpression,
|
7
|
+
ErrorExpression,
|
8
|
+
Expression,
|
9
|
+
Identifier,
|
10
|
+
InfixExpression,
|
11
|
+
PrefixExpression,
|
12
|
+
)
|
13
|
+
from .dict_extraction import DictExtraction
|
14
|
+
from .statements import (
|
15
|
+
ActionStatement,
|
16
|
+
BlockStatement,
|
17
|
+
CallStatement,
|
18
|
+
CollectionMutationStatement,
|
19
|
+
DefineStatement,
|
20
|
+
ErrorStatement,
|
21
|
+
ExpressionStatement,
|
22
|
+
ForEachStatement,
|
23
|
+
FunctionStatement,
|
24
|
+
FunctionVisibility,
|
25
|
+
IfStatement,
|
26
|
+
InteractionStatement,
|
27
|
+
Output,
|
28
|
+
Parameter,
|
29
|
+
ReturnStatement,
|
30
|
+
SayStatement,
|
31
|
+
SetStatement,
|
32
|
+
Statement,
|
33
|
+
UtilityStatement,
|
34
|
+
WhileStatement,
|
35
|
+
)
|
36
|
+
from .program import Program
|
37
|
+
from .literals import (
|
38
|
+
WholeNumberLiteral,
|
39
|
+
FloatLiteral,
|
40
|
+
StringLiteral,
|
41
|
+
YesNoLiteral,
|
42
|
+
EmptyLiteral,
|
43
|
+
URLLiteral,
|
44
|
+
UnorderedListLiteral,
|
45
|
+
OrderedListLiteral,
|
46
|
+
NamedListLiteral,
|
47
|
+
BlankLiteral,
|
48
|
+
)
|
49
|
+
from .call_expression import CallExpression
|
50
|
+
|
51
|
+
|
52
|
+
__all__ = [
|
53
|
+
"ASTNode",
|
54
|
+
"ActionStatement",
|
55
|
+
"Arguments",
|
56
|
+
"BlankLiteral",
|
57
|
+
"BlockStatement",
|
58
|
+
"CallExpression",
|
59
|
+
"CallStatement",
|
60
|
+
"CollectionAccessExpression",
|
61
|
+
"CollectionMutationStatement",
|
62
|
+
"ConditionalExpression",
|
63
|
+
"DefineStatement",
|
64
|
+
"DictExtraction",
|
65
|
+
"EmptyLiteral",
|
66
|
+
"ErrorExpression",
|
67
|
+
"ErrorStatement",
|
68
|
+
"Expression",
|
69
|
+
"ExpressionStatement",
|
70
|
+
"FloatLiteral",
|
71
|
+
"ForEachStatement",
|
72
|
+
"FunctionStatement",
|
73
|
+
"FunctionVisibility",
|
74
|
+
"Identifier",
|
75
|
+
"IfStatement",
|
76
|
+
"InfixExpression",
|
77
|
+
"InteractionStatement",
|
78
|
+
"NamedListLiteral",
|
79
|
+
"OrderedListLiteral",
|
80
|
+
"Output",
|
81
|
+
"Parameter",
|
82
|
+
"PrefixExpression",
|
83
|
+
"Program",
|
84
|
+
"ReturnStatement",
|
85
|
+
"SayStatement",
|
86
|
+
"SetStatement",
|
87
|
+
"Statement",
|
88
|
+
"StringLiteral",
|
89
|
+
"URLLiteral",
|
90
|
+
"UnorderedListLiteral",
|
91
|
+
"UtilityStatement",
|
92
|
+
"WhileStatement",
|
93
|
+
"WholeNumberLiteral",
|
94
|
+
"YesNoLiteral",
|
95
|
+
]
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import TYPE_CHECKING
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from machine_dialect.lexer.tokens import Token
|
6
|
+
|
7
|
+
|
8
|
+
class ASTNode(ABC):
|
9
|
+
@abstractmethod
|
10
|
+
def __str__(self) -> str:
|
11
|
+
pass
|
12
|
+
|
13
|
+
def desugar(self) -> "ASTNode":
|
14
|
+
"""Simplify AST node for IR generation and optimization.
|
15
|
+
|
16
|
+
This method transforms the AST to remove syntactic sugar and normalize
|
17
|
+
semantically equivalent constructs. The default implementation returns
|
18
|
+
the node unchanged.
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
A simplified version of this node or self if no simplification needed.
|
22
|
+
"""
|
23
|
+
return self
|
24
|
+
|
25
|
+
def get_source_location(self) -> tuple[int, int] | None:
|
26
|
+
"""Get the source location (line, column) of this AST node.
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
A tuple of (line, column) if location info is available, None otherwise.
|
30
|
+
"""
|
31
|
+
# Default implementation - subclasses with tokens can override
|
32
|
+
if hasattr(self, "token"):
|
33
|
+
token: Token = self.token
|
34
|
+
return (token.line, token.position)
|
35
|
+
return None
|
@@ -0,0 +1,82 @@
|
|
1
|
+
"""CallExpression AST node for function calls that return values."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from machine_dialect.ast.expressions import Expression
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from machine_dialect.lexer import Token
|
11
|
+
|
12
|
+
|
13
|
+
class CallExpression(Expression):
|
14
|
+
"""A function call expression that returns a value.
|
15
|
+
|
16
|
+
CallExpression represents a function invocation that produces a value,
|
17
|
+
used in contexts where an expression is expected (e.g., in assignments
|
18
|
+
with 'using', or as part of larger expressions).
|
19
|
+
|
20
|
+
This is distinct from CallStatement, which represents standalone function
|
21
|
+
calls that don't necessarily return values.
|
22
|
+
|
23
|
+
Attributes:
|
24
|
+
function_name: The identifier or expression that names the function.
|
25
|
+
arguments: Optional Arguments node containing the function arguments.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(
|
29
|
+
self, token: Token, function_name: Expression | None = None, arguments: Expression | None = None
|
30
|
+
) -> None:
|
31
|
+
"""Initialize a CallExpression node.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
token: The token that begins the call (typically the function name).
|
35
|
+
function_name: The expression identifying the function to call.
|
36
|
+
arguments: Optional Arguments node containing the function arguments.
|
37
|
+
"""
|
38
|
+
super().__init__(token)
|
39
|
+
self.function_name = function_name
|
40
|
+
self.arguments = arguments
|
41
|
+
|
42
|
+
def token_literal(self) -> str:
|
43
|
+
"""Return the literal value of the call token.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
The literal value of the token.
|
47
|
+
"""
|
48
|
+
return self.token.literal
|
49
|
+
|
50
|
+
def __str__(self) -> str:
|
51
|
+
"""Return string representation of the call expression.
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
A string showing the function call with its arguments.
|
55
|
+
"""
|
56
|
+
parts = []
|
57
|
+
|
58
|
+
if self.function_name:
|
59
|
+
parts.append(str(self.function_name))
|
60
|
+
|
61
|
+
if self.arguments:
|
62
|
+
parts.append(f"({self.arguments})")
|
63
|
+
else:
|
64
|
+
parts.append("()")
|
65
|
+
|
66
|
+
return "".join(parts)
|
67
|
+
|
68
|
+
def desugar(self) -> CallExpression:
|
69
|
+
"""Desugar the call expression.
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
A new CallExpression with desugared components.
|
73
|
+
"""
|
74
|
+
desugared = CallExpression(self.token)
|
75
|
+
|
76
|
+
if self.function_name:
|
77
|
+
desugared.function_name = self.function_name.desugar()
|
78
|
+
|
79
|
+
if self.arguments:
|
80
|
+
desugared.arguments = self.arguments.desugar()
|
81
|
+
|
82
|
+
return desugared
|
@@ -0,0 +1,60 @@
|
|
1
|
+
"""Dictionary extraction expressions for Machine Dialect™."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING
|
6
|
+
|
7
|
+
from machine_dialect.ast.expressions import Expression
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from machine_dialect.lexer.tokens import Token
|
11
|
+
|
12
|
+
|
13
|
+
class DictExtraction(Expression):
|
14
|
+
"""Extract keys or values from a dictionary.
|
15
|
+
|
16
|
+
Represents expressions like:
|
17
|
+
- the names of `person` (extracts keys)
|
18
|
+
- the contents of `person` (extracts values)
|
19
|
+
|
20
|
+
Attributes:
|
21
|
+
dictionary: The dictionary expression to extract from.
|
22
|
+
extract_type: What to extract ('names' for keys, 'contents' for values).
|
23
|
+
token: The token that begins this expression.
|
24
|
+
"""
|
25
|
+
|
26
|
+
def __init__(self, token: Token, dictionary: Expression, extract_type: str) -> None:
|
27
|
+
"""Initialize dictionary extraction expression.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
token: The token that begins this expression.
|
31
|
+
dictionary: The dictionary to extract from.
|
32
|
+
extract_type: 'names' or 'contents'.
|
33
|
+
"""
|
34
|
+
super().__init__(token)
|
35
|
+
self.token = token
|
36
|
+
self.dictionary = dictionary
|
37
|
+
self.extract_type = extract_type
|
38
|
+
|
39
|
+
def __str__(self) -> str:
|
40
|
+
"""Return string representation."""
|
41
|
+
if self.extract_type == "names":
|
42
|
+
return f"the names of {self.dictionary}"
|
43
|
+
else:
|
44
|
+
return f"the contents of {self.dictionary}"
|
45
|
+
|
46
|
+
def desugar(self) -> DictExtraction:
|
47
|
+
"""Desugar by recursively desugaring the dictionary."""
|
48
|
+
return DictExtraction(
|
49
|
+
self.token,
|
50
|
+
self.dictionary.desugar() if hasattr(self.dictionary, "desugar") else self.dictionary,
|
51
|
+
self.extract_type,
|
52
|
+
)
|
53
|
+
|
54
|
+
def to_hir(self) -> DictExtraction:
|
55
|
+
"""Convert to HIR representation."""
|
56
|
+
return DictExtraction(
|
57
|
+
self.token,
|
58
|
+
self.dictionary.to_hir() if hasattr(self.dictionary, "to_hir") else self.dictionary,
|
59
|
+
self.extract_type,
|
60
|
+
)
|