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,418 @@
|
|
1
|
+
"""Tests for symbol table management."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
from machine_dialect.codegen.symtab import Scope, Symbol, SymbolTable, SymbolType
|
8
|
+
|
9
|
+
|
10
|
+
class TestSymbolType:
|
11
|
+
"""Test SymbolType enum."""
|
12
|
+
|
13
|
+
def test_symbol_types(self) -> None:
|
14
|
+
"""Test all symbol type values."""
|
15
|
+
assert SymbolType.LOCAL.value == "local"
|
16
|
+
assert SymbolType.GLOBAL.value == "global"
|
17
|
+
assert SymbolType.PARAMETER.value == "parameter"
|
18
|
+
|
19
|
+
|
20
|
+
class TestSymbol:
|
21
|
+
"""Test Symbol dataclass."""
|
22
|
+
|
23
|
+
def test_create_symbol(self) -> None:
|
24
|
+
"""Test creating a symbol."""
|
25
|
+
symbol = Symbol("x", SymbolType.LOCAL, 0)
|
26
|
+
assert symbol.name == "x"
|
27
|
+
assert symbol.symbol_type == SymbolType.LOCAL
|
28
|
+
assert symbol.slot == 0
|
29
|
+
|
30
|
+
def test_create_global_symbol(self) -> None:
|
31
|
+
"""Test creating a global symbol."""
|
32
|
+
symbol = Symbol("global_var", SymbolType.GLOBAL, -1)
|
33
|
+
assert symbol.name == "global_var"
|
34
|
+
assert symbol.symbol_type == SymbolType.GLOBAL
|
35
|
+
assert symbol.slot == -1
|
36
|
+
|
37
|
+
def test_create_parameter_symbol(self) -> None:
|
38
|
+
"""Test creating a parameter symbol."""
|
39
|
+
symbol = Symbol("param", SymbolType.PARAMETER, 1)
|
40
|
+
assert symbol.name == "param"
|
41
|
+
assert symbol.symbol_type == SymbolType.PARAMETER
|
42
|
+
assert symbol.slot == 1
|
43
|
+
|
44
|
+
|
45
|
+
class TestScope:
|
46
|
+
"""Test Scope class."""
|
47
|
+
|
48
|
+
def test_create_global_scope(self) -> None:
|
49
|
+
"""Test creating a global scope."""
|
50
|
+
scope = Scope()
|
51
|
+
assert scope.parent is None
|
52
|
+
assert scope.name == "global"
|
53
|
+
assert scope.is_global is True
|
54
|
+
assert len(scope.symbols) == 0
|
55
|
+
assert scope.next_slot == 0
|
56
|
+
|
57
|
+
def test_create_nested_scope(self) -> None:
|
58
|
+
"""Test creating a nested scope."""
|
59
|
+
parent = Scope()
|
60
|
+
child = Scope(parent, "function")
|
61
|
+
assert child.parent is parent
|
62
|
+
assert child.name == "function"
|
63
|
+
assert child.is_global is False
|
64
|
+
assert len(child.symbols) == 0
|
65
|
+
assert child.next_slot == 0
|
66
|
+
|
67
|
+
def test_define_local_variable(self) -> None:
|
68
|
+
"""Test defining a local variable."""
|
69
|
+
scope = Scope(Scope(), "function")
|
70
|
+
symbol = scope.define_local("x")
|
71
|
+
|
72
|
+
assert symbol.name == "x"
|
73
|
+
assert symbol.symbol_type == SymbolType.LOCAL
|
74
|
+
assert symbol.slot == 0
|
75
|
+
assert scope.next_slot == 1
|
76
|
+
assert "x" in scope.symbols
|
77
|
+
|
78
|
+
def test_define_local_variable_already_exists(self) -> None:
|
79
|
+
"""Test defining a local variable that already exists."""
|
80
|
+
scope = Scope(Scope(), "function")
|
81
|
+
symbol1 = scope.define_local("x")
|
82
|
+
symbol2 = scope.define_local("x")
|
83
|
+
|
84
|
+
# Should return the same symbol
|
85
|
+
assert symbol1 is symbol2
|
86
|
+
assert scope.next_slot == 1 # Should not increment
|
87
|
+
|
88
|
+
def test_define_multiple_local_variables(self) -> None:
|
89
|
+
"""Test defining multiple local variables."""
|
90
|
+
scope = Scope(Scope(), "function")
|
91
|
+
x = scope.define_local("x")
|
92
|
+
y = scope.define_local("y")
|
93
|
+
z = scope.define_local("z")
|
94
|
+
|
95
|
+
assert x.slot == 0
|
96
|
+
assert y.slot == 1
|
97
|
+
assert z.slot == 2
|
98
|
+
assert scope.next_slot == 3
|
99
|
+
|
100
|
+
def test_define_parameter(self) -> None:
|
101
|
+
"""Test defining a parameter."""
|
102
|
+
scope = Scope(Scope(), "function")
|
103
|
+
symbol = scope.define_parameter("param")
|
104
|
+
|
105
|
+
assert symbol.name == "param"
|
106
|
+
assert symbol.symbol_type == SymbolType.PARAMETER
|
107
|
+
assert symbol.slot == 0
|
108
|
+
assert scope.next_slot == 1
|
109
|
+
assert "param" in scope.symbols
|
110
|
+
|
111
|
+
def test_define_multiple_parameters(self) -> None:
|
112
|
+
"""Test defining multiple parameters."""
|
113
|
+
scope = Scope(Scope(), "function")
|
114
|
+
a = scope.define_parameter("a")
|
115
|
+
b = scope.define_parameter("b")
|
116
|
+
|
117
|
+
assert a.slot == 0
|
118
|
+
assert b.slot == 1
|
119
|
+
assert scope.next_slot == 2
|
120
|
+
|
121
|
+
def test_define_global_variable(self) -> None:
|
122
|
+
"""Test defining a global variable."""
|
123
|
+
scope = Scope()
|
124
|
+
symbol = scope.define_global("global_var")
|
125
|
+
|
126
|
+
assert symbol.name == "global_var"
|
127
|
+
assert symbol.symbol_type == SymbolType.GLOBAL
|
128
|
+
assert symbol.slot == -1
|
129
|
+
assert scope.next_slot == 0 # Globals don't use slots
|
130
|
+
assert "global_var" in scope.symbols
|
131
|
+
|
132
|
+
def test_resolve_in_current_scope(self) -> None:
|
133
|
+
"""Test resolving a variable in the current scope."""
|
134
|
+
scope = Scope(Scope(), "function")
|
135
|
+
defined_symbol = scope.define_local("x")
|
136
|
+
resolved_symbol = scope.resolve("x")
|
137
|
+
|
138
|
+
assert resolved_symbol is defined_symbol
|
139
|
+
|
140
|
+
def test_resolve_in_parent_scope(self) -> None:
|
141
|
+
"""Test resolving a variable in parent scope."""
|
142
|
+
parent = Scope()
|
143
|
+
parent_symbol = parent.define_global("global_var")
|
144
|
+
child = Scope(parent, "function")
|
145
|
+
|
146
|
+
resolved_symbol = child.resolve("global_var")
|
147
|
+
assert resolved_symbol is parent_symbol
|
148
|
+
|
149
|
+
def test_resolve_nested_scopes(self) -> None:
|
150
|
+
"""Test resolving through multiple nested scopes."""
|
151
|
+
grandparent = Scope()
|
152
|
+
grandparent_symbol = grandparent.define_global("x")
|
153
|
+
|
154
|
+
parent = Scope(grandparent, "outer")
|
155
|
+
child = Scope(parent, "inner")
|
156
|
+
|
157
|
+
resolved_symbol = child.resolve("x")
|
158
|
+
assert resolved_symbol is grandparent_symbol
|
159
|
+
|
160
|
+
def test_resolve_not_found(self) -> None:
|
161
|
+
"""Test resolving a non-existent variable."""
|
162
|
+
scope = Scope()
|
163
|
+
resolved_symbol = scope.resolve("nonexistent")
|
164
|
+
assert resolved_symbol is None
|
165
|
+
|
166
|
+
def test_resolve_shadowing(self) -> None:
|
167
|
+
"""Test variable shadowing."""
|
168
|
+
parent = Scope()
|
169
|
+
parent.define_global("x")
|
170
|
+
|
171
|
+
child = Scope(parent, "function")
|
172
|
+
child_symbol = child.define_local("x")
|
173
|
+
|
174
|
+
# Should resolve to child's x, not parent's
|
175
|
+
resolved_symbol = child.resolve("x")
|
176
|
+
assert resolved_symbol is child_symbol
|
177
|
+
assert resolved_symbol.symbol_type == SymbolType.LOCAL
|
178
|
+
|
179
|
+
def test_num_locals(self) -> None:
|
180
|
+
"""Test getting number of locals."""
|
181
|
+
scope = Scope(Scope(), "function")
|
182
|
+
assert scope.num_locals() == 0
|
183
|
+
|
184
|
+
scope.define_local("x")
|
185
|
+
assert scope.num_locals() == 1
|
186
|
+
|
187
|
+
scope.define_local("y")
|
188
|
+
assert scope.num_locals() == 2
|
189
|
+
|
190
|
+
# Parameters also count as locals for slot allocation
|
191
|
+
scope.define_parameter("param")
|
192
|
+
assert scope.num_locals() == 3
|
193
|
+
|
194
|
+
def test_num_locals_globals_dont_count(self) -> None:
|
195
|
+
"""Test that globals don't count toward num_locals."""
|
196
|
+
scope = Scope()
|
197
|
+
scope.define_global("global1")
|
198
|
+
scope.define_global("global2")
|
199
|
+
assert scope.num_locals() == 0
|
200
|
+
|
201
|
+
|
202
|
+
class TestSymbolTable:
|
203
|
+
"""Test SymbolTable class."""
|
204
|
+
|
205
|
+
def test_create_symbol_table(self) -> None:
|
206
|
+
"""Test creating a symbol table."""
|
207
|
+
symtab = SymbolTable()
|
208
|
+
assert symtab.current_scope is symtab.global_scope
|
209
|
+
assert symtab.current_scope.name == "global"
|
210
|
+
assert symtab.is_global_scope() is True
|
211
|
+
|
212
|
+
def test_enter_scope(self) -> None:
|
213
|
+
"""Test entering a new scope."""
|
214
|
+
symtab = SymbolTable()
|
215
|
+
symtab.enter_scope("function")
|
216
|
+
|
217
|
+
assert symtab.current_scope.name == "function"
|
218
|
+
assert symtab.current_scope.parent is symtab.global_scope
|
219
|
+
assert symtab.is_global_scope() is False
|
220
|
+
|
221
|
+
def test_exit_scope(self) -> None:
|
222
|
+
"""Test exiting a scope."""
|
223
|
+
symtab = SymbolTable()
|
224
|
+
symtab.enter_scope("function")
|
225
|
+
symtab.exit_scope()
|
226
|
+
|
227
|
+
assert symtab.current_scope is symtab.global_scope
|
228
|
+
assert symtab.is_global_scope() is True
|
229
|
+
|
230
|
+
def test_exit_global_scope_raises_error(self) -> None:
|
231
|
+
"""Test that exiting global scope raises an error."""
|
232
|
+
symtab = SymbolTable()
|
233
|
+
with pytest.raises(RuntimeError, match="Cannot exit global scope"):
|
234
|
+
symtab.exit_scope()
|
235
|
+
|
236
|
+
def test_nested_scopes(self) -> None:
|
237
|
+
"""Test nested scope management."""
|
238
|
+
symtab = SymbolTable()
|
239
|
+
|
240
|
+
# Enter function scope
|
241
|
+
symtab.enter_scope("function")
|
242
|
+
function_scope = symtab.current_scope
|
243
|
+
|
244
|
+
# Enter block scope
|
245
|
+
symtab.enter_scope("block")
|
246
|
+
block_scope = symtab.current_scope
|
247
|
+
|
248
|
+
assert block_scope.parent is function_scope
|
249
|
+
assert function_scope.parent is symtab.global_scope
|
250
|
+
|
251
|
+
# Exit back to function
|
252
|
+
symtab.exit_scope()
|
253
|
+
assert symtab.current_scope is function_scope
|
254
|
+
|
255
|
+
# Exit back to global
|
256
|
+
symtab.exit_scope()
|
257
|
+
assert symtab.current_scope is symtab.global_scope
|
258
|
+
|
259
|
+
def test_define_in_global_scope(self) -> None:
|
260
|
+
"""Test defining variables in global scope."""
|
261
|
+
symtab = SymbolTable()
|
262
|
+
symbol = symtab.define("global_var")
|
263
|
+
|
264
|
+
assert symbol.symbol_type == SymbolType.GLOBAL
|
265
|
+
assert symbol.slot == -1
|
266
|
+
assert symbol.name == "global_var"
|
267
|
+
|
268
|
+
def test_define_in_local_scope(self) -> None:
|
269
|
+
"""Test defining variables in local scope."""
|
270
|
+
symtab = SymbolTable()
|
271
|
+
symtab.enter_scope("function")
|
272
|
+
|
273
|
+
symbol = symtab.define("local_var")
|
274
|
+
assert symbol.symbol_type == SymbolType.LOCAL
|
275
|
+
assert symbol.slot == 0
|
276
|
+
assert symbol.name == "local_var"
|
277
|
+
|
278
|
+
def test_define_parameter(self) -> None:
|
279
|
+
"""Test defining parameters."""
|
280
|
+
symtab = SymbolTable()
|
281
|
+
symtab.enter_scope("function")
|
282
|
+
|
283
|
+
symbol = symtab.define("param", is_parameter=True)
|
284
|
+
assert symbol.symbol_type == SymbolType.PARAMETER
|
285
|
+
assert symbol.slot == 0
|
286
|
+
assert symbol.name == "param"
|
287
|
+
|
288
|
+
def test_define_parameter_in_global_scope(self) -> None:
|
289
|
+
"""Test that parameters in global scope become globals."""
|
290
|
+
symtab = SymbolTable()
|
291
|
+
symbol = symtab.define("param", is_parameter=True)
|
292
|
+
|
293
|
+
# In global scope, parameters become globals
|
294
|
+
assert symbol.symbol_type == SymbolType.GLOBAL
|
295
|
+
assert symbol.slot == -1
|
296
|
+
|
297
|
+
def test_resolve_local_variable(self) -> None:
|
298
|
+
"""Test resolving a local variable."""
|
299
|
+
symtab = SymbolTable()
|
300
|
+
symtab.enter_scope("function")
|
301
|
+
|
302
|
+
defined_symbol = symtab.define("x")
|
303
|
+
resolved_symbol = symtab.resolve("x")
|
304
|
+
|
305
|
+
assert resolved_symbol is defined_symbol
|
306
|
+
|
307
|
+
def test_resolve_global_variable(self) -> None:
|
308
|
+
"""Test resolving a global variable."""
|
309
|
+
symtab = SymbolTable()
|
310
|
+
global_symbol = symtab.define("global_var")
|
311
|
+
|
312
|
+
symtab.enter_scope("function")
|
313
|
+
resolved_symbol = symtab.resolve("global_var")
|
314
|
+
|
315
|
+
assert resolved_symbol is global_symbol
|
316
|
+
|
317
|
+
def test_resolve_creates_implicit_global(self) -> None:
|
318
|
+
"""Test that resolving undefined variable creates implicit global."""
|
319
|
+
symtab = SymbolTable()
|
320
|
+
symtab.enter_scope("function")
|
321
|
+
|
322
|
+
resolved_symbol = symtab.resolve("undefined_var")
|
323
|
+
|
324
|
+
assert resolved_symbol is not None
|
325
|
+
assert resolved_symbol.name == "undefined_var"
|
326
|
+
assert resolved_symbol.symbol_type == SymbolType.GLOBAL
|
327
|
+
assert resolved_symbol.slot == -1
|
328
|
+
|
329
|
+
def test_resolve_in_global_scope_returns_none(self) -> None:
|
330
|
+
"""Test that resolving undefined var in global scope returns None."""
|
331
|
+
symtab = SymbolTable()
|
332
|
+
resolved_symbol = symtab.resolve("undefined_var")
|
333
|
+
assert resolved_symbol is None
|
334
|
+
|
335
|
+
def test_num_locals(self) -> None:
|
336
|
+
"""Test getting number of locals in current scope."""
|
337
|
+
symtab = SymbolTable()
|
338
|
+
assert symtab.num_locals() == 0
|
339
|
+
|
340
|
+
symtab.enter_scope("function")
|
341
|
+
assert symtab.num_locals() == 0
|
342
|
+
|
343
|
+
symtab.define("x")
|
344
|
+
assert symtab.num_locals() == 1
|
345
|
+
|
346
|
+
symtab.define("y")
|
347
|
+
assert symtab.num_locals() == 2
|
348
|
+
|
349
|
+
def test_current_scope_name(self) -> None:
|
350
|
+
"""Test getting current scope name."""
|
351
|
+
symtab = SymbolTable()
|
352
|
+
assert symtab.current_scope_name() == "global"
|
353
|
+
|
354
|
+
symtab.enter_scope("function")
|
355
|
+
assert symtab.current_scope_name() == "function"
|
356
|
+
|
357
|
+
symtab.enter_scope("block")
|
358
|
+
assert symtab.current_scope_name() == "block"
|
359
|
+
|
360
|
+
def test_complex_scoping_scenario(self) -> None:
|
361
|
+
"""Test a complex scenario with multiple scopes and variables."""
|
362
|
+
symtab = SymbolTable()
|
363
|
+
|
364
|
+
# Define global variable
|
365
|
+
global_var = symtab.define("global_var")
|
366
|
+
|
367
|
+
# Enter function scope
|
368
|
+
symtab.enter_scope("function")
|
369
|
+
param1 = symtab.define("param1", is_parameter=True)
|
370
|
+
param2 = symtab.define("param2", is_parameter=True)
|
371
|
+
local1 = symtab.define("local1")
|
372
|
+
|
373
|
+
# Enter block scope
|
374
|
+
symtab.enter_scope("block")
|
375
|
+
local2 = symtab.define("local2")
|
376
|
+
|
377
|
+
# Test resolution from deepest scope
|
378
|
+
assert symtab.resolve("local2") is local2
|
379
|
+
assert symtab.resolve("local1") is local1
|
380
|
+
assert symtab.resolve("param1") is param1
|
381
|
+
assert symtab.resolve("global_var") is global_var
|
382
|
+
|
383
|
+
# Test slot allocation
|
384
|
+
assert param1.slot == 0
|
385
|
+
assert param2.slot == 1
|
386
|
+
assert local1.slot == 2
|
387
|
+
assert local2.slot == 0 # New scope, new slot numbering
|
388
|
+
|
389
|
+
# Test scope info
|
390
|
+
assert symtab.current_scope_name() == "block"
|
391
|
+
assert symtab.num_locals() == 1 # Only local2 in current scope
|
392
|
+
|
393
|
+
symtab.exit_scope()
|
394
|
+
assert symtab.num_locals() == 3 # param1, param2, local1
|
395
|
+
|
396
|
+
def test_variable_shadowing_through_symbol_table(self) -> None:
|
397
|
+
"""Test variable shadowing through symbol table interface."""
|
398
|
+
symtab = SymbolTable()
|
399
|
+
|
400
|
+
# Define global x
|
401
|
+
global_x = symtab.define("x")
|
402
|
+
|
403
|
+
# Enter function and define local x
|
404
|
+
symtab.enter_scope("function")
|
405
|
+
local_x = symtab.define("x")
|
406
|
+
|
407
|
+
# Should resolve to local x
|
408
|
+
resolved_x = symtab.resolve("x")
|
409
|
+
assert resolved_x is local_x
|
410
|
+
assert resolved_x.symbol_type == SymbolType.LOCAL
|
411
|
+
|
412
|
+
# Exit function scope
|
413
|
+
symtab.exit_scope()
|
414
|
+
|
415
|
+
# Should now resolve to global x
|
416
|
+
resolved_x = symtab.resolve("x")
|
417
|
+
assert resolved_x is global_x
|
418
|
+
assert resolved_x.symbol_type == SymbolType.GLOBAL
|