tawasuyu

tawasuyu/minga

Sovereign P2P content sharing with a content-addressed FUSE mount, in Rust.

⎇ 85795c42cbd49259b2fef73a15989c73ac319141 03_ukupacha/minga/minga-core/src/alpha/common.rs Rust · 105 líneas · 3.4 KB Historia Quién tocó qué crudo

feat: minga standalone — compartición P2P soberana con montaje FUSE content-addressed (front-door, git-dep al monorepo) 85795c42c · Sergio ·

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}