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,426 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""Machine Dialect™ REPL (Read-Eval-Print Loop).
|
3
|
+
|
4
|
+
This module provides an interactive REPL for the Machine Dialect™ language.
|
5
|
+
It can operate in multiple modes:
|
6
|
+
- Default: Execute code using the Rust VM
|
7
|
+
- Debug tokens (--debug-tokens): Tokenizes input and displays tokens
|
8
|
+
- AST mode (--ast): Show HIR/AST without executing
|
9
|
+
"""
|
10
|
+
|
11
|
+
import argparse
|
12
|
+
import sys
|
13
|
+
from typing import Any
|
14
|
+
|
15
|
+
# readline provides command history and line editing, but is not available on Windows
|
16
|
+
if sys.platform != "win32":
|
17
|
+
import readline # noqa: F401
|
18
|
+
|
19
|
+
from machine_dialect.compiler.config import CompilerConfig
|
20
|
+
from machine_dialect.compiler.context import CompilationContext
|
21
|
+
from machine_dialect.compiler.phases.hir_generation import HIRGenerationPhase
|
22
|
+
from machine_dialect.lexer.lexer import Lexer
|
23
|
+
from machine_dialect.lexer.tokens import Token
|
24
|
+
from machine_dialect.parser.parser import Parser
|
25
|
+
|
26
|
+
|
27
|
+
class REPL:
|
28
|
+
"""Interactive REPL for Machine Dialect™.
|
29
|
+
|
30
|
+
Provides an interactive environment for testing Machine Dialect™ syntax
|
31
|
+
by parsing input and displaying the AST or tokens.
|
32
|
+
|
33
|
+
Attributes:
|
34
|
+
prompt: The prompt string displayed to the user.
|
35
|
+
running: Flag indicating whether the REPL is running.
|
36
|
+
debug_tokens: Whether to show tokens instead of AST.
|
37
|
+
show_ast: Whether to show AST instead of evaluating.
|
38
|
+
accumulated_source: Accumulated source code for parsing.
|
39
|
+
multiline_buffer: Buffer for collecting multi-line input.
|
40
|
+
in_multiline_mode: Whether currently collecting multi-line input.
|
41
|
+
hir_phase: HIR generation phase for desugaring AST nodes.
|
42
|
+
vm_runner: Optional VM runner for code execution.
|
43
|
+
"""
|
44
|
+
|
45
|
+
def __init__(self, debug_tokens: bool = False, show_ast: bool = False) -> None:
|
46
|
+
"""Initialize the REPL with default settings.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
debug_tokens: Whether to show tokens instead of evaluating.
|
50
|
+
show_ast: Whether to show AST instead of evaluating.
|
51
|
+
"""
|
52
|
+
self.prompt = "md> "
|
53
|
+
self.running = True
|
54
|
+
self.debug_tokens = debug_tokens
|
55
|
+
self.show_ast = show_ast
|
56
|
+
self.accumulated_source = ""
|
57
|
+
self.multiline_buffer = ""
|
58
|
+
self.in_multiline_mode = False
|
59
|
+
self.hir_phase = HIRGenerationPhase() # HIR generation phase for desugaring
|
60
|
+
self.vm_runner: Any = None
|
61
|
+
self._init_vm_runner()
|
62
|
+
|
63
|
+
def _init_vm_runner(self) -> None:
|
64
|
+
"""Initialize the VM runner if not in token/AST debug modes."""
|
65
|
+
if not self.debug_tokens and not self.show_ast:
|
66
|
+
try:
|
67
|
+
from machine_dialect.compiler.vm_runner import VMRunner
|
68
|
+
|
69
|
+
self.vm_runner = VMRunner(debug=False, optimize=True)
|
70
|
+
except (ImportError, RuntimeError) as e:
|
71
|
+
print(f"Warning: Rust VM not available: {e}")
|
72
|
+
print("Falling back to AST display mode.")
|
73
|
+
self.show_ast = True
|
74
|
+
|
75
|
+
def print_welcome(self) -> None:
|
76
|
+
"""Print the welcome message when REPL starts."""
|
77
|
+
print("Machine Dialect™ REPL v0.1.0")
|
78
|
+
if self.debug_tokens:
|
79
|
+
mode = "Token Debug Mode"
|
80
|
+
elif self.show_ast:
|
81
|
+
mode = "HIR/AST Display Mode"
|
82
|
+
elif self.vm_runner:
|
83
|
+
mode = "Rust VM Execution Mode"
|
84
|
+
else:
|
85
|
+
mode = "HIR Mode (desugared AST)"
|
86
|
+
print(f"Mode: {mode}")
|
87
|
+
print("Type 'exit' to exit, 'help' for help")
|
88
|
+
print("-" * 50)
|
89
|
+
|
90
|
+
def print_help(self) -> None:
|
91
|
+
"""Print help information about available commands."""
|
92
|
+
print("\nAvailable commands:")
|
93
|
+
print(" exit - Exit the REPL")
|
94
|
+
print(" help - Show this help message")
|
95
|
+
print(" clear - Clear the screen")
|
96
|
+
if not self.debug_tokens:
|
97
|
+
print(" reset - Clear accumulated source")
|
98
|
+
|
99
|
+
if self.debug_tokens:
|
100
|
+
print("\nEnter any text to see its tokens.")
|
101
|
+
elif self.show_ast:
|
102
|
+
print("\nEnter Machine Dialect™ code to see its HIR (desugared AST).")
|
103
|
+
print("Source is accumulated across lines until an error occurs.")
|
104
|
+
elif self.vm_runner:
|
105
|
+
print("\nEnter Machine Dialect™ code to execute it on the Rust VM.")
|
106
|
+
print("Source is accumulated across lines until an error occurs.")
|
107
|
+
else:
|
108
|
+
print("\nEnter Machine Dialect™ code to see its HIR (desugared AST).")
|
109
|
+
print("Source is accumulated across lines until an error occurs.")
|
110
|
+
|
111
|
+
print("\nMulti-line input:")
|
112
|
+
print(" Lines ending with ':' enter multi-line mode")
|
113
|
+
print(" Lines starting with '>' continue multi-line input")
|
114
|
+
print(" Empty line or line not matching above completes input")
|
115
|
+
print(" Ctrl+C cancels multi-line input")
|
116
|
+
|
117
|
+
print("\nExample: Set `x` to _10_.")
|
118
|
+
print("\nMulti-line example:")
|
119
|
+
print(" md> If _5_ > _3_ then:")
|
120
|
+
print(" ... > _42_.")
|
121
|
+
print(" ... ")
|
122
|
+
print()
|
123
|
+
|
124
|
+
def clear_screen(self) -> None:
|
125
|
+
"""Clear the terminal screen."""
|
126
|
+
import os
|
127
|
+
|
128
|
+
os.system("cls" if os.name == "nt" else "clear")
|
129
|
+
|
130
|
+
def format_token(self, token: Token) -> str:
|
131
|
+
"""Format a token for display.
|
132
|
+
|
133
|
+
Args:
|
134
|
+
token: The token to format.
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
A formatted string representation of the token.
|
138
|
+
"""
|
139
|
+
return f" {token.type.name:<20} | {token.literal!r}"
|
140
|
+
|
141
|
+
def should_continue_multiline(self, line: str) -> bool:
|
142
|
+
"""Check if we should continue collecting multi-line input.
|
143
|
+
|
144
|
+
Args:
|
145
|
+
line: The current input line.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
True if we should continue collecting input, False otherwise.
|
149
|
+
"""
|
150
|
+
stripped = line.strip()
|
151
|
+
# Continue if line ends with colon or starts with '>'
|
152
|
+
return stripped.endswith(":") or stripped.startswith(">")
|
153
|
+
|
154
|
+
def get_multiline_prompt(self) -> str:
|
155
|
+
"""Get the appropriate prompt for multi-line input.
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
The prompt string to use.
|
159
|
+
"""
|
160
|
+
# Count the depth of '>' markers in the buffer
|
161
|
+
depth = 0
|
162
|
+
for line in self.multiline_buffer.split("\n"):
|
163
|
+
stripped = line.strip()
|
164
|
+
if stripped.startswith(">"):
|
165
|
+
# Count consecutive '>' at the start
|
166
|
+
for char in stripped:
|
167
|
+
if char == ">":
|
168
|
+
depth += 1
|
169
|
+
else:
|
170
|
+
break
|
171
|
+
break
|
172
|
+
|
173
|
+
if depth > 0:
|
174
|
+
return "... "
|
175
|
+
else:
|
176
|
+
return "... "
|
177
|
+
|
178
|
+
def tokenize_and_print(self, input_text: str) -> None:
|
179
|
+
"""Tokenize the input and print the results.
|
180
|
+
|
181
|
+
Args:
|
182
|
+
input_text: The Machine Dialect™ code to tokenize.
|
183
|
+
|
184
|
+
Note:
|
185
|
+
This method handles both successful tokenization and error cases,
|
186
|
+
displaying any lexical errors before showing the tokens.
|
187
|
+
"""
|
188
|
+
try:
|
189
|
+
from machine_dialect.lexer.tokens import TokenType
|
190
|
+
|
191
|
+
lexer = Lexer(input_text)
|
192
|
+
|
193
|
+
# Stream tokens
|
194
|
+
tokens = []
|
195
|
+
while True:
|
196
|
+
token = lexer.next_token()
|
197
|
+
tokens.append(token)
|
198
|
+
if token.type == TokenType.MISC_EOF:
|
199
|
+
break
|
200
|
+
|
201
|
+
print(f"\nTokens ({len(tokens)}):")
|
202
|
+
print("-" * 50)
|
203
|
+
print(f" {'Type':<20} | Literal")
|
204
|
+
print("-" * 50)
|
205
|
+
|
206
|
+
for token in tokens:
|
207
|
+
print(self.format_token(token))
|
208
|
+
|
209
|
+
print("-" * 50)
|
210
|
+
print()
|
211
|
+
|
212
|
+
except Exception as e:
|
213
|
+
print(f"Error: {e}")
|
214
|
+
print()
|
215
|
+
|
216
|
+
def parse_and_print(self, input_text: str) -> None:
|
217
|
+
"""Parse the input and print the AST or evaluation result.
|
218
|
+
|
219
|
+
Args:
|
220
|
+
input_text: The Machine Dialect™ code to parse.
|
221
|
+
|
222
|
+
Note:
|
223
|
+
This method accumulates source code and attempts to parse it.
|
224
|
+
If parsing fails, it shows the error and removes the problematic line.
|
225
|
+
"""
|
226
|
+
# Add new input to accumulated source
|
227
|
+
if self.accumulated_source:
|
228
|
+
# Add a newline separator if we have existing content
|
229
|
+
test_source = self.accumulated_source + "\n" + input_text
|
230
|
+
else:
|
231
|
+
test_source = input_text
|
232
|
+
|
233
|
+
# Create parser and parse
|
234
|
+
parser = Parser()
|
235
|
+
ast = parser.parse(test_source)
|
236
|
+
|
237
|
+
# Check for errors
|
238
|
+
if parser.has_errors():
|
239
|
+
# Show parser errors but don't update accumulated source
|
240
|
+
print("\nErrors found:")
|
241
|
+
print("-" * 50)
|
242
|
+
for error in parser.errors:
|
243
|
+
print(f" {error}")
|
244
|
+
print("-" * 50)
|
245
|
+
print("(Input not added to accumulated source)")
|
246
|
+
print()
|
247
|
+
else:
|
248
|
+
# If successful, update accumulated source
|
249
|
+
self.accumulated_source = test_source
|
250
|
+
|
251
|
+
# Generate HIR by desugaring the AST
|
252
|
+
# Create a minimal compilation context for HIR generation
|
253
|
+
from pathlib import Path
|
254
|
+
|
255
|
+
from machine_dialect.ast.program import Program
|
256
|
+
|
257
|
+
config = CompilerConfig(verbose=False)
|
258
|
+
context = CompilationContext(source_path=Path("<repl>"), source_content=test_source, config=config)
|
259
|
+
hir = self.hir_phase.run(context, ast)
|
260
|
+
|
261
|
+
# Execute or show AST based on mode
|
262
|
+
if self.vm_runner:
|
263
|
+
# Execute using Rust VM
|
264
|
+
try:
|
265
|
+
result = self.vm_runner.execute(self.accumulated_source)
|
266
|
+
print("\nExecution Result:")
|
267
|
+
print("-" * 50)
|
268
|
+
if result is not None:
|
269
|
+
print(f" {result}")
|
270
|
+
else:
|
271
|
+
print(" (no return value)")
|
272
|
+
print("-" * 50)
|
273
|
+
print()
|
274
|
+
except Exception as e:
|
275
|
+
print(f"\nExecution Error: {e}")
|
276
|
+
print()
|
277
|
+
else:
|
278
|
+
# Show HIR/AST
|
279
|
+
print("\nHIR (desugared AST):")
|
280
|
+
print("-" * 50)
|
281
|
+
if isinstance(hir, Program) and hir.statements:
|
282
|
+
for node in hir.statements:
|
283
|
+
print(f" {node}")
|
284
|
+
else:
|
285
|
+
print(" (empty)")
|
286
|
+
print("-" * 50)
|
287
|
+
print()
|
288
|
+
|
289
|
+
def run(self) -> int:
|
290
|
+
"""Run the main REPL loop.
|
291
|
+
|
292
|
+
Handles user input, command processing, and multi-line input collection.
|
293
|
+
Continues until the user exits or an unhandled error occurs.
|
294
|
+
|
295
|
+
Returns:
|
296
|
+
Exit code (0 for normal exit, 1 for error exit).
|
297
|
+
"""
|
298
|
+
self.print_welcome()
|
299
|
+
|
300
|
+
while self.running:
|
301
|
+
try:
|
302
|
+
# Determine prompt based on multiline mode
|
303
|
+
if self.in_multiline_mode:
|
304
|
+
prompt = self.get_multiline_prompt()
|
305
|
+
else:
|
306
|
+
prompt = self.prompt
|
307
|
+
|
308
|
+
# Get input
|
309
|
+
user_input = input(prompt)
|
310
|
+
|
311
|
+
# In multiline mode, handle special cases
|
312
|
+
if self.in_multiline_mode:
|
313
|
+
# Check if we should continue multiline
|
314
|
+
if self.should_continue_multiline(user_input):
|
315
|
+
# Add to buffer with newline
|
316
|
+
if self.multiline_buffer:
|
317
|
+
self.multiline_buffer += "\n" + user_input
|
318
|
+
else:
|
319
|
+
self.multiline_buffer = user_input
|
320
|
+
continue
|
321
|
+
else:
|
322
|
+
# End multiline mode - process the complete buffer
|
323
|
+
if self.multiline_buffer:
|
324
|
+
complete_input = self.multiline_buffer + "\n" + user_input
|
325
|
+
else:
|
326
|
+
complete_input = user_input
|
327
|
+
|
328
|
+
# Reset multiline mode
|
329
|
+
self.multiline_buffer = ""
|
330
|
+
self.in_multiline_mode = False
|
331
|
+
|
332
|
+
# Process the complete input
|
333
|
+
user_input = complete_input
|
334
|
+
else:
|
335
|
+
# Check for commands (only in normal mode)
|
336
|
+
if user_input.strip().lower() == "exit":
|
337
|
+
print("Goodbye!")
|
338
|
+
self.running = False
|
339
|
+
return 0 # Normal exit
|
340
|
+
elif user_input.strip().lower() == "help":
|
341
|
+
self.print_help()
|
342
|
+
continue
|
343
|
+
elif user_input.strip().lower() == "clear":
|
344
|
+
self.clear_screen()
|
345
|
+
self.print_welcome()
|
346
|
+
# Also clear accumulated source
|
347
|
+
if not self.debug_tokens:
|
348
|
+
self.accumulated_source = ""
|
349
|
+
continue
|
350
|
+
elif user_input.strip().lower() == "reset" and not self.debug_tokens:
|
351
|
+
# Reset accumulated source in AST mode
|
352
|
+
self.accumulated_source = ""
|
353
|
+
print("Accumulated source cleared.")
|
354
|
+
continue
|
355
|
+
|
356
|
+
# Check if we should enter multiline mode
|
357
|
+
if not self.debug_tokens and self.should_continue_multiline(user_input):
|
358
|
+
self.in_multiline_mode = True
|
359
|
+
self.multiline_buffer = user_input
|
360
|
+
continue
|
361
|
+
|
362
|
+
# Process non-empty input
|
363
|
+
if user_input.strip():
|
364
|
+
# Auto-append period if missing (for non-token mode)
|
365
|
+
if not self.debug_tokens and not user_input.strip().endswith("."):
|
366
|
+
user_input = user_input + "."
|
367
|
+
|
368
|
+
# Process input based on mode
|
369
|
+
if self.debug_tokens:
|
370
|
+
self.tokenize_and_print(user_input)
|
371
|
+
else:
|
372
|
+
self.parse_and_print(user_input)
|
373
|
+
|
374
|
+
except (KeyboardInterrupt, EOFError):
|
375
|
+
# Handle Ctrl+C and Ctrl+D
|
376
|
+
if self.in_multiline_mode:
|
377
|
+
# Cancel multiline mode
|
378
|
+
print("\nMultiline input cancelled.")
|
379
|
+
self.multiline_buffer = ""
|
380
|
+
self.in_multiline_mode = False
|
381
|
+
else:
|
382
|
+
print("\nGoodbye!")
|
383
|
+
self.running = False
|
384
|
+
return 0 # Normal exit via Ctrl+D
|
385
|
+
except Exception as e:
|
386
|
+
print(f"Unexpected error: {e}")
|
387
|
+
# Reset multiline mode on error
|
388
|
+
if self.in_multiline_mode:
|
389
|
+
self.multiline_buffer = ""
|
390
|
+
self.in_multiline_mode = False
|
391
|
+
return 1 # Error exit
|
392
|
+
|
393
|
+
return 0 # Default normal exit
|
394
|
+
|
395
|
+
|
396
|
+
def main() -> None:
|
397
|
+
"""Entry point for the Machine Dialect™ REPL.
|
398
|
+
|
399
|
+
Parses command line arguments and starts the appropriate REPL mode.
|
400
|
+
Supports token debug mode and AST display mode via command line flags.
|
401
|
+
"""
|
402
|
+
parser = argparse.ArgumentParser(description="Machine Dialect™ REPL")
|
403
|
+
parser.add_argument(
|
404
|
+
"--debug-tokens",
|
405
|
+
action="store_true",
|
406
|
+
help="Run in token debug mode (show tokens)",
|
407
|
+
)
|
408
|
+
parser.add_argument(
|
409
|
+
"--ast",
|
410
|
+
action="store_true",
|
411
|
+
help="Run in AST mode (show AST instead of evaluating)",
|
412
|
+
)
|
413
|
+
args = parser.parse_args()
|
414
|
+
|
415
|
+
# Check for incompatible flags
|
416
|
+
if args.debug_tokens and args.ast:
|
417
|
+
print("Error: --debug-tokens and --ast flags are not compatible")
|
418
|
+
sys.exit(1)
|
419
|
+
|
420
|
+
repl = REPL(debug_tokens=args.debug_tokens, show_ast=args.ast)
|
421
|
+
exit_code = repl.run()
|
422
|
+
sys.exit(exit_code)
|
423
|
+
|
424
|
+
|
425
|
+
if __name__ == "__main__":
|
426
|
+
main()
|
File without changes
|