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,285 @@
|
|
1
|
+
"""Tests for debug information tracking."""
|
2
|
+
|
3
|
+
from machine_dialect.ast import (
|
4
|
+
BlockStatement,
|
5
|
+
Expression,
|
6
|
+
FunctionStatement,
|
7
|
+
FunctionVisibility,
|
8
|
+
Identifier,
|
9
|
+
InfixExpression,
|
10
|
+
Parameter,
|
11
|
+
Program,
|
12
|
+
ReturnStatement,
|
13
|
+
SetStatement,
|
14
|
+
StringLiteral,
|
15
|
+
WholeNumberLiteral,
|
16
|
+
)
|
17
|
+
from machine_dialect.lexer.tokens import Token, TokenType
|
18
|
+
from machine_dialect.mir.debug_info import (
|
19
|
+
DebugInfo,
|
20
|
+
DebugInfoBuilder,
|
21
|
+
DebugVariable,
|
22
|
+
SourceLocation,
|
23
|
+
)
|
24
|
+
from machine_dialect.mir.hir_to_mir import HIRToMIRLowering
|
25
|
+
from machine_dialect.mir.mir_instructions import BinaryOp, LoadConst
|
26
|
+
from machine_dialect.mir.mir_types import MIRType
|
27
|
+
from machine_dialect.mir.mir_values import Constant, Variable
|
28
|
+
|
29
|
+
|
30
|
+
class TestDebugInfoTracking:
|
31
|
+
"""Test debug information tracking during compilation."""
|
32
|
+
|
33
|
+
def _create_infix(self, left: Expression, op: str, right: Expression) -> InfixExpression:
|
34
|
+
"""Helper to create InfixExpression properly."""
|
35
|
+
token = Token(TokenType.OP_PLUS if op == "+" else TokenType.OP_STAR, op, 0, 0)
|
36
|
+
expr = InfixExpression(token, op, left)
|
37
|
+
expr.right = right
|
38
|
+
return expr
|
39
|
+
|
40
|
+
def _token(self, token_type: TokenType, value: str = "") -> Token:
|
41
|
+
"""Create a token for testing."""
|
42
|
+
return Token(token_type, value, 0, 0)
|
43
|
+
|
44
|
+
def test_source_location_tracking(self) -> None:
|
45
|
+
"""Test tracking source locations for instructions."""
|
46
|
+
debug_info = DebugInfo()
|
47
|
+
debug_info.current_file = "test.md"
|
48
|
+
|
49
|
+
# Create a dummy instruction
|
50
|
+
inst = LoadConst(Variable("temp", MIRType.INT, 0), Constant(42), (1, 1))
|
51
|
+
location = SourceLocation("test.md", 10, 5)
|
52
|
+
|
53
|
+
# Track the location
|
54
|
+
debug_info.set_instruction_location(inst, location)
|
55
|
+
|
56
|
+
# Retrieve the location
|
57
|
+
retrieved = debug_info.get_instruction_location(inst)
|
58
|
+
assert retrieved is not None
|
59
|
+
assert retrieved.file == "test.md"
|
60
|
+
assert retrieved.line == 10
|
61
|
+
assert retrieved.column == 5
|
62
|
+
assert str(retrieved) == "test.md:10:5"
|
63
|
+
|
64
|
+
def test_variable_debug_info(self) -> None:
|
65
|
+
"""Test debug info for variables."""
|
66
|
+
debug_info = DebugInfo()
|
67
|
+
|
68
|
+
# Create a variable and its debug info
|
69
|
+
var = Variable("count", MIRType.INT)
|
70
|
+
debug_var = DebugVariable(name="count", type_name="INT", scope_level=1, is_parameter=False)
|
71
|
+
|
72
|
+
# Add to debug info
|
73
|
+
debug_info.add_variable(var, debug_var)
|
74
|
+
|
75
|
+
# Check it was tracked
|
76
|
+
assert var in debug_info.variable_info
|
77
|
+
assert "count" in debug_info.symbols
|
78
|
+
assert debug_info.symbols["count"].type_name == "INT"
|
79
|
+
assert debug_info.symbols["count"].scope_level == 1
|
80
|
+
assert not debug_info.symbols["count"].is_parameter
|
81
|
+
|
82
|
+
def test_line_mapping(self) -> None:
|
83
|
+
"""Test bytecode to source line mapping."""
|
84
|
+
from machine_dialect.mir.debug_info import LineMapping
|
85
|
+
|
86
|
+
debug_info = DebugInfo()
|
87
|
+
|
88
|
+
# Add some line mappings
|
89
|
+
debug_info.add_line_mapping(LineMapping(0, 1)) # Bytecode offset 0 -> line 1
|
90
|
+
debug_info.add_line_mapping(LineMapping(10, 2)) # Bytecode offset 10 -> line 2
|
91
|
+
debug_info.add_line_mapping(LineMapping(20, 5)) # Bytecode offset 20 -> line 5
|
92
|
+
debug_info.add_line_mapping(LineMapping(30, 7)) # Bytecode offset 30 -> line 7
|
93
|
+
|
94
|
+
# Test lookups
|
95
|
+
assert debug_info.get_line_for_offset(0) == 1
|
96
|
+
assert debug_info.get_line_for_offset(5) == 1 # Between 0 and 10
|
97
|
+
assert debug_info.get_line_for_offset(10) == 2
|
98
|
+
assert debug_info.get_line_for_offset(15) == 2 # Between 10 and 20
|
99
|
+
assert debug_info.get_line_for_offset(25) == 5 # Between 20 and 30
|
100
|
+
assert debug_info.get_line_for_offset(35) == 7 # After 30
|
101
|
+
|
102
|
+
def test_source_map_generation(self) -> None:
|
103
|
+
"""Test source map generation."""
|
104
|
+
from machine_dialect.mir.debug_info import LineMapping
|
105
|
+
|
106
|
+
debug_info = DebugInfo()
|
107
|
+
debug_info.current_file = "example.md"
|
108
|
+
|
109
|
+
# Add line mappings
|
110
|
+
debug_info.add_line_mapping(LineMapping(0, 1))
|
111
|
+
debug_info.add_line_mapping(LineMapping(20, 3))
|
112
|
+
debug_info.add_line_mapping(LineMapping(40, 5))
|
113
|
+
|
114
|
+
# Add symbols
|
115
|
+
var1 = Variable("x", MIRType.INT)
|
116
|
+
var2 = Variable("y", MIRType.STRING)
|
117
|
+
debug_info.add_variable(var1, DebugVariable("x", "INT", 0, False))
|
118
|
+
debug_info.add_variable(var2, DebugVariable("y", "STRING", 1, True))
|
119
|
+
|
120
|
+
# Generate source map
|
121
|
+
source_map = debug_info.generate_source_map()
|
122
|
+
|
123
|
+
assert source_map["version"] == 1
|
124
|
+
assert source_map["file"] == "example.md"
|
125
|
+
assert len(source_map["mappings"]) == 3
|
126
|
+
assert source_map["mappings"][0]["bytecode_offset"] == 0
|
127
|
+
assert source_map["mappings"][0]["source_line"] == 1
|
128
|
+
|
129
|
+
assert "x" in source_map["symbols"]
|
130
|
+
assert source_map["symbols"]["x"]["type"] == "INT"
|
131
|
+
assert source_map["symbols"]["x"]["scope_level"] == 0
|
132
|
+
assert not source_map["symbols"]["x"]["is_parameter"]
|
133
|
+
|
134
|
+
assert "y" in source_map["symbols"]
|
135
|
+
assert source_map["symbols"]["y"]["is_parameter"]
|
136
|
+
|
137
|
+
def test_debug_info_builder(self) -> None:
|
138
|
+
"""Test the debug info builder."""
|
139
|
+
builder = DebugInfoBuilder()
|
140
|
+
|
141
|
+
# Track some variables
|
142
|
+
var1 = Variable("local", MIRType.INT)
|
143
|
+
var2 = Variable("param", MIRType.STRING)
|
144
|
+
|
145
|
+
builder.track_variable("local", var1, "INT", is_parameter=False)
|
146
|
+
builder.track_variable("param", var2, "STRING", is_parameter=True)
|
147
|
+
|
148
|
+
# Track scope changes
|
149
|
+
builder.enter_scope()
|
150
|
+
var3 = Variable("nested", MIRType.BOOL)
|
151
|
+
builder.track_variable("nested", var3, "BOOL", is_parameter=False)
|
152
|
+
|
153
|
+
# Get debug info
|
154
|
+
debug_info = builder.get_debug_info()
|
155
|
+
|
156
|
+
assert "local" in debug_info.symbols
|
157
|
+
assert debug_info.symbols["local"].scope_level == 0
|
158
|
+
|
159
|
+
assert "param" in debug_info.symbols
|
160
|
+
assert debug_info.symbols["param"].is_parameter
|
161
|
+
|
162
|
+
assert "nested" in debug_info.symbols
|
163
|
+
assert debug_info.symbols["nested"].scope_level == 1
|
164
|
+
|
165
|
+
# Exit scope
|
166
|
+
builder.exit_scope()
|
167
|
+
assert builder.scope_level == 0
|
168
|
+
|
169
|
+
def test_instruction_tracking(self) -> None:
|
170
|
+
"""Test tracking instructions with source locations."""
|
171
|
+
builder = DebugInfoBuilder()
|
172
|
+
builder.debug_info.current_file = "test.md"
|
173
|
+
|
174
|
+
# Create some instructions
|
175
|
+
inst1 = LoadConst(Variable("t1", MIRType.INT, 0), Constant(10), (1, 1))
|
176
|
+
inst2 = LoadConst(Variable("t2", MIRType.INT, 0), Constant(20), (1, 1))
|
177
|
+
inst3 = BinaryOp(
|
178
|
+
Variable("t3", MIRType.INT), "+", Variable("t1", MIRType.INT, 0), Variable("t2", MIRType.INT), (1, 1)
|
179
|
+
)
|
180
|
+
|
181
|
+
# Track with line numbers
|
182
|
+
builder.track_instruction(inst1, 5, 10)
|
183
|
+
builder.track_instruction(inst2, 6, 10)
|
184
|
+
builder.track_instruction(inst3, 7, 15)
|
185
|
+
|
186
|
+
debug_info = builder.get_debug_info()
|
187
|
+
|
188
|
+
# Check locations were recorded
|
189
|
+
loc1 = debug_info.get_instruction_location(inst1)
|
190
|
+
assert loc1 is not None
|
191
|
+
assert loc1.line == 5
|
192
|
+
assert loc1.column == 10
|
193
|
+
|
194
|
+
loc3 = debug_info.get_instruction_location(inst3)
|
195
|
+
assert loc3 is not None
|
196
|
+
assert loc3.line == 7
|
197
|
+
assert loc3.column == 15
|
198
|
+
|
199
|
+
# Current line should be updated
|
200
|
+
assert builder.current_line == 7
|
201
|
+
|
202
|
+
def test_debug_info_in_lowering(self) -> None:
|
203
|
+
"""Test that debug info is collected during HIR to MIR lowering."""
|
204
|
+
program = Program(
|
205
|
+
statements=[
|
206
|
+
SetStatement(
|
207
|
+
self._token(TokenType.KW_SET, "set"),
|
208
|
+
Identifier(self._token(TokenType.MISC_IDENT, "count"), "count"),
|
209
|
+
WholeNumberLiteral(self._token(TokenType.LIT_WHOLE_NUMBER, "0"), 0),
|
210
|
+
),
|
211
|
+
SetStatement(
|
212
|
+
self._token(TokenType.KW_SET, "set"),
|
213
|
+
Identifier(self._token(TokenType.MISC_IDENT, "message"), "message"),
|
214
|
+
StringLiteral(self._token(TokenType.LIT_TEXT, '"Hello"'), '"Hello"'),
|
215
|
+
),
|
216
|
+
]
|
217
|
+
)
|
218
|
+
|
219
|
+
# Create lowerer and lower the program
|
220
|
+
lowerer = HIRToMIRLowering()
|
221
|
+
lowerer.lower_program(program)
|
222
|
+
|
223
|
+
# Check debug info was collected
|
224
|
+
debug_info = lowerer.debug_builder.get_debug_info()
|
225
|
+
|
226
|
+
# Should have tracked variables
|
227
|
+
assert len(debug_info.symbols) >= 2
|
228
|
+
assert "count" in debug_info.symbols
|
229
|
+
assert "message" in debug_info.symbols
|
230
|
+
|
231
|
+
# Check types were tracked (MIRType enum returns lowercase strings)
|
232
|
+
assert (
|
233
|
+
"int" in debug_info.symbols["count"].type_name.lower()
|
234
|
+
or "unknown" in debug_info.symbols["count"].type_name.lower()
|
235
|
+
)
|
236
|
+
assert (
|
237
|
+
"string" in debug_info.symbols["message"].type_name.lower()
|
238
|
+
or "unknown" in debug_info.symbols["message"].type_name.lower()
|
239
|
+
)
|
240
|
+
|
241
|
+
def test_parameter_debug_info(self) -> None:
|
242
|
+
"""Test debug info for function parameters."""
|
243
|
+
# Create body block and add statements
|
244
|
+
body = BlockStatement(self._token(TokenType.OP_GT, ">"))
|
245
|
+
body.statements = [
|
246
|
+
ReturnStatement(
|
247
|
+
Token(TokenType.KW_RETURN, "return", 0, 0),
|
248
|
+
return_value=self._create_infix(
|
249
|
+
Identifier(self._token(TokenType.MISC_IDENT, "x"), "x"),
|
250
|
+
"+",
|
251
|
+
Identifier(self._token(TokenType.MISC_IDENT, "y"), "y"),
|
252
|
+
),
|
253
|
+
)
|
254
|
+
]
|
255
|
+
|
256
|
+
func = FunctionStatement(
|
257
|
+
token=self._token(TokenType.KW_DEFINE, "define"),
|
258
|
+
visibility=FunctionVisibility.FUNCTION,
|
259
|
+
name=Identifier(self._token(TokenType.MISC_IDENT, "add"), "add"),
|
260
|
+
inputs=[
|
261
|
+
Parameter(
|
262
|
+
self._token(TokenType.MISC_IDENT, "x"),
|
263
|
+
Identifier(self._token(TokenType.MISC_IDENT, "x"), "x"),
|
264
|
+
"int",
|
265
|
+
),
|
266
|
+
Parameter(
|
267
|
+
self._token(TokenType.MISC_IDENT, "y"),
|
268
|
+
Identifier(self._token(TokenType.MISC_IDENT, "y"), "y"),
|
269
|
+
"int",
|
270
|
+
),
|
271
|
+
],
|
272
|
+
body=body,
|
273
|
+
)
|
274
|
+
|
275
|
+
program = Program(statements=[func])
|
276
|
+
lowerer = HIRToMIRLowering()
|
277
|
+
lowerer.lower_program(program)
|
278
|
+
|
279
|
+
debug_info = lowerer.debug_builder.get_debug_info()
|
280
|
+
|
281
|
+
# Check parameters were tracked
|
282
|
+
assert "x" in debug_info.symbols
|
283
|
+
assert "y" in debug_info.symbols
|
284
|
+
assert debug_info.symbols["x"].is_parameter
|
285
|
+
assert debug_info.symbols["y"].is_parameter
|
@@ -0,0 +1,192 @@
|
|
1
|
+
"""Tests for dictionary extraction MIR lowering."""
|
2
|
+
|
3
|
+
from machine_dialect.ast import Program
|
4
|
+
from machine_dialect.ast.dict_extraction import DictExtraction
|
5
|
+
from machine_dialect.ast.expressions import Identifier
|
6
|
+
from machine_dialect.ast.literals import NamedListLiteral, StringLiteral, WholeNumberLiteral
|
7
|
+
from machine_dialect.ast.statements import SetStatement, Statement
|
8
|
+
from machine_dialect.lexer.tokens import Token, TokenType
|
9
|
+
from machine_dialect.mir.hir_to_mir import lower_to_mir
|
10
|
+
|
11
|
+
|
12
|
+
class TestDictExtractionLowering:
|
13
|
+
"""Test lowering of dictionary extraction to MIR."""
|
14
|
+
|
15
|
+
def test_lower_dict_keys_extraction(self) -> None:
|
16
|
+
"""Test lowering 'the names of dict' to DictKeys instruction."""
|
17
|
+
# Create a dictionary and extract its keys
|
18
|
+
dict_token = Token(TokenType.PUNCT_DASH, "-", 1, 1)
|
19
|
+
dict_literal = NamedListLiteral(
|
20
|
+
dict_token,
|
21
|
+
[
|
22
|
+
(
|
23
|
+
"name",
|
24
|
+
StringLiteral(Token(TokenType.LIT_TEXT, "Alice", 1, 1), "Alice"),
|
25
|
+
),
|
26
|
+
(
|
27
|
+
"age",
|
28
|
+
WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "30", 1, 1), 30),
|
29
|
+
),
|
30
|
+
],
|
31
|
+
)
|
32
|
+
|
33
|
+
# Create the extraction expression
|
34
|
+
extraction = DictExtraction(
|
35
|
+
Token(TokenType.MISC_STOPWORD, "the", 1, 1),
|
36
|
+
Identifier(Token(TokenType.MISC_IDENT, "person", 1, 1), "person"),
|
37
|
+
"names",
|
38
|
+
)
|
39
|
+
|
40
|
+
# Create statements to lower
|
41
|
+
statements: list[Statement] = [
|
42
|
+
SetStatement(
|
43
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
44
|
+
Identifier(Token(TokenType.MISC_IDENT, "person", 1, 1), "person"),
|
45
|
+
dict_literal,
|
46
|
+
),
|
47
|
+
SetStatement(
|
48
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
49
|
+
Identifier(Token(TokenType.MISC_IDENT, "keys", 1, 1), "keys"),
|
50
|
+
extraction,
|
51
|
+
),
|
52
|
+
]
|
53
|
+
|
54
|
+
# Lower to MIR
|
55
|
+
program = Program(statements)
|
56
|
+
mir_module = lower_to_mir(program)
|
57
|
+
|
58
|
+
# Check that we have DictKeys instruction
|
59
|
+
main_func = mir_module.get_function("__main__")
|
60
|
+
|
61
|
+
assert main_func is not None
|
62
|
+
|
63
|
+
# Look for DictKeys instruction
|
64
|
+
dict_keys_insts = []
|
65
|
+
for block in main_func.cfg.blocks.values():
|
66
|
+
for inst in block.instructions:
|
67
|
+
if inst.__class__.__name__ == "DictKeys":
|
68
|
+
dict_keys_insts.append(inst)
|
69
|
+
|
70
|
+
assert len(dict_keys_insts) == 1, "Should have one DictKeys instruction"
|
71
|
+
|
72
|
+
def test_lower_dict_values_extraction(self) -> None:
|
73
|
+
"""Test lowering 'the contents of dict' to DictValues instruction."""
|
74
|
+
# Create a dictionary and extract its values
|
75
|
+
dict_token = Token(TokenType.PUNCT_DASH, "-", 1, 1)
|
76
|
+
dict_literal = NamedListLiteral(
|
77
|
+
dict_token,
|
78
|
+
[
|
79
|
+
(
|
80
|
+
"host",
|
81
|
+
StringLiteral(Token(TokenType.LIT_TEXT, "localhost", 1, 1), "localhost"),
|
82
|
+
),
|
83
|
+
(
|
84
|
+
"port",
|
85
|
+
WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "8080", 1, 1), 8080),
|
86
|
+
),
|
87
|
+
],
|
88
|
+
)
|
89
|
+
|
90
|
+
# Create the extraction expression
|
91
|
+
extraction = DictExtraction(
|
92
|
+
Token(TokenType.MISC_STOPWORD, "the", 1, 1),
|
93
|
+
Identifier(Token(TokenType.MISC_IDENT, "config", 1, 1), "config"),
|
94
|
+
"contents",
|
95
|
+
)
|
96
|
+
|
97
|
+
# Create statements to lower
|
98
|
+
statements: list[Statement] = [
|
99
|
+
SetStatement(
|
100
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
101
|
+
Identifier(Token(TokenType.MISC_IDENT, "config", 1, 1), "config"),
|
102
|
+
dict_literal,
|
103
|
+
),
|
104
|
+
SetStatement(
|
105
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
106
|
+
Identifier(Token(TokenType.MISC_IDENT, "values", 1, 1), "values"),
|
107
|
+
extraction,
|
108
|
+
),
|
109
|
+
]
|
110
|
+
|
111
|
+
# Lower to MIR
|
112
|
+
program = Program(statements)
|
113
|
+
mir_module = lower_to_mir(program)
|
114
|
+
|
115
|
+
# Check that we have DictValues instruction
|
116
|
+
main_func = mir_module.get_function("__main__")
|
117
|
+
assert main_func is not None
|
118
|
+
|
119
|
+
# Look for DictValues instruction
|
120
|
+
dict_values_insts = []
|
121
|
+
for block in main_func.cfg.blocks.values():
|
122
|
+
for inst in block.instructions:
|
123
|
+
if inst.__class__.__name__ == "DictValues":
|
124
|
+
dict_values_insts.append(inst)
|
125
|
+
|
126
|
+
assert len(dict_values_insts) == 1, "Should have one DictValues instruction"
|
127
|
+
|
128
|
+
def test_extraction_creates_array_result(self) -> None:
|
129
|
+
"""Test that extraction creates an array result."""
|
130
|
+
# Create the extraction expression
|
131
|
+
extraction = DictExtraction(
|
132
|
+
Token(TokenType.MISC_STOPWORD, "the", 1, 1),
|
133
|
+
Identifier(Token(TokenType.MISC_IDENT, "data", 1, 1), "data"),
|
134
|
+
"names",
|
135
|
+
)
|
136
|
+
|
137
|
+
# Create statements
|
138
|
+
statements: list[Statement] = [
|
139
|
+
SetStatement(
|
140
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
141
|
+
Identifier(Token(TokenType.MISC_IDENT, "data", 1, 1), "data"),
|
142
|
+
NamedListLiteral(Token(TokenType.PUNCT_DASH, "-", 1, 1), []),
|
143
|
+
),
|
144
|
+
SetStatement(
|
145
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
146
|
+
Identifier(Token(TokenType.MISC_IDENT, "result", 1, 1), "result"),
|
147
|
+
extraction,
|
148
|
+
),
|
149
|
+
]
|
150
|
+
|
151
|
+
# Lower to MIR
|
152
|
+
program = Program(statements)
|
153
|
+
mir_module = lower_to_mir(program)
|
154
|
+
|
155
|
+
# The result should be stored as an array type
|
156
|
+
main_func = mir_module.get_function("__main__")
|
157
|
+
|
158
|
+
# Check that the result is treated as an array
|
159
|
+
# This would be verified by checking the type of the temp register
|
160
|
+
# that holds the result of DictKeys/DictValues
|
161
|
+
assert main_func is not None
|
162
|
+
|
163
|
+
def test_extraction_from_expression(self) -> None:
|
164
|
+
"""Test extraction from a dictionary expression (not just identifier)."""
|
165
|
+
# This tests that we can extract from any expression that evaluates to a dict
|
166
|
+
extraction = DictExtraction(
|
167
|
+
Token(TokenType.MISC_STOPWORD, "the", 1, 1),
|
168
|
+
# Could be a more complex expression in real code
|
169
|
+
Identifier(Token(TokenType.MISC_IDENT, "get_config", 1, 1), "get_config"),
|
170
|
+
"contents",
|
171
|
+
)
|
172
|
+
|
173
|
+
statements: list[Statement] = [
|
174
|
+
SetStatement(
|
175
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
176
|
+
Identifier(Token(TokenType.MISC_IDENT, "get_config", 1, 1), "get_config"),
|
177
|
+
NamedListLiteral(Token(TokenType.PUNCT_DASH, "-", 1, 1), []),
|
178
|
+
),
|
179
|
+
SetStatement(
|
180
|
+
Token(TokenType.KW_SET, "Set", 1, 1),
|
181
|
+
Identifier(Token(TokenType.MISC_IDENT, "vals", 1, 1), "vals"),
|
182
|
+
extraction,
|
183
|
+
),
|
184
|
+
]
|
185
|
+
|
186
|
+
# Lower to MIR
|
187
|
+
program = Program(statements)
|
188
|
+
mir_module = lower_to_mir(program)
|
189
|
+
|
190
|
+
# Should successfully lower
|
191
|
+
main_func = mir_module.get_function("__main__")
|
192
|
+
assert main_func is not None
|