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.
Files changed (268) hide show
  1. machine_dialect/__main__.py +667 -0
  2. machine_dialect/agent/__init__.py +5 -0
  3. machine_dialect/agent/agent.py +360 -0
  4. machine_dialect/ast/__init__.py +95 -0
  5. machine_dialect/ast/ast_node.py +35 -0
  6. machine_dialect/ast/call_expression.py +82 -0
  7. machine_dialect/ast/dict_extraction.py +60 -0
  8. machine_dialect/ast/expressions.py +439 -0
  9. machine_dialect/ast/literals.py +309 -0
  10. machine_dialect/ast/program.py +35 -0
  11. machine_dialect/ast/statements.py +1433 -0
  12. machine_dialect/ast/tests/test_ast_string_representation.py +62 -0
  13. machine_dialect/ast/tests/test_boolean_literal.py +29 -0
  14. machine_dialect/ast/tests/test_collection_hir.py +138 -0
  15. machine_dialect/ast/tests/test_define_statement.py +142 -0
  16. machine_dialect/ast/tests/test_desugar.py +541 -0
  17. machine_dialect/ast/tests/test_foreach_desugar.py +245 -0
  18. machine_dialect/cfg/__init__.py +6 -0
  19. machine_dialect/cfg/config.py +156 -0
  20. machine_dialect/cfg/examples.py +221 -0
  21. machine_dialect/cfg/generate_with_ai.py +187 -0
  22. machine_dialect/cfg/openai_generation.py +200 -0
  23. machine_dialect/cfg/parser.py +94 -0
  24. machine_dialect/cfg/tests/__init__.py +1 -0
  25. machine_dialect/cfg/tests/test_cfg_parser.py +252 -0
  26. machine_dialect/cfg/tests/test_config.py +188 -0
  27. machine_dialect/cfg/tests/test_examples.py +391 -0
  28. machine_dialect/cfg/tests/test_generate_with_ai.py +354 -0
  29. machine_dialect/cfg/tests/test_openai_generation.py +256 -0
  30. machine_dialect/codegen/__init__.py +5 -0
  31. machine_dialect/codegen/bytecode_module.py +89 -0
  32. machine_dialect/codegen/bytecode_serializer.py +300 -0
  33. machine_dialect/codegen/opcodes.py +101 -0
  34. machine_dialect/codegen/register_codegen.py +1996 -0
  35. machine_dialect/codegen/symtab.py +208 -0
  36. machine_dialect/codegen/tests/__init__.py +1 -0
  37. machine_dialect/codegen/tests/test_array_operations_codegen.py +295 -0
  38. machine_dialect/codegen/tests/test_bytecode_serializer.py +185 -0
  39. machine_dialect/codegen/tests/test_register_codegen_ssa.py +324 -0
  40. machine_dialect/codegen/tests/test_symtab.py +418 -0
  41. machine_dialect/codegen/vm_serializer.py +621 -0
  42. machine_dialect/compiler/__init__.py +18 -0
  43. machine_dialect/compiler/compiler.py +197 -0
  44. machine_dialect/compiler/config.py +149 -0
  45. machine_dialect/compiler/context.py +149 -0
  46. machine_dialect/compiler/phases/__init__.py +19 -0
  47. machine_dialect/compiler/phases/bytecode_optimization.py +90 -0
  48. machine_dialect/compiler/phases/codegen.py +40 -0
  49. machine_dialect/compiler/phases/hir_generation.py +39 -0
  50. machine_dialect/compiler/phases/mir_generation.py +86 -0
  51. machine_dialect/compiler/phases/optimization.py +110 -0
  52. machine_dialect/compiler/phases/parsing.py +39 -0
  53. machine_dialect/compiler/pipeline.py +143 -0
  54. machine_dialect/compiler/tests/__init__.py +1 -0
  55. machine_dialect/compiler/tests/test_compiler.py +568 -0
  56. machine_dialect/compiler/vm_runner.py +173 -0
  57. machine_dialect/errors/__init__.py +32 -0
  58. machine_dialect/errors/exceptions.py +369 -0
  59. machine_dialect/errors/messages.py +82 -0
  60. machine_dialect/errors/tests/__init__.py +0 -0
  61. machine_dialect/errors/tests/test_expected_token_errors.py +188 -0
  62. machine_dialect/errors/tests/test_name_errors.py +118 -0
  63. machine_dialect/helpers/__init__.py +0 -0
  64. machine_dialect/helpers/stopwords.py +225 -0
  65. machine_dialect/helpers/validators.py +30 -0
  66. machine_dialect/lexer/__init__.py +9 -0
  67. machine_dialect/lexer/constants.py +23 -0
  68. machine_dialect/lexer/lexer.py +907 -0
  69. machine_dialect/lexer/tests/__init__.py +0 -0
  70. machine_dialect/lexer/tests/helpers.py +86 -0
  71. machine_dialect/lexer/tests/test_apostrophe_identifiers.py +122 -0
  72. machine_dialect/lexer/tests/test_backtick_identifiers.py +140 -0
  73. machine_dialect/lexer/tests/test_boolean_literals.py +108 -0
  74. machine_dialect/lexer/tests/test_case_insensitive_keywords.py +188 -0
  75. machine_dialect/lexer/tests/test_comments.py +200 -0
  76. machine_dialect/lexer/tests/test_double_asterisk_keywords.py +127 -0
  77. machine_dialect/lexer/tests/test_lexer_position.py +113 -0
  78. machine_dialect/lexer/tests/test_list_tokens.py +282 -0
  79. machine_dialect/lexer/tests/test_stopwords.py +80 -0
  80. machine_dialect/lexer/tests/test_strict_equality.py +129 -0
  81. machine_dialect/lexer/tests/test_token.py +41 -0
  82. machine_dialect/lexer/tests/test_tokenization.py +294 -0
  83. machine_dialect/lexer/tests/test_underscore_literals.py +343 -0
  84. machine_dialect/lexer/tests/test_url_literals.py +169 -0
  85. machine_dialect/lexer/tokens.py +487 -0
  86. machine_dialect/linter/__init__.py +10 -0
  87. machine_dialect/linter/__main__.py +144 -0
  88. machine_dialect/linter/linter.py +154 -0
  89. machine_dialect/linter/rules/__init__.py +8 -0
  90. machine_dialect/linter/rules/base.py +112 -0
  91. machine_dialect/linter/rules/statement_termination.py +99 -0
  92. machine_dialect/linter/tests/__init__.py +1 -0
  93. machine_dialect/linter/tests/mdrules/__init__.py +0 -0
  94. machine_dialect/linter/tests/mdrules/test_md101_statement_termination.py +181 -0
  95. machine_dialect/linter/tests/test_linter.py +81 -0
  96. machine_dialect/linter/tests/test_rules.py +110 -0
  97. machine_dialect/linter/tests/test_violations.py +71 -0
  98. machine_dialect/linter/violations.py +51 -0
  99. machine_dialect/mir/__init__.py +69 -0
  100. machine_dialect/mir/analyses/__init__.py +20 -0
  101. machine_dialect/mir/analyses/alias_analysis.py +315 -0
  102. machine_dialect/mir/analyses/dominance_analysis.py +49 -0
  103. machine_dialect/mir/analyses/escape_analysis.py +286 -0
  104. machine_dialect/mir/analyses/loop_analysis.py +272 -0
  105. machine_dialect/mir/analyses/tests/test_type_analysis.py +736 -0
  106. machine_dialect/mir/analyses/type_analysis.py +448 -0
  107. machine_dialect/mir/analyses/use_def_chains.py +232 -0
  108. machine_dialect/mir/basic_block.py +385 -0
  109. machine_dialect/mir/dataflow.py +445 -0
  110. machine_dialect/mir/debug_info.py +208 -0
  111. machine_dialect/mir/hir_to_mir.py +1738 -0
  112. machine_dialect/mir/mir_dumper.py +366 -0
  113. machine_dialect/mir/mir_function.py +167 -0
  114. machine_dialect/mir/mir_instructions.py +1877 -0
  115. machine_dialect/mir/mir_interpreter.py +556 -0
  116. machine_dialect/mir/mir_module.py +225 -0
  117. machine_dialect/mir/mir_printer.py +480 -0
  118. machine_dialect/mir/mir_transformer.py +410 -0
  119. machine_dialect/mir/mir_types.py +367 -0
  120. machine_dialect/mir/mir_validation.py +455 -0
  121. machine_dialect/mir/mir_values.py +268 -0
  122. machine_dialect/mir/optimization_config.py +233 -0
  123. machine_dialect/mir/optimization_pass.py +251 -0
  124. machine_dialect/mir/optimization_pipeline.py +355 -0
  125. machine_dialect/mir/optimizations/__init__.py +84 -0
  126. machine_dialect/mir/optimizations/algebraic_simplification.py +733 -0
  127. machine_dialect/mir/optimizations/branch_prediction.py +372 -0
  128. machine_dialect/mir/optimizations/constant_propagation.py +634 -0
  129. machine_dialect/mir/optimizations/cse.py +398 -0
  130. machine_dialect/mir/optimizations/dce.py +288 -0
  131. machine_dialect/mir/optimizations/inlining.py +551 -0
  132. machine_dialect/mir/optimizations/jump_threading.py +487 -0
  133. machine_dialect/mir/optimizations/licm.py +405 -0
  134. machine_dialect/mir/optimizations/loop_unrolling.py +366 -0
  135. machine_dialect/mir/optimizations/strength_reduction.py +422 -0
  136. machine_dialect/mir/optimizations/tail_call.py +207 -0
  137. machine_dialect/mir/optimizations/tests/test_loop_unrolling.py +483 -0
  138. machine_dialect/mir/optimizations/type_narrowing.py +397 -0
  139. machine_dialect/mir/optimizations/type_specialization.py +447 -0
  140. machine_dialect/mir/optimizations/type_specific.py +906 -0
  141. machine_dialect/mir/optimize_mir.py +89 -0
  142. machine_dialect/mir/pass_manager.py +391 -0
  143. machine_dialect/mir/profiling/__init__.py +26 -0
  144. machine_dialect/mir/profiling/profile_collector.py +318 -0
  145. machine_dialect/mir/profiling/profile_data.py +372 -0
  146. machine_dialect/mir/profiling/profile_reader.py +272 -0
  147. machine_dialect/mir/profiling/profile_writer.py +226 -0
  148. machine_dialect/mir/register_allocation.py +302 -0
  149. machine_dialect/mir/reporting/__init__.py +17 -0
  150. machine_dialect/mir/reporting/optimization_reporter.py +314 -0
  151. machine_dialect/mir/reporting/report_formatter.py +289 -0
  152. machine_dialect/mir/ssa_construction.py +342 -0
  153. machine_dialect/mir/tests/__init__.py +1 -0
  154. machine_dialect/mir/tests/test_algebraic_associativity.py +204 -0
  155. machine_dialect/mir/tests/test_algebraic_complex_patterns.py +221 -0
  156. machine_dialect/mir/tests/test_algebraic_division.py +126 -0
  157. machine_dialect/mir/tests/test_algebraic_simplification.py +863 -0
  158. machine_dialect/mir/tests/test_basic_block.py +425 -0
  159. machine_dialect/mir/tests/test_branch_prediction.py +459 -0
  160. machine_dialect/mir/tests/test_call_lowering.py +168 -0
  161. machine_dialect/mir/tests/test_collection_lowering.py +604 -0
  162. machine_dialect/mir/tests/test_cross_block_constant_propagation.py +255 -0
  163. machine_dialect/mir/tests/test_custom_passes.py +166 -0
  164. machine_dialect/mir/tests/test_debug_info.py +285 -0
  165. machine_dialect/mir/tests/test_dict_extraction_lowering.py +192 -0
  166. machine_dialect/mir/tests/test_dictionary_lowering.py +299 -0
  167. machine_dialect/mir/tests/test_double_negation.py +231 -0
  168. machine_dialect/mir/tests/test_escape_analysis.py +233 -0
  169. machine_dialect/mir/tests/test_hir_to_mir.py +465 -0
  170. machine_dialect/mir/tests/test_hir_to_mir_complete.py +389 -0
  171. machine_dialect/mir/tests/test_hir_to_mir_simple.py +130 -0
  172. machine_dialect/mir/tests/test_inlining.py +435 -0
  173. machine_dialect/mir/tests/test_licm.py +472 -0
  174. machine_dialect/mir/tests/test_mir_dumper.py +313 -0
  175. machine_dialect/mir/tests/test_mir_instructions.py +445 -0
  176. machine_dialect/mir/tests/test_mir_module.py +860 -0
  177. machine_dialect/mir/tests/test_mir_printer.py +387 -0
  178. machine_dialect/mir/tests/test_mir_types.py +123 -0
  179. machine_dialect/mir/tests/test_mir_types_enhanced.py +132 -0
  180. machine_dialect/mir/tests/test_mir_validation.py +378 -0
  181. machine_dialect/mir/tests/test_mir_values.py +168 -0
  182. machine_dialect/mir/tests/test_one_based_indexing.py +202 -0
  183. machine_dialect/mir/tests/test_optimization_helpers.py +60 -0
  184. machine_dialect/mir/tests/test_optimization_pipeline.py +554 -0
  185. machine_dialect/mir/tests/test_optimization_reporter.py +318 -0
  186. machine_dialect/mir/tests/test_pass_manager.py +294 -0
  187. machine_dialect/mir/tests/test_pass_registration.py +64 -0
  188. machine_dialect/mir/tests/test_profiling.py +356 -0
  189. machine_dialect/mir/tests/test_register_allocation.py +307 -0
  190. machine_dialect/mir/tests/test_report_formatters.py +372 -0
  191. machine_dialect/mir/tests/test_ssa_construction.py +433 -0
  192. machine_dialect/mir/tests/test_tail_call.py +236 -0
  193. machine_dialect/mir/tests/test_type_annotated_instructions.py +192 -0
  194. machine_dialect/mir/tests/test_type_narrowing.py +277 -0
  195. machine_dialect/mir/tests/test_type_specialization.py +421 -0
  196. machine_dialect/mir/tests/test_type_specific_optimization.py +545 -0
  197. machine_dialect/mir/tests/test_type_specific_optimization_advanced.py +382 -0
  198. machine_dialect/mir/type_inference.py +368 -0
  199. machine_dialect/parser/__init__.py +12 -0
  200. machine_dialect/parser/enums.py +45 -0
  201. machine_dialect/parser/parser.py +3655 -0
  202. machine_dialect/parser/protocols.py +11 -0
  203. machine_dialect/parser/symbol_table.py +169 -0
  204. machine_dialect/parser/tests/__init__.py +0 -0
  205. machine_dialect/parser/tests/helper_functions.py +193 -0
  206. machine_dialect/parser/tests/test_action_statements.py +334 -0
  207. machine_dialect/parser/tests/test_boolean_literal_expressions.py +152 -0
  208. machine_dialect/parser/tests/test_call_statements.py +154 -0
  209. machine_dialect/parser/tests/test_call_statements_errors.py +187 -0
  210. machine_dialect/parser/tests/test_collection_mutations.py +264 -0
  211. machine_dialect/parser/tests/test_conditional_expressions.py +343 -0
  212. machine_dialect/parser/tests/test_define_integration.py +468 -0
  213. machine_dialect/parser/tests/test_define_statements.py +311 -0
  214. machine_dialect/parser/tests/test_dict_extraction.py +115 -0
  215. machine_dialect/parser/tests/test_empty_literal.py +155 -0
  216. machine_dialect/parser/tests/test_float_literal_expressions.py +163 -0
  217. machine_dialect/parser/tests/test_identifier_expressions.py +57 -0
  218. machine_dialect/parser/tests/test_if_empty_block.py +61 -0
  219. machine_dialect/parser/tests/test_if_statements.py +299 -0
  220. machine_dialect/parser/tests/test_illegal_tokens.py +86 -0
  221. machine_dialect/parser/tests/test_infix_expressions.py +680 -0
  222. machine_dialect/parser/tests/test_integer_literal_expressions.py +137 -0
  223. machine_dialect/parser/tests/test_interaction_statements.py +269 -0
  224. machine_dialect/parser/tests/test_list_literals.py +277 -0
  225. machine_dialect/parser/tests/test_no_none_in_ast.py +94 -0
  226. machine_dialect/parser/tests/test_panic_mode_recovery.py +171 -0
  227. machine_dialect/parser/tests/test_parse_errors.py +114 -0
  228. machine_dialect/parser/tests/test_possessive_syntax.py +182 -0
  229. machine_dialect/parser/tests/test_prefix_expressions.py +415 -0
  230. machine_dialect/parser/tests/test_program.py +13 -0
  231. machine_dialect/parser/tests/test_return_statements.py +89 -0
  232. machine_dialect/parser/tests/test_set_statements.py +152 -0
  233. machine_dialect/parser/tests/test_strict_equality.py +258 -0
  234. machine_dialect/parser/tests/test_symbol_table.py +217 -0
  235. machine_dialect/parser/tests/test_url_literal_expressions.py +209 -0
  236. machine_dialect/parser/tests/test_utility_statements.py +423 -0
  237. machine_dialect/parser/token_buffer.py +159 -0
  238. machine_dialect/repl/__init__.py +3 -0
  239. machine_dialect/repl/repl.py +426 -0
  240. machine_dialect/repl/tests/__init__.py +0 -0
  241. machine_dialect/repl/tests/test_repl.py +606 -0
  242. machine_dialect/semantic/__init__.py +12 -0
  243. machine_dialect/semantic/analyzer.py +906 -0
  244. machine_dialect/semantic/error_messages.py +189 -0
  245. machine_dialect/semantic/tests/__init__.py +1 -0
  246. machine_dialect/semantic/tests/test_analyzer.py +364 -0
  247. machine_dialect/semantic/tests/test_error_messages.py +104 -0
  248. machine_dialect/tests/edge_cases/__init__.py +10 -0
  249. machine_dialect/tests/edge_cases/test_boundary_access.py +256 -0
  250. machine_dialect/tests/edge_cases/test_empty_collections.py +166 -0
  251. machine_dialect/tests/edge_cases/test_invalid_operations.py +243 -0
  252. machine_dialect/tests/edge_cases/test_named_list_edge_cases.py +295 -0
  253. machine_dialect/tests/edge_cases/test_nested_structures.py +313 -0
  254. machine_dialect/tests/edge_cases/test_type_mixing.py +277 -0
  255. machine_dialect/tests/integration/test_array_operations_emulation.py +248 -0
  256. machine_dialect/tests/integration/test_list_compilation.py +395 -0
  257. machine_dialect/tests/integration/test_lists_and_dictionaries.py +322 -0
  258. machine_dialect/type_checking/__init__.py +21 -0
  259. machine_dialect/type_checking/tests/__init__.py +1 -0
  260. machine_dialect/type_checking/tests/test_type_system.py +230 -0
  261. machine_dialect/type_checking/type_system.py +270 -0
  262. machine_dialect-0.1.0a1.dist-info/METADATA +128 -0
  263. machine_dialect-0.1.0a1.dist-info/RECORD +268 -0
  264. machine_dialect-0.1.0a1.dist-info/WHEEL +5 -0
  265. machine_dialect-0.1.0a1.dist-info/entry_points.txt +3 -0
  266. machine_dialect-0.1.0a1.dist-info/licenses/LICENSE +201 -0
  267. machine_dialect-0.1.0a1.dist-info/top_level.txt +2 -0
  268. machine_dialect_vm/__init__.pyi +15 -0
