feat(minga-core): cierre del α-hashing de Rust — if let, while let, let-else, or-pattern, let-chains
Cierra los 5 pendientes documentados en alpha.rs. El hash
alpha-equivalente ahora es estable bajo renombre de TODOS los binders
de Rust, no solo los del MVP (params, let, for, match arms).
Pendientes cerrados:
- if let X = expr { ... }: if_expression detecta let_condition en
condition, recolecta binders del pattern, los propaga al
consequence. Alternative (else) no los ve.
- while let X = expr { ... }: simetrico al if-let, propaga al body.
- let-else: ya funcionaba por construccion (alternative procesado en
scope antes que feed_block extienda con los binders).
- or_pattern: ambos lados introducen los mismos binders (Rust
enforcement). Emit recorre todos, collect solo el primero para no
duplicar.
- let-chains (if let X = a && let Y = b): collect_let_condition_binders
recursa en el arbol del condition capturando todos los let_condition
vivan donde vivan (binary_expression u otros).
Helper nuevo: feed_let_condition para que el pattern del let_condition
pase por feed_pattern (que distingue binders de constructors). Sin
esto, los identifiers del pattern se hasheaban como variables libres
y Some(x) != Some(y) aun teniendo el mismo significado.
Tests: 6 nuevos en alpha_invariants:
- alpha_if_let_binder_rename_invariant
- alpha_if_let_else_does_not_see_binder
- alpha_while_let_binder_rename_invariant
- alpha_let_else_binder_rename_invariant
- alpha_or_pattern_binder_rename_invariant
- alpha_let_chain_binders_propagate_to_consequence
- alpha_if_let_does_not_collide_with_unrelated_program (negativo)
36 tests alpha verdes. 115 totales en minga-core.
Refactorings del tipo "rename variable" no inflan el storage del
repo. Pendiente futuro: alpha-hashing per-language (Python, TS, JS,
Go) — cada uno requiere conocimiento profundo de su gramatica.
This commit is contained in:
@@ -6,6 +6,66 @@ ratio/diff ver `git show <sha>`.
|
||||
|
||||
## 2026-05-09
|
||||
|
||||
### feat(minga-core): cierre del α-hashing de Rust — if let, while let, let-else, or-pattern, let-chains
|
||||
Cierra los 5 pendientes documentados en `alpha.rs`. El hash
|
||||
α-equivalente ahora es estable bajo renombre de TODOS los binders
|
||||
de Rust, no sólo los del MVP (parámetros, let, for, match arms).
|
||||
|
||||
Pendientes cerrados:
|
||||
- **`if let X = expr { ... }`**: `if_expression` detecta
|
||||
`let_condition` en su `condition`, recolecta los binders del
|
||||
pattern, los propaga al `consequence`. El `alternative` (else)
|
||||
NO los ve.
|
||||
- **`while let X = expr { ... }`**: simétrico al if-let, propaga al
|
||||
`body`. El `condition` mismo se evalúa con scope previo (los
|
||||
binders todavía no existen).
|
||||
- **`let-else`**: `let_declaration` con campo `alternative`. El
|
||||
alternative se procesa con el scope ANTES de los binders (ya
|
||||
funcionaba: `feed_let` llama `feed` para no-pattern children con
|
||||
el scope actual; `feed_block` extiende el scope DESPUÉS de
|
||||
`feed_let`).
|
||||
- **`or_pattern`**: en `pat1 | pat2` (Rust enforcement: ambos lados
|
||||
introducen los mismos binders). Para emit, recorremos cada lado
|
||||
con `feed_pattern`. Para collect, sólo el primer lado — iterar
|
||||
todos duplicaría binders y rompería los índices de Bruijn.
|
||||
- **let-chains** (`if let X = a && let Y = b { ... }`): el
|
||||
`collect_let_condition_binders` recursa en el árbol del condition,
|
||||
capturando todos los `let_condition` (vivan dentro de
|
||||
`binary_expression` u otros nodos). Ambos binders quedan en scope
|
||||
del consequence.
|
||||
|
||||
Helper nuevo: `feed_let_condition` para que el `pattern` del
|
||||
let_condition pase por `feed_pattern` (que distingue binders vs
|
||||
constructors). Sin esto, los identifiers del pattern se hasheaban
|
||||
como variables libres y `Some(x)` ≠ `Some(y)` aún teniendo el
|
||||
mismo significado.
|
||||
|
||||
Tests: 6 nuevos en `tests/alpha_invariants.rs`:
|
||||
- `alpha_if_let_binder_rename_invariant`
|
||||
- `alpha_if_let_else_does_not_see_binder` (sanity)
|
||||
- `alpha_while_let_binder_rename_invariant`
|
||||
- `alpha_let_else_binder_rename_invariant`
|
||||
- `alpha_or_pattern_binder_rename_invariant`
|
||||
- `alpha_let_chain_binders_propagate_to_consequence`
|
||||
- `alpha_if_let_does_not_collide_with_unrelated_program` (negativo:
|
||||
programas distintos NO deben dar el mismo hash)
|
||||
|
||||
36 tests α verdes (eran 30). 115 tests totales en minga-core.
|
||||
|
||||
Lo que esto significa: el hash α-equivalente de Rust en minga es
|
||||
**completo** — cubre todos los constructos del lenguaje que
|
||||
introducen bindings. Dos versiones del mismo programa que difieren
|
||||
sólo en nombres de variables (incluyendo en `if let`, `while let`,
|
||||
`or-pattern`, etc.) producen el mismo hash y por tanto la misma
|
||||
identidad CAS. Refactorings del tipo "rename variable" no inflan
|
||||
el storage del repo.
|
||||
|
||||
Pendientes futuros:
|
||||
- α-hashing per-language (Python: def/lambda/comprehensions; TS/JS:
|
||||
function/arrow/destructuring; Go: func/closure). Cada uno
|
||||
requiere conocimiento profundo de la gramática y tests
|
||||
exhaustivos. Plantilla genérica no aplica.
|
||||
|
||||
### feat(minga): multi-lenguaje en parser — Python, TypeScript, JavaScript, Go
|
||||
Minga deja de ser Rust-only. Cualquiera de los cinco dialectos
|
||||
(Rust + 4 nuevos) se ingresa al CAS por su AST normalizado, hashea
|
||||
|
||||
Reference in New Issue
Block a user