|
18 | 18 | from extensions import ASMExtensionError, HookRegistry, RuntimeServices, StepContext, TypeContext, TypeRegistry, TypeSpec, build_default_services |
19 | 19 | from parser import ( |
20 | 20 | Assignment, |
| 21 | + Declaration, |
21 | 22 | Block, |
22 | 23 | BreakStatement, |
23 | 24 | CallArgument, |
|
27 | 28 | ForStatement, |
28 | 29 | ParForStatement, |
29 | 30 | FuncDef, |
| 31 | + LambdaExpression, |
30 | 32 | AsyncStatement, |
31 | 33 | GotoStatement, |
32 | 34 | GotopointStatement, |
@@ -152,6 +154,8 @@ def __init__(self, target: Value) -> None: |
152 | 154 | class Environment: |
153 | 155 | parent: Optional["Environment"] = None |
154 | 156 | values: Dict[str, Value] = field(default_factory=dict) |
| 157 | + # declared but unassigned symbol types |
| 158 | + declared: Dict[str, str] = field(default_factory=dict) |
155 | 159 | frozen: set = field(default_factory=set) |
156 | 160 | permafrozen: set = field(default_factory=set) |
157 | 161 |
|
@@ -213,16 +217,56 @@ def set(self, name: str, value: Value, declared_type: Optional[str] = None) -> N |
213 | 217 | found_env.values[name] = value |
214 | 218 | return |
215 | 219 |
|
| 220 | + # No existing value binding found. Look for a prior type declaration |
| 221 | + # in this environment chain. |
| 222 | + decl_env: Optional[Environment] = None |
| 223 | + decl_type: Optional[str] = None |
| 224 | + env2: Optional[Environment] = self |
| 225 | + while env2 is not None: |
| 226 | + if name in env2.declared: |
| 227 | + decl_env = env2 |
| 228 | + decl_type = env2.declared[name] |
| 229 | + break |
| 230 | + env2 = env2.parent |
| 231 | + |
216 | 232 | if declared_type is None: |
217 | | - raise ASMRuntimeError( |
218 | | - f"Identifier '{name}' must be declared with a type before assignment", |
219 | | - rewrite_rule="ASSIGN", |
220 | | - ) |
| 233 | + # Assignment without inline declaration: require a prior declaration |
| 234 | + if decl_env is None: |
| 235 | + raise ASMRuntimeError( |
| 236 | + f"Identifier '{name}' must be declared with a type before assignment", |
| 237 | + rewrite_rule="ASSIGN", |
| 238 | + ) |
| 239 | + if decl_type != value.type: |
| 240 | + raise ASMRuntimeError( |
| 241 | + f"Assigned value type {value.type} does not match declaration {decl_type}", |
| 242 | + rewrite_rule="ASSIGN", |
| 243 | + ) |
| 244 | + decl_env.values[name] = value |
| 245 | + return |
| 246 | + |
| 247 | + # Assignment with inline declaration: if there is an existing declaration |
| 248 | + # ensure it matches; otherwise record declaration in current env. |
| 249 | + if decl_env is not None: |
| 250 | + if decl_type != declared_type: |
| 251 | + raise ASMRuntimeError( |
| 252 | + f"Type mismatch for '{name}': previously declared as {decl_type}", |
| 253 | + rewrite_rule="ASSIGN", |
| 254 | + ) |
| 255 | + if declared_type != value.type: |
| 256 | + raise ASMRuntimeError( |
| 257 | + f"Assigned value type {value.type} does not match declaration {declared_type}", |
| 258 | + rewrite_rule="ASSIGN", |
| 259 | + ) |
| 260 | + decl_env.values[name] = value |
| 261 | + return |
| 262 | + |
| 263 | + # No prior declaration: record it in this environment and create the value |
221 | 264 | if declared_type != value.type: |
222 | 265 | raise ASMRuntimeError( |
223 | 266 | f"Assigned value type {value.type} does not match declaration {declared_type}", |
224 | 267 | rewrite_rule="ASSIGN", |
225 | 268 | ) |
| 269 | + self.declared[name] = declared_type |
226 | 270 | self.values[name] = value |
227 | 271 |
|
228 | 272 | def get(self, name: str) -> Value: |
@@ -3486,6 +3530,9 @@ def _func_to_str(ctx: TypeContext, v: Value) -> str: |
3486 | 3530 | self._functions_version: int = 0 |
3487 | 3531 | self._call_resolution_cache: Dict[Tuple[str, str, int], Optional[str]] = {} |
3488 | 3532 |
|
| 3533 | + # Monotonic counter for anonymous lambda names (for logs/tracebacks only). |
| 3534 | + self._lambda_counter: int = 0 |
| 3535 | + |
3489 | 3536 | def _mark_functions_changed(self) -> None: |
3490 | 3537 | self._functions_version += 1 |
3491 | 3538 | self._call_resolution_cache.clear() |
@@ -3715,6 +3762,28 @@ def _execute_block(self, statements: List[Statement], env: Environment) -> None: |
3715 | 3762 | def _execute_statement(self, statement: Statement, env: Environment) -> None: |
3716 | 3763 | self._log_step(rule=statement.__class__.__name__, location=statement.location) |
3717 | 3764 | statement_type = type(statement) |
| 3765 | + if statement_type is Declaration: |
| 3766 | + # Record a type declaration without creating a value. Do not |
| 3767 | + # introduce a binding; reads will still raise until the name is |
| 3768 | + # assigned. If a value already exists in the same environment, |
| 3769 | + # ensure the types match. |
| 3770 | + if statement.name in self.functions: |
| 3771 | + raise ASMRuntimeError( |
| 3772 | + f"Identifier '{statement.name}' already bound as function", |
| 3773 | + location=statement.location, |
| 3774 | + rewrite_rule="ASSIGN", |
| 3775 | + ) |
| 3776 | + env_found = env._find_env(statement.name) |
| 3777 | + if env_found is not None: |
| 3778 | + existing = env_found.values.get(statement.name) |
| 3779 | + if existing is not None and existing.type != statement.declared_type: |
| 3780 | + raise ASMRuntimeError( |
| 3781 | + f"Type mismatch for '{statement.name}': previously declared as {existing.type}", |
| 3782 | + location=statement.location, |
| 3783 | + rewrite_rule="ASSIGN", |
| 3784 | + ) |
| 3785 | + env.declared[statement.name] = statement.declared_type |
| 3786 | + return |
3718 | 3787 | if statement_type is Assignment: |
3719 | 3788 | if statement.target in self.functions: |
3720 | 3789 | raise ASMRuntimeError( |
@@ -4496,6 +4565,27 @@ def _evaluate_expression(self, expression: Expression, env: Environment) -> Valu |
4496 | 4565 | location=expression.location, |
4497 | 4566 | rewrite_rule="IDENT", |
4498 | 4567 | ) |
| 4568 | + if expression_type is LambdaExpression: |
| 4569 | + # Create an anonymous function value that closes over the current environment. |
| 4570 | + self._lambda_counter += 1 |
| 4571 | + name = f"<lambda_{self._lambda_counter}>" |
| 4572 | + fn = Function( |
| 4573 | + name=name, |
| 4574 | + params=expression.params, |
| 4575 | + return_type=expression.return_type, |
| 4576 | + body=expression.body, |
| 4577 | + closure=env, |
| 4578 | + ) |
| 4579 | + self._log_step( |
| 4580 | + rule="LAMBDA", |
| 4581 | + location=expression.location, |
| 4582 | + extra={ |
| 4583 | + "name": name, |
| 4584 | + "params": [p.name for p in expression.params], |
| 4585 | + "return_type": expression.return_type, |
| 4586 | + }, |
| 4587 | + ) |
| 4588 | + return Value(TYPE_FUNC, fn) |
4499 | 4589 | if expression_type is CallExpression: |
4500 | 4590 | callee_expr = expression.callee |
4501 | 4591 | callee_ident = callee_expr.name if type(callee_expr) is Identifier else None |
|
0 commit comments