⎇ 85795c42cbd49259b2fef73a15989c73ac319141
03_ukupacha/minga/minga-core/src/alpha/common.rs
Historia
Quién tocó qué
crudo
1//! Primitives compartidos entre todos los profiles α-hashing.
2//!
3//! Cada profile per-language (rust, python, ecmascript, go) tiene su
4//! propia lógica de "qué nodos introducen binders" y "cómo distinguir
5//! binders de constructors". Pero el format del wire del hash
6//! (TAG_LEAF, TAG_BINDER, índice de Bruijn) es universal: lo emitimos
7//! desde acá para garantizar que dos lenguajes con la misma
8//! estructura semántica produzcan hashes comparables a nivel de bits.
9
10use crate::ast::SemanticNode;
11use blake3::Hasher;
12
13pub const TAG_NO_LEAF: u8 = 0;
14pub const TAG_LEAF: u8 = 1;
15pub const TAG_BINDER: u8 = 2;
16pub const TAG_REF_BOUND: u8 = 3;
17pub const TAG_REF_FREE: u8 = 4;
18
19/// Emite el kind del nodo + presencia/ausencia de field_name.
20pub fn write_kind_and_field(h: &mut Hasher, node: &SemanticNode) {
21 write_str(h, &node.kind);
22 match &node.field_name {
23 Some(f) => {
24 h.update(&[1]);
25 write_str(h, f);
26 }
27 None => {
28 h.update(&[0]);
29 }
30 }
31}
32
33pub fn write_str(h: &mut Hasher, s: &str) {
34 h.update(&(s.len() as u64).to_le_bytes());
35 h.update(s.as_bytes());
36}
37
38/// Emite el marker de leaf: TAG_LEAF + bytes del leaf si lo hay,
39/// TAG_NO_LEAF si no.
40pub fn emit_leaf_marker(h: &mut Hasher, node: &SemanticNode) {
41 match &node.leaf_text {
42 Some(t) => {
43 h.update(&[TAG_LEAF]);
44 h.update(&(t.len() as u64).to_le_bytes());
45 h.update(t);
46 }
47 None => {
48 h.update(&[TAG_NO_LEAF]);
49 }
50 }
51}
52
53/// Emite un binder anónimo: el contenido textual NO afecta el hash.
54/// Esta es la primitiva de α-equivalencia: dos términos que sólo
55/// difieren en nombres de variables ligadas hashean idénticos.
56pub fn emit_binder_body(h: &mut Hasher) {
57 h.update(&[TAG_NO_LEAF]);
58 h.update(&[TAG_BINDER]);
59 h.update(&[0u8; 8]);
60}
61
62/// Emite el kind del nodo + binder body. Atajo para nodos cuyo único
63/// rol es ser binder (e.g. un identifier en posición de pattern).
64pub fn emit_binder_node(h: &mut Hasher, node: &SemanticNode) {
65 write_kind_and_field(h, node);
66 emit_binder_body(h);
67}
68
69/// Emite un identifier referencia: si está en scope, índice de
70/// Bruijn (offset desde la cima); si no, nombre literal (variable
71/// libre).
72pub fn emit_identifier_ref(h: &mut Hasher, node: &SemanticNode, scope: &[String]) {
73 h.update(&[TAG_NO_LEAF]);
74 if let Some(t) = &node.leaf_text {
75 if let Ok(name) = std::str::from_utf8(t) {
76 if let Some(i) = scope.iter().rposition(|n| n == name) {
77 let de_bruijn = (scope.len() - 1 - i) as u64;
78 h.update(&[TAG_REF_BOUND]);
79 h.update(&de_bruijn.to_le_bytes());
80 } else {
81 h.update(&[TAG_REF_FREE]);
82 h.update(&(t.len() as u64).to_le_bytes());
83 h.update(t);
84 }
85 } else {
86 h.update(&[TAG_REF_FREE]);
87 h.update(&(t.len() as u64).to_le_bytes());
88 h.update(t);
89 }
90 } else {
91 h.update(&[TAG_REF_FREE]);
92 h.update(&[0u8; 8]);
93 }
94 h.update(&[0u8; 8]);
95}
96
97/// Push el nombre del identifier al vector de binders, si tiene
98/// leaf_text válido. Helper común para todos los `collect_binders`.
99pub fn push_identifier_name(node: &SemanticNode, out: &mut Vec<String>) {
100 if let Some(t) = &node.leaf_text {
101 if let Ok(s) = std::str::from_utf8(t) {
102 out.push(s.to_string());
103 }
104 }
105}