Versión 1.2.5
Lenguaje de programación de propósito general con sintaxis expresiva y moderna. Diseñado para ser fácil de aprender y productivo de usar.
- Características
- Instalación rápida
- Inicio rápido
- Sintaxis
- Arquitectura
- Uso
- Desarrollo
- Documentación de módulos
- Tests
- Roadmap
- Contribuir
- Licencia
- 🎯 Sintaxis clara y concisa - Fácil de leer y escribir
- 🔄 Tipado dinámico con anotaciones opcionales - Flexibilidad sin sacrificar claridad
- 🔒 Manejo de errores - Sistema try/catch con
Errornativo - 🏗️ Programación orientada a objetos - Clases con herencia (
ext:) - 🔧 Funciones de primera clase - Funciones como valores y recursividad
- 🎨 Interpolación de strings - Sintaxis natural con
&variable - 🔄 Control de flujo completo - if/else, switch/case, for, foreach, while, do-while
- 🚀 REPL interactivo - Experimenta en tiempo real con
umbral-repl - 📦 CLI potente - Ejecuta archivos con
umbral archivo.um - 📚 Sistema de módulos - Importaciones/exportaciones con
equip/origin - 🔧 Gestor de paquetes UMP - Instalación automática de librerías
- 📖 Biblioteca estándar - Funciones esenciales para strings, números, archivos y colecciones
- 🎨 Arrays y diccionarios - Estructuras de datos con métodos integrados
- ⚡ Operadores completos - Aritméticos, lógicos, comparación, incremento/decremento y spread
⚠️ Interfaces - Definición conin:e implementación conimp:(sintaxis definida, validación pendiente)- Debugger integrado - Herramientas de depuración
- 📊 Language Server Protocol (LSP) - Soporte para editores
Descarga la última versión desde Releases:
- Linux:
umbral_1.2.5_amd64.deb - Windows:
umbral_1.2.5_x64.exe - Código fuente:
umbral-1.2.5.tar.gzoumbral-1.2.5.zip
sudo dpkg -i umbral_1.2.5_amd64.debEjecuta el instalador umbral_1.2.5_x64.exe y sigue las instrucciones.
- Rust 1.70 o superior
- Git
git clone https://github.com/hersac/umbral.git
cd umbral
./install.shgit clone https://github.com/hersac/umbral.git
cd umbral
PowerShell -ExecutionPolicy Bypass -File install.ps1Importante: Cierra y vuelve a abrir tu terminal después de instalar.
Warning
Conflicto de versiones: Si previamente instalaste Umbral manualmente con cargo install, es posible que esa versión tenga prioridad. Ejecuta cargo uninstall umbral antes de usar el instalador para evitar conflictos.
umbral --version
umbral-repl📖 Guía de instalación completa
Crea un archivo hola.um:
v: mensaje = "Hola, Mundo!";
tprint(mensaje);
Ejecuta:
umbral hola.umumbral-replumbral> v: x = 10;
umbral> v: y = 20;
umbral> tprint(x + y);
30
!! Variables (mutables)
v: nombre = "Heriberto";
v: edad->Int = 30;
v: precio->Flo = 99.99;
v: activo->Bool = true;
!! Constantes (inmutables)
c: PI = 3.14159;
c: MAX_INTENTOS = 3;
Umbral soporta inferencia de tipos (tipado dinámico) y anotaciones de tipo opcionales (tipado fuerte).
!! Inferencia de tipos (el tipo se deduce automáticamente)
v: entero = 42; !! Umbral infiere que es Int
v: flotante = 3.14; !! Umbral infiere que es Flo
v: texto = 'Hola'; !! Umbral infiere que es Str
v: booleano = true; !! Umbral infiere que es Bool
v: nulo = null; !! Umbral infiere que es null
!! Tipado fuerte (especificas el tipo explícitamente)
v: edad->Int = 30; !! Declaración con tipo Int
v: precio->Flo = 99.99; !! Declaración con tipo Flo
v: nombre->Str = "María"; !! Declaración con tipo Str
v: activo->Bool = true; !! Declaración con tipo Bool
!! Tipos disponibles:
!! - Int (enteros: 42, -10, 0)
!! - Flo (flotantes: 3.14, -0.5, 2.0)
!! - Str (strings: 'hola', "mundo")
!! - Bool (booleanos: true, false)
!! Arrays
v: numeros = {1, 2, 3, 4, 5}; !! Inferido como array
v: nombres->[]Str = {'Ana', 'Luis', 'María'}; !! Array tipado de strings
!! Operador Spread (&) para expandir arrays
c: arr1 = {1, 2, 3};
c: arr2 = {4, 5, 6};
c: combinado = { &arr1, &arr2 }; !! Resultado: {1, 2, 3, 4, 5, 6}
c: mixto = { 0, &arr1, 99, &arr2 }; !! Resultado: {0, 1, 2, 3, 99, 4, 5, 6}
!! Objetos/Diccionarios
v: persona = [ !! Inferido como objeto
"nombre" => "Juan",
"edad" => 25,
"ciudad" => "Madrid"
];
v: config->Objeto = [ !! Objeto tipado explícito
"host" => "localhost",
"port" => 8080
];
!! Aritméticos
v: suma = 10 + 5; !! 15
v: resta = 10 - 5; !! 5
v: multiplicacion = 10 * 5; !! 50
v: division = 10 / 5; !! 2
v: modulo = 10 % 3; !! 1
!! Comparación
v: igual = 10 == 10; !! true
v: diferente = 10 != 5; !! true
v: menor = 5 < 10; !! true
v: mayor = 10 > 5; !! true
!! Lógicos
v: y = true && false; !! false
v: o = true || false; !! true
v: no = !true; !! false
!! Incremento/Decremento
v: contador = 0;
contador++; !! 1
contador--; !! 0
!! Spread (expansión de arrays)
v: a = {1, 2};
v: b = {3, 4};
v: c = { &a, &b }; !! {1, 2, 3, 4} - Expande ambos arrays
v: d = a + b; !! {1, 2, 3, 4} - Concatenación equivalente
!! If / Else if / Else
i: (edad < 18) {
tprint('Menor de edad');
} ie: (edad < 65) {
tprint('Adulto');
} e: {
tprint('Adulto mayor');
}
!! Switch / Case
sw: (opcion) {
ca: 1 =>
tprint('Opción 1');
ca: 2 =>
tprint('Opción 2');
def: =>
tprint('Opción inválida');
}
!! For
fo: (v: i = 0; i < 10; i++) {
tprint(i);
}
!! ForEach
v: frutas = {'manzana', 'pera', 'naranja'};
fe: (v: fruta <= frutas) {
tprint(fruta);
}
!! While
v: contador = 0;
wh: (contador < 5) {
tprint(contador);
contador++;
}
!! Do-While
v: numero = 0;
dw: {
tprint(numero);
numero++;
} (numero < 5)
Umbral proporciona un sistema robusto de manejo de excepciones mediante bloques try-catch-finally (tr, ct, fy) y la sentencia throw (tw).
!! Estructura básica
tr: {
!! Código que puede fallar
tw: "Ocurrió un error inesperado";
} ct: (v: e) {
!! Manejo del error
tprint("Capturado: &e");
} fy: {
!! Bloque opcional, siempre se ejecuta
tprint("Limpiando recursos...");
}
!! Captura tipada con clase Error
tr: {
v: errorCritico = n: Error("Fallo de conexión");
tw: errorCritico;
} ct: (c: e -> Error) {
tprint("Error de sistema detectado: &e.mensaje");
}
!! Función simple
f: saludar(nombre->Str) {
tprint("Hola, &nombre!");
}
!! Función con retorno
f: sumar(a->Int, b->Int)->Int {
r: (a + b);
}
!! Función recursiva
f: factorial(n->Int)->Int {
i: (n <= 1) {
r: (1);
} e: {
r: (n * factorial(n - 1));
}
}
!! Uso
saludar("María");
v: resultado = sumar(10, 20);
tprint(factorial(5));
!! Definición de clase
cs: Persona {
pr: nombre->Str;
pr: edad->Int;
!! Constructor
pu f: Persona(nombre->Str, edad->Int) {
th.nombre = nombre;
th.edad = edad;
}
!! Método público
pu f: presentarse()->Void {
tprint("Hola, soy &th.nombre y tengo &th.edad años");
}
!! Método con retorno
pu f: esMayorDeEdad()->Bool {
r: (th.edad >= 18);
}
}
!! Instanciación
c: persona1 = n: Persona("Juan", 25);
persona1.presentarse();
!! Interfaces (in:)
!! Define contratos que las clases deben cumplir
in: Dibujable {
f: dibujar()->Void;
f: obtenerColor()->Str;
}
!! Implementar una interfaz (imp:)
cs: Circulo imp: Dibujable {
pr: radio->Flo;
pr: color->Str;
pu f: Circulo(radio->Flo, color->Str) {
th.radio = radio;
th.color = color;
}
!! Implementación de métodos de la interfaz
pu f: dibujar()->Void {
tprint("Dibujando círculo de radio &th.radio");
}
pu f: obtenerColor()->Str {
r: (th.color);
}
}
!! Herencia (ext:)
!! Una clase puede extender de otra clase base
cs: Empleado ext: Persona {
pr: salario->Flo;
pr: puesto->Str;
pu f: Empleado(nombre->Str, edad->Int, puesto->Str, salario->Flo) {
!! Llamar al constructor de la clase base
th.nombre = nombre;
th.edad = edad;
th.puesto = puesto;
th.salario = salario;
}
!! Sobrescribir método heredado
pu f: presentarse()->Void {
tprint("Hola, soy &th.nombre, &th.puesto de la empresa");
}
!! Nuevo método específico
pu f: obtenerSalario()->Flo {
r: (th.salario);
}
}
!! Uso de herencia
c: empleado1 = n: Empleado("Ana", 28, "Desarrolladora", 50000.0);
empleado1.presentarse(); !! "Hola, soy Ana, Desarrolladora de la empresa"
tprint(empleado1.obtenerSalario()); !! 50000.0
tprint(empleado1.esMayorDeEdad()); !! true (heredado de Persona)
!! Combinación: Herencia + Interfaz
cs: Rectangulo ext: Forma imp: Dibujable {
pr: ancho->Flo;
pr: alto->Flo;
!! Implementa métodos de la clase base Forma
!! Implementa métodos de la interfaz Dibujable
}
!! Definición de enum con valores inferidos (0, 1, 2...)
em: Prioridad {
Baja,
Media,
Alta
}
!! Definición de enum con valores explícitos
em: CodigoHTTP {
Ok=200,
Creado=201,
NoEncontrado=404,
ErrorServidor=500
}
em: Estado {
Activo='activo',
Inactivo='inactivo',
Pendiente='pendiente'
}
!! Enums mixtos (inferidos y explícitos)
em: Mixto {
Primero, !! valor = 0
Segundo='custom', !! valor = 'custom'
Tercero, !! valor = 2
Cuarto=100 !! valor = 100
}
!! Uso de enums - retornan directamente sus valores
v: prioridad = Prioridad.Alta; !! prioridad = 2
v: codigo = CodigoHTTP.Ok; !! codigo = 200
v: estadoActual = Estado.Activo; !! estadoActual = 'activo'
!! Imprimir valores
tprint(prioridad); !! 2
tprint(codigo); !! 200
tprint(estadoActual); !! activo
!! Comparación directa con valores
i: (prioridad == 2) {
tprint('Prioridad es 2');
}
i: (codigo == 200) {
tprint('Código es 200');
}
i: (estadoActual == 'activo') {
tprint('Estado activo');
}
!! Operaciones aritméticas
v: suma = Prioridad.Baja + Prioridad.Media + Prioridad.Alta;
tprint(suma); !! 3 (0 + 1 + 2)
!! Enums en funciones
f: procesarCodigo(codigo) {
i: (codigo == 200) {
tprint('Solicitud exitosa');
}
i: (codigo == 404) {
tprint('Recurso no encontrado');
}
}
procesarCodigo(CodigoHTTP.Ok);
!! Enums exportables
out em: TipoUsuario {
Admin,
Editor,
Lector
}
!! Archivo: modulos/matematicas.um
!! Solo los elementos con 'out' pueden ser importados
out f: sumar(a->Int, b->Int)->Int {
r: (a + b);
}
out c: PI = 3.14159;
!! Función privada (sin 'out')
f: funcionInterna()->Int {
r: (42);
}
!! Archivo: main.um
!! Sintaxis de importación
!! 1. Importación simple
equip sumar origin 'modulos/matematicas.um';
!! 2. Importación con alias
equip sumar as suma origin 'modulos/matematicas.um';
!! 3. Importación de lista
equip { sumar, PI } origin 'modulos/matematicas.um';
!! 4. Importación con namespace
equip * as mat origin 'modulos/matematicas.um';
c: resultado = mat_sumar(10, 5);
!! 5. Orden invertido
origin 'modulos/matematicas.um' equip sumar;
Umbral tiene soporte integrado para el gestor de paquetes UMP (Umbral Package Manager), que permite instalar y gestionar librerías de forma sencilla.
# Instalar una librería desde el registry
ump add http
# La librería se descarga en modules_ump/Una vez instalada una librería con UMP, puedes importarla usando solo su nombre:
!! Importación simplificada de módulo UMP
equip { fetch } origin 'http';
!! Uso de la librería
c: url = "https://api.ejemplo.com/datos";
c: respuesta = fetch(url);
tprint("Respuesta: &respuesta");
Ventajas:
- ✅ No necesitas conocer la estructura interna de
modules_ump - ✅ Sintaxis consistente con
ump add <nombre> - ✅ Búsqueda automática en la jerarquía de directorios
- ✅ Compatible con rutas relativas tradicionales
Más información: UMP Package Manager
!! String simple
v: texto1 = 'Hola mundo';
!! String con interpolación
v: nombre = "María";
v: edad = 25;
v: mensaje = "Hola &nombre, tienes &edad años";
tprint(mensaje);
!! String multilínea
v: parrafo = '''
Este es un texto
que ocupa múltiples
líneas.
''';
!! Arrays
v: numeros = {10, 20, 30};
tprint(numeros[0]); !! 10
tprint(numeros.length); !! 3
!! Métodos de arrays
v: lista = {1, 2, 3};
v: nuevaLista = lista.push(4); !! {1, 2, 3, 4}
v: sinUltimo = lista.pop(); !! {1, 2}
v: longitud = lista.len(); !! 3
!! Encadenamiento de métodos
v: resultado = {1, 2}.push(3).push(4).pop(); !! {1, 2, 3}
!! Objetos
v: config = [
"host" => "localhost",
"port" => 8080
];
tprint(config.host); !! localhost
!! Propiedades de objetos
c: persona = n: Persona("Ana", 30);
tprint(persona.nombre); !! Ana
persona.setEdad(31);
!! Este es un comentario de línea
v: x = 10; !! Comentario al final de línea
!!
!! Bloque de comentarios
!! múltiples líneas
!!
Umbral incluye una biblioteca estándar mínima con funciones esenciales accesibles mediante el objeto global Std.
v: texto = " Hola ";
v: limpio = Std.trim(texto); !! "Hola"
v: partes = Std.split("a,b,c", ","); !! ["a", "b", "c"]
v: nuevo = Std.replace("hola", "o", "0"); !! "h0la"
v: mayus = Std.to_upper("umbral"); !! "UMBRAL"
v: minus = Std.to_lower("UMBRAL"); !! "umbral"
v: numero = Std.parse_int("42"); !! 42
v: decimal = Std.parse_float("3.14"); !! 3.14
v: texto = Std.to_string(123); !! "123"
v: aleatorio = Std.random(); !! 0.0 - 1.0
v: absoluto = Std.abs(-10); !! 10
v: ok = Std.write_file("datos.txt", "contenido");
v: existe = Std.exists("datos.txt"); !! true
v: contenido = Std.read_file("datos.txt");
!! Métodos de Std (también disponibles directamente en arrays)
v: lista = {1, 2, 3};
v: longitud = Std.len(lista); !! 3
v: nueva = Std.push(lista, 4); !! {1, 2, 3, 4}
v: sin_ultimo = Std.pop(lista); !! {1, 2}
!! Equivalente usando métodos directos
v: lista2 = {1, 2, 3};
v: longitud2 = lista2.len(); !! 3
v: nueva2 = lista2.push(4); !! {1, 2, 3, 4}
v: sin_ultimo2 = lista2.pop(); !! {1, 2}
!! Diccionarios
v: dict = ["a" => 1, "b" => 2];
v: claves = Std.keys(dict); !! ["a", "b"]
- Int: Entero con signo de 64 bits (
i64). - Flo: Punto flotante de doble precisión (
f64). - Bool: Booleano (
true,false). - Str: Cadena de caracteres UTF-8.
- Null: Representa la ausencia de valor (
null). - Void: Tipo de retorno para funciones sin valor.
- List: Secuencia ordenada de valores (
[]). - Dict: Colección de pares clave-valor (
HashMap). - Obj: Instancia de una clase.
- Func: Referencia a una función.
- Inferencia: El tipo se infiere en la asignación si no se especifica.
- Anotación:
v: nombre->Tipo = valor;fuerza la validación del tipo. - Coerción: No existe coerción implícita entre tipos incompatibles (ej.
Int+Stres error, excepto en interpolación).
programa ::= sentencia*
sentencia ::= declaracion_var
| declaracion_const
| asignacion
| definicion_func
| definicion_clase
| control_flujo
| throw_stmt
| expresion ';'
declaracion_var ::= 'v:' identificador ('->' tipo)? '=' expresion ';'
declaracion_const ::= 'c:' identificador ('->' tipo)? '=' expresion ';'
asignacion ::= identificador '=' expresion ';'
definicion_func ::= 'f:' identificador '(' parametros? ')' ('->' tipo)? bloque
definicion_clase ::= 'cs:' identificador '{' miembro_clase* '}'
control_flujo ::= if_stmt | while_stmt | for_stmt | switch_stmt | try_stmt
try_stmt ::= 'tr:' bloque catch_block? finally_block?
catch_block ::= 'ct:' '(' identificador_var ':'? ('->' tipo)? ')' bloque
finally_block ::= 'fy:' bloque
throw_stmt ::= 'tw:' expresion ';'
bloque ::= '{' sentencia* '}'- Igualdad Numérica:
IntyFloson comparables.10 == 10.0estrue. - Igualdad Estricta por Tipo: Tipos diferentes (salvo numéricos) nunca son iguales.
10 == "10"esfalse. - Identidad: Objetos y Listas se comparan por referencia.
- Declaración Explícita: Toda variable debe ser declarada con
v:oc:antes de usarse. - Asignación: Asignar a una variable no declarada es un ERROR. No existe declaración implícita.
- Shadowing: Declarar una variable con el mismo nombre que una existente en un ámbito superior generará una ADVERTENCIA (Warning).
- Ámbito: Léxico (bloques
{}).
Los siguientes valores se evalúan como false: false, null, 0, 0.0, "", []. Todo lo demás es true.
- Importación:
equip [item] origin 'ruta'; - Exportación:
outprefijo en declaraciones. - Rutas: Relativas al archivo actual o nombre de módulo UMP.
- Módulos UMP: Si el origen no contiene
/,./o../, se busca automáticamente enmodules_ump/. - Búsqueda: El intérprete busca
modules_umpsubiendo en la jerarquía de directorios hasta encontrarlo.
Umbral está construido como un intérprete modular en Rust:
┌─────────────────────────────────────────┐
│ Código fuente (.um) │
└─────────────────┬───────────────────────┘
│
▼
┌────────────────┐
│ umbral-lexer │ ──→ Tokens
└────────┬───────┘
│
▼
┌────────────────┐
│ umbral-parser │ ──→ AST
└────────┬───────┘
│
▼
┌────────────────┐
│ umbral-runtime │ ──→ Ejecución
└────────┬───────┘
│
▼
┌────────────────────┐
│ umbral-interpreter │ ──→ Coordinador
└──────┬────────┬────┘
│ │
┌────────┘ └────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ umbral-cli │ │ umbral-repl │
└──────────────┘ └──────────────┘
Archivos Interactivo
| Módulo | Propósito | README |
|---|---|---|
umbral-lexer |
Análisis léxico (tokenización) | README |
umbral-parser |
Análisis sintáctico (AST) | README |
umbral-runtime |
Motor de ejecución | README |
umbral-interpreter |
Coordinador de fases | README |
umbral-cli |
Ejecutor de archivos | README |
umbral-repl |
REPL interactivo | README |
# Sintaxis básica
umbral archivo.um
# Ejemplos
umbral main.um
umbral codigo-ejemplo/main.um
umbral /ruta/completa/programa.umumbral-replComandos del REPL:
| Comando | Descripción |
|---|---|
:help |
Muestra ayuda |
:clear |
Reinicia el intérprete |
:exit / :quit |
Sale del REPL |
Ctrl+C |
Cancela entrada actual |
Ctrl+D |
Sale del REPL |
Ejemplo de sesión:
umbral> v: x = 10;
umbral> v: y = 20;
umbral> f: sumar(a, b) {
-> r: (a + b);
-> }
umbral> tprint(sumar(x, y));
30
git clone https://github.com/hersac/umbral.git
cd umbral# Modo debug
cargo build
# Modo release (optimizado)
cargo build --release# CLI
cargo run --bin umbral -- archivo.um
# REPL
cargo run --bin umbral-replumbral/
├── Cargo.toml # Workspace principal
├── install.sh # Instalador Linux/macOS
├── install.ps1 # Instalador Windows
├── uninstall.sh # Desinstalador Linux/macOS
├── uninstall.ps1 # Desinstalador Windows
├── test_repl.sh # Tests del REPL
├── INSTALL.md # Guía de instalación
├── LICENSE # Licencia del proyecto
└── crates/
├── umbral-lexer/ # Tokenizador
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
├── umbral-parser/ # Parser (AST)
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
├── umbral-runtime/ # Motor de ejecución
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
├── umbral-interpreter/ # Coordinador
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
├── umbral-cli/ # CLI
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
└── umbral-repl/ # REPL
├── Cargo.toml
├── README.md
└── src/
Cada módulo tiene su propia documentación detallada:
- umbral-lexer - Tokenización y análisis léxico
- umbral-parser - Parser y construcción del AST
- umbral-runtime - Motor de ejecución y runtime
- umbral-interpreter - API unificada
- umbral-cli - Ejecutor de archivos
- umbral-repl - REPL interactivo
cargo testcargo test -p umbral-lexer
cargo test -p umbral-parser
cargo test -p umbral-runtime
cargo test -p umbral-interpreter./test_repl.shEl proyecto incluye una carpeta ejemplos/ con código de demostración de todas las funcionalidades de Umbral, organizados por tema (variables, funciones, clases, bucles, importaciones, etc.).
- ✅ Lexer completo con soporte para todos los tokens
- ✅ Parser con AST robusto
- ✅ Runtime funcional con gestión de memoria
- ✅ Variables y constantes con tipado (Int, Flo, Str, Bool, Void)
- ✅ Funciones con retorno de tipos avanzados y recursividad
- ✅ Clases con herencia (
ext:) - ✅ Sintaxis de interfaces (
in:eimp:) - ✅ Control de flujo completo (if/else, switch/case, for, foreach, while, do-while)
- ✅ Operadores completos (aritméticos, lógicos, comparación, incremento/decremento, spread)
- ✅ Arrays y diccionarios con métodos integrados
- ✅ Interpolación de strings con
&variable - ✅ Sistema de módulos con importaciones/exportaciones
- ✅ Control de acceso con
outpara exportaciones - ✅ Múltiples sintaxis de importación (equip/origin)
- ✅ Integración con gestor de paquetes UMP
- ✅ Resolución automática de módulos en
modules_ump/ - ✅ Biblioteca estándar (Std) con funciones para strings, números, archivos y colecciones
- ✅ CLI (
umbral) con gestión de versiones centralizada - ✅ REPL interactivo (
umbral-repl) - ✅ Instaladores para Linux/macOS/Windows
- ✅ Paquetes binarios (.deb, .exe)
- Validación completa de interfaces en runtime
- ✅ Enums funcionales
- ✅ Manejo de errores con try/catch
- Optimización de performance del intérprete
- Expansión de la biblioteca estándar
- Debugger integrado
- Language Server Protocol (LSP)
- Sistema de tipos estático opcional
- Compilador a bytecode
¡Las contribuciones son bienvenidas!
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/amazing-feature) - Haz commit de tus cambios (
git commit -m 'Add amazing feature') - Push a la rama (
git push origin feature/amazing-feature) - Abre un Pull Request
- Sigue las convenciones de código existentes
- Escribe tests para nuevas funcionalidades
- Actualiza la documentación cuando sea necesario
- Asegúrate de que
cargo testpase antes de enviar PR
Abre un issue con:
- Descripción clara del problema
- Pasos para reproducir
- Comportamiento esperado vs actual
- Versión de Umbral y sistema operativo
Este proyecto está bajo la licencia especificada en LICENSE.
- Heriberto Sánchez - Creador y mantenedor principal
- GitHub: @hersac
- Repositorio: github.com/hersac/umbral
Gracias a la comunidad de Rust por las excelentes herramientas y librerías que hicieron posible este proyecto.
¡Disfruta programando en Umbral! 🎉