@@ -0,0 +1,439 @@
1
+ """AST nodes for expression types in Machine Dialect™.
2
+
3
+ This module defines the expression nodes used in the Abstract Syntax Tree (AST)
4
+ for Machine Dialect™. Expressions are constructs that can be evaluated to produce
5
+ a value, unlike statements which perform actions.
6
+
7
+ Expressions include:
8
+ - Identifier: Variable names and references
9
+ - Literals: Numbers, strings, booleans (to be added)
10
+ - Operations: Mathematical, logical, and other operations (to be added)
11
+ """
12
+
13
+ from typing import Any
14
+
15
+ from machine_dialect.ast import ASTNode
16
+ from machine_dialect.lexer import Token
17
+
18
+
19
+ class Expression(ASTNode):
20
+ """Base class for all expression nodes in the AST.
21
+
22
+ An expression represents a construct that can be evaluated to produce
23
+ a value. This includes identifiers, literals, operations, and function calls.
24
+ """
25
+
26
+ def __init__(self, token: Token) -> None:
27
+ """Initialize an Expression node.
28
+
29
+ Args:
30
+ token: The token that begins this expression.
31
+ """
32
+ self.token = token
33
+
34
+ def desugar(self) -> "Expression":
35
+ """Default desugar for expressions returns self.
36
+
37
+ Returns:
38
+ The expression unchanged.
39
+ """
40
+ return self
41
+
42
+
43
+ class Identifier(Expression):
44
+ """An identifier expression representing a variable or name.
45
+
46
+ Identifiers are names that refer to variables, functions, or other
47
+ named entities in the program. In Machine Dialect™, identifiers can
48
+ be written with or without backticks (e.g., `x` or x).
49
+
50
+ Attributes:
51
+ value: The string value of the identifier name.
52
+ """
53
+
54
+ def __init__(self, token: Token, value: str) -> None:
55
+ """Initialize an Identifier node.
56
+
57
+ Args:
58
+ token: The token containing the identifier.
59
+ value: The string value of the identifier name.
60
+ """
61
+ super().__init__(token)
62
+ self.value = value
63
+
64
+ def __str__(self) -> str:
65
+ """Return the string representation of the identifier.
66
+
67
+ Returns:
68
+ The identifier wrapped in backticks, e.g., "`variable`".
69
+ """
70
+ return f"`{self.value}`"
71
+
72
+ def desugar(self) -> "Identifier":
73
+ """Identifiers are already in simplest form.
74
+
75
+ Returns:
76
+ Self unchanged.
77
+ """
78
+ return self
79
+
80
+
81
+ class PrefixExpression(Expression):
82
+ """A prefix expression with an operator applied to an expression.
83
+
84
+ Prefix expressions consist of a prefix operator followed by an expression.
85
+ Examples include negative numbers (-42), boolean negation (not True),
86
+ and other unary operations.
87
+
88
+ Attributes:
89
+ operator: The prefix operator as a string (e.g., "-", "not").
90
+ right: The expression that the operator is applied to.
91
+ """
92
+
93
+ def __init__(self, token: Token, operator: str) -> None:
94
+ """Initialize a PrefixExpression node.
95
+
96
+ Args:
97
+ token: The token containing the prefix operator.
98
+ operator: The operator string (e.g., "-", "not").
99
+ """
100
+ super().__init__(token)
101
+ self.operator = operator
102
+ self.right: Expression | None = None
103
+
104
+ def __str__(self) -> str:
105
+ """Return the string representation of the prefix expression.
106
+
107
+ Returns:
108
+ The expression in the format "(operator right)", e.g., "(-42)".
109
+ """
110
+ if self.operator == "not":
111
+ return f"({self.operator} {self.right})"
112
+ return f"({self.operator}{self.right})"
113
+
114
+ def desugar(self) -> "PrefixExpression":
115
+ """Desugar prefix expression by recursively desugaring operand.
116
+
117
+ Returns:
118
+ A new PrefixExpression with desugared right operand.
119
+ """
120
+ if self.right is None:
121
+ return self
122
+
123
+ desugared = PrefixExpression(self.token, self.operator)
124
+ desugared.right = self.right.desugar()
125
+ return desugared
126
+
127
+
128
+ class InfixExpression(Expression):
129
+ """An infix expression with an operator between two expressions.
130
+
131
+ Infix expressions consist of a left expression, an infix operator, and a
132
+ right expression. Examples include arithmetic (5 + 3), comparisons (x > y),
133
+ and logical operations (a and b).
134
+
135
+ Attributes:
136
+ left: The left operand expression.
137
+ operator: The infix operator as a string (e.g., "+", "==", "and").
138
+ right: The right operand expression.
139
+ """
140
+
141
+ # Map token types to canonical operator strings
142
+ # Used by both desugar and canonicalize to normalize operators
143
+ _OPERATOR_MAP = None
144
+
145
+ @classmethod
146
+ def _get_operator_map(cls) -> dict[Any, str]:
147
+ """Get the operator mapping, creating it lazily if needed."""
148
+ if cls._OPERATOR_MAP is None:
149
+ from machine_dialect.lexer import TokenType
150
+
151
+ cls._OPERATOR_MAP = {
152
+ TokenType.OP_PLUS: "+",
153
+ TokenType.OP_MINUS: "-",
154
+ TokenType.OP_STAR: "*",
155
+ TokenType.OP_DIVISION: "/",
156
+ TokenType.OP_EQ: "==",
157
+ TokenType.OP_NOT_EQ: "!=",
158
+ TokenType.OP_STRICT_EQ: "===",
159
+ TokenType.OP_STRICT_NOT_EQ: "!==",
160
+ TokenType.OP_LT: "<",
161
+ TokenType.OP_GT: ">",
162
+ TokenType.OP_LTE: "<=",
163
+ TokenType.OP_GTE: ">=",
164
+ TokenType.OP_CARET: "^",
165
+ }
166
+ return cls._OPERATOR_MAP
167
+
168
+ def __init__(self, token: Token, operator: str, left: Expression) -> None:
169
+ """Initialize an InfixExpression node.
170
+
171
+ Args:
172
+ token: The token containing the infix operator.
173
+ operator: The operator string (e.g., "+", "-", "==").
174
+ left: The left-hand expression.
175
+ """
176
+ super().__init__(token)
177
+ self.operator = operator
178
+ self.left = left
179
+ self.right: Expression | None = None
180
+
181
+ def __str__(self) -> str:
182
+ """Return the string representation of the infix expression.
183
+
184
+ Returns:
185
+ The expression in the format "(left operator right)", e.g., "(5 + 3)".
186
+ """
187
+ return f"({self.left} {self.operator} {self.right})"
188
+
189
+ def desugar(self) -> "InfixExpression":
190
+ """Desugar infix expression by normalizing operators and recursively desugaring operands.
191
+
192
+ Normalizes operators based on their token type to symbolic equivalents.
193
+
194
+ Returns:
195
+ A new InfixExpression with normalized operator and desugared operands.
196
+ """
197
+ # Get the shared operator mapping
198
+ operator_map = self._get_operator_map()
199
+
200
+ # Normalize the operator based on token type
201
+ normalized_op = operator_map.get(self.token.type, self.operator)
202
+
203
+ # Create new expression with normalized operator
204
+ desugared = InfixExpression(self.token, normalized_op, self.left.desugar())
205
+ if self.right:
206
+ desugared.right = self.right.desugar()
207
+ return desugared
208
+
209
+
210
+ class Arguments(Expression):
211
+ """Represents arguments for a function call.
212
+
213
+ Arguments can be positional, named (keyword), or a mix of both.
214
+ Positional arguments must come before named arguments.
215
+
216
+ Attributes:
217
+ positional: List of positional argument expressions.
218
+ named: List of tuples (name, value) for named arguments.
219
+ """
220
+
221
+ def __init__(self, token: Token) -> None:
222
+ """Initialize an Arguments node.
223
+
224
+ Args:
225
+ token: The token that begins the arguments (usually 'with').
226
+ """
227
+ super().__init__(token)
228
+ self.positional: list[Expression] = []
229
+ self.named: list[tuple[Identifier, Expression]] = []
230
+
231
+ def __str__(self) -> str:
232
+ """Return the string representation of the arguments.
233
+
234
+ Returns:
235
+ A comma-separated list of arguments.
236
+ """
237
+ parts = []
238
+ # Add positional arguments
239
+ for arg in self.positional:
240
+ parts.append(str(arg))
241
+ # Add named arguments
242
+ for name, value in self.named:
243
+ parts.append(f"{name}: {value}")
244
+ return ", ".join(parts)
245
+
246
+ def desugar(self) -> "Arguments":
247
+ """Desugar arguments by recursively desugaring all argument expressions.
248
+
249
+ Returns:
250
+ A new Arguments node with desugared expressions.
251
+ """
252
+ desugared = Arguments(self.token)
253
+ desugared.positional = [arg.desugar() for arg in self.positional]
254
+ desugared.named = [
255
+ (name.desugar() if isinstance(name, Expression) else name, value.desugar()) for name, value in self.named
256
+ ]
257
+ return desugared
258
+
259
+
260
+ class ConditionalExpression(Expression):
261
+ """A conditional (ternary) expression.
262
+
263
+ Conditional expressions evaluate to one of two values based on a condition.
264
+ In Machine Dialect™, they follow the pattern:
265
+ "value_if_true if/when condition, else/otherwise value_if_false"
266
+
267
+ Attributes:
268
+ condition: The boolean expression to evaluate.
269
+ consequence: The expression to return if condition is true.
270
+ alternative: The expression to return if condition is false.
271
+ """
272
+
273
+ def __init__(self, token: Token, consequence: Expression) -> None:
274
+ """Initialize a ConditionalExpression node.
275
+
276
+ Args:
277
+ token: The token where the expression begins.
278
+ consequence: The expression to return if condition is true.
279
+ """
280
+ super().__init__(token)
281
+ self.consequence = consequence
282
+ self.condition: Expression | None = None
283
+ self.alternative: Expression | None = None
284
+
285
+ def __str__(self) -> str:
286
+ """Return the string representation of the conditional expression.
287
+
288
+ Returns:
289
+ The expression in the format "(consequence if condition else alternative)".
290
+ """
291
+ return f"({self.consequence} if {self.condition} else {self.alternative})"
292
+
293
+ def desugar(self) -> "ConditionalExpression":
294
+ """Desugar conditional expression by recursively desugaring all parts.
295
+
296
+ Returns:
297
+ A new ConditionalExpression with desugared components.
298
+ """
299
+ desugared = ConditionalExpression(self.token, self.consequence.desugar())
300
+ if self.condition:
301
+ desugared.condition = self.condition.desugar()
302
+ if self.alternative:
303
+ desugared.alternative = self.alternative.desugar()
304
+ return desugared
305
+
306
+
307
+ class CollectionAccessExpression(Expression):
308
+ """Access collection element by index or name.
309
+
310
+ Supports multiple access patterns:
311
+ - Ordinal access: `the first item of list`, `the second item of list`
312
+ - Numeric access: `item _5_ of list` (one-based indexing)
313
+ - Property access: `dict`'s name` for named lists
314
+ - Name access: Direct name access for dictionaries
315
+
316
+ Attributes:
317
+ collection: The collection being accessed.
318
+ accessor: The index, ordinal, or name used for access.
319
+ access_type: Type of access ('ordinal', 'numeric', 'name', 'property').
320
+ """
321
+
322
+ def __init__(
323
+ self, token: Token, collection: Expression, accessor: Expression | str | int, access_type: str
324
+ ) -> None:
325
+ """Initialize a CollectionAccessExpression.
326
+
327
+ Args:
328
+ token: The token that begins this expression.
329
+ collection: The collection being accessed.
330
+ accessor: The index, ordinal, or name used for access.
331
+ access_type: Type of access ('ordinal', 'numeric', 'name', 'property').
332
+ """
333
+ super().__init__(token)
334
+ self.collection = collection
335
+ self.accessor = accessor
336
+ self.access_type = access_type
337
+
338
+ def __str__(self) -> str:
339
+ """Return the string representation of the collection access.
340
+
341
+ Returns:
342
+ A string representing the collection access pattern.
343
+ """
344
+ if self.access_type == "ordinal":
345
+ return f"the {self.accessor} item of {self.collection}"
346
+ elif self.access_type == "numeric":
347
+ return f"item _{self.accessor}_ of {self.collection}"
348
+ elif self.access_type == "property":
349
+ return f"{self.collection}'s {self.accessor}"
350
+ else: # name
351
+ return f"{self.collection}[{self.accessor}]"
352
+
353
+ def desugar(self) -> "CollectionAccessExpression":
354
+ """Desugar collection access by recursively desugaring the collection.
355
+
356
+ Returns:
357
+ A new CollectionAccessExpression with desugared collection.
358
+ """
359
+ desugared = CollectionAccessExpression(self.token, self.collection.desugar(), self.accessor, self.access_type)
360
+ # If accessor is an expression, desugar it too
361
+ if isinstance(self.accessor, Expression):
362
+ desugared.accessor = self.accessor.desugar()
363
+ return desugared
364
+
365
+ def to_hir(self) -> "CollectionAccessExpression":
366
+ """Convert collection access to HIR representation.
367
+
368
+ Converts one-based user indices to zero-based for internal use.
369
+
370
+ Returns:
371
+ HIR representation with adjusted indices.
372
+ """
373
+ # Convert collection to HIR
374
+ hir_collection = self.collection.to_hir() if hasattr(self.collection, "to_hir") else self.collection
375
+
376
+ # Process accessor based on type
377
+ hir_accessor = self.accessor
378
+ if self.access_type == "ordinal":
379
+ # Convert ordinals to zero-based numeric indices
380
+ ordinal_map = {"first": 0, "second": 1, "third": 2}
381
+ if isinstance(self.accessor, str) and self.accessor.lower() in ordinal_map:
382
+ hir_accessor = ordinal_map[self.accessor.lower()]
383
+ # Change type to numeric since we converted
384
+ return CollectionAccessExpression(self.token, hir_collection, hir_accessor, "numeric")
385
+ elif self.accessor == "last":
386
+ # Keep "last" as special case - will be handled in MIR generation
387
+ hir_accessor = "last"
388
+ elif self.access_type == "numeric":
389
+ # Convert one-based to zero-based index
390
+ if isinstance(self.accessor, int):
391
+ hir_accessor = self.accessor - 1 # Convert to 0-based
392
+ elif isinstance(self.accessor, Expression):
393
+ # For expressions, we'll need to handle this in MIR generation
394
+ # by subtracting 1 at runtime
395
+ hir_accessor = self.accessor.to_hir() if hasattr(self.accessor, "to_hir") else self.accessor
396
+ elif isinstance(self.accessor, Expression):
397
+ hir_accessor = self.accessor.to_hir() if hasattr(self.accessor, "to_hir") else self.accessor
398
+
399
+ return CollectionAccessExpression(self.token, hir_collection, hir_accessor, self.access_type)
400
+
401
+
402
+ class ErrorExpression(Expression):
403
+ """An expression that failed to parse correctly.
404
+
405
+ ErrorExpressions preserve the AST structure even when parsing fails,
406
+ allowing the parser to continue and collect multiple errors. They
407
+ contain information about what went wrong during parsing.
408
+
409
+ Attributes:
410
+ message: Human-readable error message describing what went wrong.
411
+ """
412
+
413
+ def __init__(self, token: Token, message: str = "") -> None:
414
+ """Initialize an ErrorExpression node.
415
+
416
+ Args:
417
+ token: The token where the error began.
418
+ message: Error message describing the parsing failure.
419
+ """
420
+ super().__init__(token)
421
+ self.message = message
422
+
423
+ def __str__(self) -> str:
424
+ """Return the string representation of the error expression.
425
+
426
+ Returns:
427
+ A string like "<error: message>".
428
+ """
429
+ if self.message:
430
+ return f"<error: {self.message}>"
431
+ return "<error>"
432
+
433
+ def desugar(self) -> "ErrorExpression":
434
+ """Error expressions remain unchanged.
435
+
436
+ Returns:
437
+ Self unchanged.
438
+ """
439
+ return self