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,62 @@
|
|
1
|
+
from machine_dialect.ast import Identifier, Program, ReturnStatement, SetStatement, WholeNumberLiteral
|
2
|
+
from machine_dialect.lexer import Token, TokenType
|
3
|
+
|
4
|
+
|
5
|
+
class TestASTStringRepresentation:
|
6
|
+
def test_set_statement_string(self) -> None:
|
7
|
+
program: Program = Program(
|
8
|
+
statements=[
|
9
|
+
SetStatement(
|
10
|
+
token=Token(TokenType.KW_SET, "Set", 1, 0),
|
11
|
+
name=Identifier(token=Token(TokenType.MISC_IDENT, "my_var", 1, 4), value="my_var"),
|
12
|
+
value=Identifier(
|
13
|
+
token=Token(TokenType.MISC_IDENT, "other_var", 1, 15),
|
14
|
+
value="other_var",
|
15
|
+
),
|
16
|
+
)
|
17
|
+
]
|
18
|
+
)
|
19
|
+
|
20
|
+
program_str = str(program)
|
21
|
+
|
22
|
+
assert program_str == "Set `my_var` to `other_var`.\n"
|
23
|
+
|
24
|
+
def test_return_statement_string(self) -> None:
|
25
|
+
program: Program = Program(
|
26
|
+
statements=[
|
27
|
+
ReturnStatement(
|
28
|
+
token=Token(TokenType.KW_RETURN, "Give back", 1, 0),
|
29
|
+
return_value=Identifier(token=Token(TokenType.MISC_IDENT, "my_var", 1, 7), value="my_var"),
|
30
|
+
)
|
31
|
+
]
|
32
|
+
)
|
33
|
+
|
34
|
+
program_str = str(program)
|
35
|
+
|
36
|
+
assert program_str == "\nGive back `my_var`.\n"
|
37
|
+
|
38
|
+
def test_multiple_statements_string(self) -> None:
|
39
|
+
program: Program = Program(
|
40
|
+
statements=[
|
41
|
+
SetStatement(
|
42
|
+
token=Token(TokenType.KW_SET, "Set", 1, 0),
|
43
|
+
name=Identifier(token=Token(TokenType.MISC_IDENT, "x", 1, 4), value="x"),
|
44
|
+
value=WholeNumberLiteral(token=Token(TokenType.LIT_WHOLE_NUMBER, "_42_", 1, 11), value=42),
|
45
|
+
),
|
46
|
+
SetStatement(
|
47
|
+
token=Token(TokenType.KW_SET, "Set", 2, 0),
|
48
|
+
name=Identifier(token=Token(TokenType.MISC_IDENT, "result", 2, 4), value="result"),
|
49
|
+
value=Identifier(token=Token(TokenType.MISC_IDENT, "x", 2, 11), value="x"),
|
50
|
+
),
|
51
|
+
ReturnStatement(
|
52
|
+
token=Token(TokenType.KW_RETURN, "Give back", 3, 0),
|
53
|
+
return_value=Identifier(token=Token(TokenType.MISC_IDENT, "result", 3, 7), value="result"),
|
54
|
+
),
|
55
|
+
]
|
56
|
+
)
|
57
|
+
|
58
|
+
program_str = str(program)
|
59
|
+
|
60
|
+
# The Program concatenates statements with ".\n" and adds final ".\n"
|
61
|
+
expected = "Set `x` to _42_.\nSet `result` to `x`.\n\nGive back `result`.\n"
|
62
|
+
assert program_str == expected
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from machine_dialect.ast import YesNoLiteral
|
2
|
+
from machine_dialect.lexer import Token, TokenType
|
3
|
+
|
4
|
+
|
5
|
+
class TestBooleanLiteral:
|
6
|
+
def test_boolean_literal_true_display(self) -> None:
|
7
|
+
"""Test that YesNoLiteral displays Yes with underscores."""
|
8
|
+
token = Token(TokenType.LIT_YES, "Yes", 1, 0)
|
9
|
+
literal = YesNoLiteral(token, True)
|
10
|
+
|
11
|
+
assert str(literal) == "_Yes_"
|
12
|
+
|
13
|
+
def test_boolean_literal_false_display(self) -> None:
|
14
|
+
"""Test that YesNoLiteral displays No with underscores."""
|
15
|
+
token = Token(TokenType.LIT_NO, "No", 1, 0)
|
16
|
+
literal = YesNoLiteral(token, False)
|
17
|
+
|
18
|
+
assert str(literal) == "_No_"
|
19
|
+
|
20
|
+
def test_boolean_literal_value(self) -> None:
|
21
|
+
"""Test that YesNoLiteral stores the correct boolean value."""
|
22
|
+
true_token = Token(TokenType.LIT_YES, "Yes", 1, 0)
|
23
|
+
true_literal = YesNoLiteral(true_token, True)
|
24
|
+
|
25
|
+
false_token = Token(TokenType.LIT_NO, "No", 1, 0)
|
26
|
+
false_literal = YesNoLiteral(false_token, False)
|
27
|
+
|
28
|
+
assert true_literal.value is True
|
29
|
+
assert false_literal.value is False
|
@@ -0,0 +1,138 @@
|
|
1
|
+
"""Test HIR generation for collection operations."""
|
2
|
+
|
3
|
+
from machine_dialect.ast.expressions import CollectionAccessExpression, Identifier
|
4
|
+
from machine_dialect.ast.literals import UnorderedListLiteral, WholeNumberLiteral
|
5
|
+
from machine_dialect.ast.statements import CollectionMutationStatement
|
6
|
+
from machine_dialect.lexer import Token, TokenType
|
7
|
+
|
8
|
+
|
9
|
+
class TestCollectionHIR:
|
10
|
+
"""Test HIR generation for collection operations."""
|
11
|
+
|
12
|
+
def test_list_literal_to_hir(self) -> None:
|
13
|
+
"""Test that list literals convert to HIR properly."""
|
14
|
+
token = Token(TokenType.PUNCT_DASH, "-", 1, 1)
|
15
|
+
from machine_dialect.ast.expressions import Expression
|
16
|
+
|
17
|
+
elements: list[Expression] = [
|
18
|
+
WholeNumberLiteral(token, 1),
|
19
|
+
WholeNumberLiteral(token, 2),
|
20
|
+
WholeNumberLiteral(token, 3),
|
21
|
+
]
|
22
|
+
|
23
|
+
unordered = UnorderedListLiteral(token, elements)
|
24
|
+
hir = unordered.to_hir()
|
25
|
+
|
26
|
+
assert isinstance(hir, UnorderedListLiteral)
|
27
|
+
assert len(hir.elements) == 3
|
28
|
+
assert all(isinstance(elem, WholeNumberLiteral) for elem in hir.elements)
|
29
|
+
|
30
|
+
def test_collection_access_ordinal_to_hir(self) -> None:
|
31
|
+
"""Test that ordinal access converts to zero-based index."""
|
32
|
+
token = Token(TokenType.KW_FIRST, "first", 1, 1)
|
33
|
+
collection = Identifier(token, "items")
|
34
|
+
|
35
|
+
# Test "first" -> 0
|
36
|
+
access = CollectionAccessExpression(token, collection, "first", "ordinal")
|
37
|
+
hir = access.to_hir()
|
38
|
+
|
39
|
+
assert hir.accessor == 0
|
40
|
+
assert hir.access_type == "numeric" # Changed from ordinal to numeric
|
41
|
+
|
42
|
+
# Test "second" -> 1
|
43
|
+
access = CollectionAccessExpression(token, collection, "second", "ordinal")
|
44
|
+
hir = access.to_hir()
|
45
|
+
|
46
|
+
assert hir.accessor == 1
|
47
|
+
assert hir.access_type == "numeric"
|
48
|
+
|
49
|
+
# Test "third" -> 2
|
50
|
+
access = CollectionAccessExpression(token, collection, "third", "ordinal")
|
51
|
+
hir = access.to_hir()
|
52
|
+
|
53
|
+
assert hir.accessor == 2
|
54
|
+
assert hir.access_type == "numeric"
|
55
|
+
|
56
|
+
def test_collection_access_numeric_to_hir(self) -> None:
|
57
|
+
"""Test that numeric access converts from 1-based to 0-based."""
|
58
|
+
token = Token(TokenType.KW_ITEM, "item", 1, 1)
|
59
|
+
collection = Identifier(token, "items")
|
60
|
+
|
61
|
+
# Test numeric index: 1 -> 0
|
62
|
+
access = CollectionAccessExpression(token, collection, 1, "numeric")
|
63
|
+
hir = access.to_hir()
|
64
|
+
|
65
|
+
assert hir.accessor == 0 # 1-based to 0-based
|
66
|
+
assert hir.access_type == "numeric"
|
67
|
+
|
68
|
+
# Test larger index: 877 -> 876
|
69
|
+
access = CollectionAccessExpression(token, collection, 877, "numeric")
|
70
|
+
hir = access.to_hir()
|
71
|
+
|
72
|
+
assert hir.accessor == 876
|
73
|
+
assert hir.access_type == "numeric"
|
74
|
+
|
75
|
+
def test_collection_access_last_to_hir(self) -> None:
|
76
|
+
"""Test that 'last' remains as special case."""
|
77
|
+
token = Token(TokenType.KW_LAST, "last", 1, 1)
|
78
|
+
collection = Identifier(token, "items")
|
79
|
+
|
80
|
+
access = CollectionAccessExpression(token, collection, "last", "ordinal")
|
81
|
+
hir = access.to_hir()
|
82
|
+
|
83
|
+
# "last" should remain as "last" for special handling in MIR
|
84
|
+
assert hir.accessor == "last"
|
85
|
+
assert hir.access_type == "ordinal" # Keeps ordinal type for "last"
|
86
|
+
|
87
|
+
def test_collection_mutation_ordinal_to_hir(self) -> None:
|
88
|
+
"""Test that mutation statements convert ordinals properly."""
|
89
|
+
token = Token(TokenType.KW_SET, "Set", 1, 1)
|
90
|
+
collection = Identifier(token, "items")
|
91
|
+
value = WholeNumberLiteral(token, 10)
|
92
|
+
|
93
|
+
# Test "Set the first item" -> index 0
|
94
|
+
mutation = CollectionMutationStatement(token, "set", collection, value, "first", "ordinal")
|
95
|
+
hir = mutation.to_hir()
|
96
|
+
|
97
|
+
assert hir.position == 0
|
98
|
+
assert hir.position_type == "numeric" # Changed from ordinal
|
99
|
+
|
100
|
+
# Test "Set the second item" -> index 1
|
101
|
+
mutation = CollectionMutationStatement(token, "set", collection, value, "second", "ordinal")
|
102
|
+
hir = mutation.to_hir()
|
103
|
+
|
104
|
+
assert hir.position == 1
|
105
|
+
assert hir.position_type == "numeric"
|
106
|
+
|
107
|
+
def test_collection_mutation_numeric_to_hir(self) -> None:
|
108
|
+
"""Test that mutation statements convert numeric indices."""
|
109
|
+
token = Token(TokenType.KW_SET, "Set", 1, 1)
|
110
|
+
collection = Identifier(token, "items")
|
111
|
+
value = WholeNumberLiteral(token, 20)
|
112
|
+
|
113
|
+
# Test "Set item 1" -> index 0
|
114
|
+
mutation = CollectionMutationStatement(token, "set", collection, value, 1, "numeric")
|
115
|
+
hir = mutation.to_hir()
|
116
|
+
|
117
|
+
assert hir.position == 0 # 1-based to 0-based
|
118
|
+
assert hir.position_type == "numeric"
|
119
|
+
|
120
|
+
# Test "Set item 42" -> index 41
|
121
|
+
mutation = CollectionMutationStatement(token, "set", collection, value, 42, "numeric")
|
122
|
+
hir = mutation.to_hir()
|
123
|
+
|
124
|
+
assert hir.position == 41
|
125
|
+
assert hir.position_type == "numeric"
|
126
|
+
|
127
|
+
def test_collection_mutation_insert_to_hir(self) -> None:
|
128
|
+
"""Test that insert operations convert positions properly."""
|
129
|
+
token = Token(TokenType.KW_INSERT, "Insert", 1, 1)
|
130
|
+
collection = Identifier(token, "items")
|
131
|
+
value = WholeNumberLiteral(token, 15)
|
132
|
+
|
133
|
+
# Test "Insert at position 2" -> index 1
|
134
|
+
mutation = CollectionMutationStatement(token, "insert", collection, value, 2, "numeric")
|
135
|
+
hir = mutation.to_hir()
|
136
|
+
|
137
|
+
assert hir.position == 1 # 1-based to 0-based
|
138
|
+
assert hir.position_type == "numeric"
|
@@ -0,0 +1,142 @@
|
|
1
|
+
from machine_dialect.ast import (
|
2
|
+
BlockStatement,
|
3
|
+
DefineStatement,
|
4
|
+
Identifier,
|
5
|
+
SetStatement,
|
6
|
+
StringLiteral,
|
7
|
+
WholeNumberLiteral,
|
8
|
+
)
|
9
|
+
from machine_dialect.lexer import Token, TokenType
|
10
|
+
|
11
|
+
|
12
|
+
class TestDefineStatement:
|
13
|
+
"""Test DefineStatement AST node."""
|
14
|
+
|
15
|
+
def test_simple_definition(self) -> None:
|
16
|
+
"""Test basic variable definition without default."""
|
17
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
18
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "count", 1, 8), "count")
|
19
|
+
type_spec = ["Whole Number"]
|
20
|
+
|
21
|
+
stmt = DefineStatement(token, name, type_spec)
|
22
|
+
|
23
|
+
assert str(stmt) == "Define `count` as Whole Number."
|
24
|
+
assert stmt.initial_value is None
|
25
|
+
assert stmt.type_spec == ["Whole Number"]
|
26
|
+
|
27
|
+
def test_definition_with_default(self) -> None:
|
28
|
+
"""Test variable definition with default value."""
|
29
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
30
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "message", 1, 8), "message")
|
31
|
+
type_spec = ["Text"]
|
32
|
+
default = StringLiteral(Token(TokenType.LIT_TEXT, '"Hello"', 1, 30), "Hello")
|
33
|
+
|
34
|
+
stmt = DefineStatement(token, name, type_spec, default)
|
35
|
+
|
36
|
+
assert str(stmt) == 'Define `message` as Text (default: _"Hello"_).'
|
37
|
+
assert stmt.initial_value == default
|
38
|
+
|
39
|
+
def test_union_type_definition(self) -> None:
|
40
|
+
"""Test definition with union types."""
|
41
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
42
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "value", 1, 8), "value")
|
43
|
+
type_spec = ["Whole Number", "Text", "Yes/No"]
|
44
|
+
|
45
|
+
stmt = DefineStatement(token, name, type_spec)
|
46
|
+
|
47
|
+
assert str(stmt) == "Define `value` as Whole Number or Text or Yes/No."
|
48
|
+
|
49
|
+
def test_desugar_without_default(self) -> None:
|
50
|
+
"""Test desugaring when no default value."""
|
51
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
52
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "x", 1, 8), "x")
|
53
|
+
type_spec = ["Whole Number"]
|
54
|
+
|
55
|
+
stmt = DefineStatement(token, name, type_spec)
|
56
|
+
desugared = stmt.desugar()
|
57
|
+
|
58
|
+
# Should return self when no default
|
59
|
+
assert desugared is stmt
|
60
|
+
|
61
|
+
def test_desugar_with_default(self) -> None:
|
62
|
+
"""Test desugaring with default value."""
|
63
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
64
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "count", 1, 8), "count")
|
65
|
+
type_spec = ["Whole Number"]
|
66
|
+
default = WholeNumberLiteral(Token(TokenType.LIT_WHOLE_NUMBER, "0", 1, 30), 0)
|
67
|
+
|
68
|
+
stmt = DefineStatement(token, name, type_spec, default)
|
69
|
+
desugared = stmt.desugar()
|
70
|
+
|
71
|
+
# Should return a BlockStatement with Define and Set
|
72
|
+
assert isinstance(desugared, BlockStatement)
|
73
|
+
assert len(desugared.statements) == 2
|
74
|
+
|
75
|
+
# First statement should be Define without default
|
76
|
+
define_stmt = desugared.statements[0]
|
77
|
+
assert isinstance(define_stmt, DefineStatement)
|
78
|
+
assert define_stmt.initial_value is None
|
79
|
+
assert define_stmt.name.value == "count"
|
80
|
+
assert define_stmt.type_spec == ["Whole Number"]
|
81
|
+
|
82
|
+
# Second statement should be Set
|
83
|
+
set_stmt = desugared.statements[1]
|
84
|
+
assert isinstance(set_stmt, SetStatement)
|
85
|
+
assert set_stmt.name is not None
|
86
|
+
assert set_stmt.name.value == "count"
|
87
|
+
assert set_stmt.value == default
|
88
|
+
|
89
|
+
def test_multiple_types_string_representation(self) -> None:
|
90
|
+
"""Test string representation with multiple types."""
|
91
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
92
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "data", 1, 8), "data")
|
93
|
+
type_spec = ["Number", "Text", "Yes/No", "Empty"]
|
94
|
+
|
95
|
+
stmt = DefineStatement(token, name, type_spec)
|
96
|
+
|
97
|
+
assert str(stmt) == "Define `data` as Number or Text or Yes/No or Empty."
|
98
|
+
|
99
|
+
def test_all_type_names(self) -> None:
|
100
|
+
"""Test that all supported type names work correctly."""
|
101
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
102
|
+
|
103
|
+
type_names = [
|
104
|
+
"Text",
|
105
|
+
"Whole Number",
|
106
|
+
"Float",
|
107
|
+
"Number",
|
108
|
+
"Yes/No",
|
109
|
+
"URL",
|
110
|
+
"Date",
|
111
|
+
"DateTime",
|
112
|
+
"Time",
|
113
|
+
"List",
|
114
|
+
"Empty",
|
115
|
+
]
|
116
|
+
|
117
|
+
for type_name in type_names:
|
118
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "var", 1, 8), "var")
|
119
|
+
stmt = DefineStatement(token, name, [type_name])
|
120
|
+
assert str(stmt) == f"Define `var` as {type_name}."
|
121
|
+
|
122
|
+
def test_desugar_preserves_name_reference(self) -> None:
|
123
|
+
"""Test that desugaring preserves the same name reference."""
|
124
|
+
token = Token(TokenType.KW_DEFINE, "Define", 1, 1)
|
125
|
+
name = Identifier(Token(TokenType.MISC_IDENT, "test", 1, 8), "test")
|
126
|
+
type_spec = ["Text"]
|
127
|
+
default = StringLiteral(Token(TokenType.LIT_TEXT, '"value"', 1, 30), "value")
|
128
|
+
|
129
|
+
stmt = DefineStatement(token, name, type_spec, default)
|
130
|
+
desugared = stmt.desugar()
|
131
|
+
|
132
|
+
assert isinstance(desugared, BlockStatement)
|
133
|
+
define_stmt = desugared.statements[0]
|
134
|
+
set_stmt = desugared.statements[1]
|
135
|
+
|
136
|
+
# Both statements should reference the same variable name
|
137
|
+
assert isinstance(define_stmt, DefineStatement)
|
138
|
+
assert isinstance(set_stmt, SetStatement)
|
139
|
+
assert isinstance(define_stmt.name, Identifier)
|
140
|
+
assert isinstance(set_stmt.name, Identifier)
|
141
|
+
assert define_stmt.name.value == "test"
|
142
|
+
assert set_stmt.name.value == "test"
|