Saltar al contenido
Vacav0.1.0GitHub

v0.1.0 · 109 pruebas · MIT

El backend
también se escribe
en español.

Vaca es un lenguaje de servidores con un motor en Rust, sin frameworks y con TLS auditado. Llaves, punto y coma opcional, análisis de variables no usadas y concurrencia real. Para una API, un pipeline de datos o un laboratorio de estrés.

Tus programas no necesitan Node ni npm: un binario y tus archivos.vaca.

servidor concurrente
servidor.vaca
:8080
importar http
importar json
importar concurrencia

peticiones = concurrencia.contador(0)

fn manejador(peticion) {
    total = peticiones.incrementar()

    si peticion.ruta == "/salud" {
        retornar {"cuerpo": {
            "estado": "ok",
            "peticiones": total
        }}
    }

    si peticion.metodo == "POST" y peticion.ruta == "/eco" {
        datos = json.convertir(peticion.cuerpo)
        retornar {"estado": 201,
                  "cuerpo": {"recibido": datos}}
    }

    retornar {"estado": 404,
              "cuerpo": {"error": "no encontrada"}}
}

http.servidor(8080, manejador, 1000)

Sin frameworks. Sin runtime externo. Sin ceremonia.

Paradigma
Multiparadigma
Tipado
Dinámico + opcional
Motor
Bytecode + VM en Rust
Dependencias
Rust estándar + rustls

El lenguaje

Un lenguaje completo,
no un juguete.

Lo que esperas de un lenguaje serio, con la sintaxis que ya piensas. Todo esto viene en la biblioteca estándar: no hay paquetes que instalar ni ecosistema que mantener.

Sintaxis en español

si, sino, mientras, retornar. Sin traducciones mentales ni palabras reservadas en inglés.

Llaves y punto y coma opcional

La estructura se ve a simple vista. El punto y coma existe, pero nunca es obligatorio.

Variables no usadas = error

El análisis semántico corre antes de ejecutar. Si declaras una variable y no la usas, el programa no arranca.

Tipos dinámicos y anotaciones

Escribe contador: Entero = 0 cuando quieras documentar. El tipado sigue siendo flexible.

Funciones de primera clase

Valores por defecto, callbacks, closures y recursividad. Las funciones se pasan y se devuelven.

POO sin ceremonia

Clases, herencia, este e iniciar. Métodos y propiedades con la misma sintaxis de siempre.

Colecciones con métodos

Listas y diccionarios con agregar, filtrar, mapear, ordenar, get, set y mucho más.

Errores explícitos

lanzar, intentar, atrapar y afirmar. Sin excepciones silenciosas ni retornos mágicos.

Módulos nativos

JSON, SQLite, matemáticas, archivos, texto, tiempo, sistema y DataFrames en la biblioteca estándar.

Servidor incluido

Cliente y servidor HTTP/1.1. Cada conexión en su hilo, con límite de conexiones configurable.

Datos sin pandas

Lee CSV, filtra, ordena, agrupa y exporta. DataFrames ligeros que caben en la stdlib.

Concurrencia real

async y esperar sobre hilos del sistema. Contadores atómicos, paralelo y esperar_todas.

Sin frameworks. Sin node_modules. Rust estándar yrustls para TLS.

En práctica

Se lee como se piensa.

Sin signos de más ni ceremonias. Cuatro programas reales que puedes ejecutar hoy con vaca archivo.vaca.

hola.vaca
# Variables, tipos e interpolación
nombre: Texto = "Mundo"
contador = 0

para i = 1 hasta 3 {
    contador = contador + i
}

imprimir(a"Hola, {nombre}. Suma: {contador}")

# Listas y diccionarios con métodos
frutas = ["pera", "uva", "higo"]
frutas.agregar("melón")
frutas.ordenar()
imprimir(frutas)
servidor.vaca
importar http
importar json
importar concurrencia

peticiones = concurrencia.contador(0)

fn manejador(peticion) {
    total = peticiones.incrementar()

    si peticion.metodo == "GET" y peticion.ruta == "/salud" {
        retornar {"cuerpo": {
            "estado": "ok",
            "peticiones": total
        }}
    }

    si peticion.metodo == "POST" y peticion.ruta == "/eco" {
        datos = json.convertir(peticion.cuerpo)
        retornar {"estado": 201,
                  "cuerpo": {"recibido": datos}}
    }

    retornar {"estado": 404,
              "cuerpo": {"error": "no encontrada"}}
}

http.servidor(8080, manejador, 1000)
datos.vaca
importar datos
importar json

df = datos.leer_csv("ventas.csv")
datos.ver(df)

grandes = datos.filtrar(df, "monto", ">=", 100)
ordenado = datos.ordenar(grandes, "monto", falso)
resumen = datos.agrupar_resumir(ordenado, "ciudad", "monto", "suma")

imprimir(json.serializar(resumen))
datos.guardar_csv(resumen, "resumen.csv")
figuras.vaca
clase Figura {
    fn area() {
        lanzar "cada figura debe implementar area"
    }
}

clase Circulo(Figura) {
    fn iniciar(radio) {
        este.radio = radio
    }

    fn area() {
        retornar 3.1416 * este.radio * este.radio
    }
}

clase Rectangulo(Figura) {
    fn iniciar(ancho, alto) {
        este.ancho = ancho
        este.alto = alto
    }

    fn area() {
        retornar este.ancho * este.alto
    }
}

figuras = [nuevo Circulo(2), nuevo Rectangulo(3, 4)]

para figura en figuras {
    imprimir(a"Área: {figura.area()}")
}

Sin marketing

La lista de un lenguaje normal.

Esto es lo que cualquier lenguaje serio debería tener. Vaca no lo tiene todo: aquí está el estado real, sin letra pequeña.

  • Listo
  • Parcial
  • Pendiente

Núcleo del lenguaje

  • Variables y constantesListo
  • Condicionales y buclesListo
  • Funciones, closures y recursiónListo
  • Listas y diccionariosListo
  • Clases, herencia y esteListo
  • Excepciones y afirmacionesListo
  • Interpolación de textoListo
  • Anotaciones de tipo opcionalesParcial

Seguridad y ergonomía

  • Variables no usadas = errorListo
  • Llaves, sin indentaciónListo
  • Punto y coma opcionalListo
  • Constantes en MAYÚSCULASListo
  • Comprobación estática de tiposPendiente
  • Recolector de basuraPendiente
  • super para llamar al padrePendiente

Backend y datos

  • Servidor HTTP concurrenteListo
  • Cliente HTTPListo
  • JSONListo
  • CSV y DataFramesListo
  • SQLiteListo
  • TLS / HTTPSListo
  • Middleware nativoPendiente

Concurrencia

  • async y esperarListo
  • Hilos del sistemaListo
  • Contadores atómicosListo
  • paralelo y esperar_todasListo
  • Event loopPendiente
  • Canales entre tareasPendiente

Herramientas

  • CLI: run, repl, tokens, ast, módulosListo
  • REPL multilíneaListo
  • Extensión de ZedListo
  • Módulos vaca.mod + iniciar.vacaListo
  • Bundles de producción verificadosListo
  • Formateador vaca fmtPendiente
  • Gestor de paquetesListo
  • LSPPendiente
  • vaca testPendiente

Distribución

  • cargo installListo
  • Tarball con instaladorListo
  • Imagen DockerListo
  • Unidad systemdListo
  • Binario estático muslListo
  • Cross-compilación documentadaListo

El detalle, fase por fase, está en el estado del proyecto.

Bajo el capó

Concurrencia que
no es de mentira.

Cada async fn arranca un hilo. Cada conexión HTTP, también. Los contadores atómicos no pierden incrementos y las tareas se pueden esperar varias veces sin repetir trabajo.

paralelo.vaca
hilos reales
importar tiempo

async fn consultar(nombre, ms) {
    tiempo.dormir(ms)
    retornar a"{nombre} lista"
}

inicio = tiempo.millis()

a = consultar("A", 200)
b = consultar("B", 200)

resultado_a = esperar a
resultado_b = esperar b

imprimir(tiempo.millis() - inicio)   # ~200, no 400

Es un modelo de hilos, no un event loop.Para cargas masivas, limita las conexiones conhttp.servidor(..., limite) o reparte el tráfico entre varias instancias. Cada conexión reserva 8 MiB de pila virtual.

Instalación

De cero a servidor
en tres comandos.

Sin instaladores exóticos ni un runtime que aprender. Rust estable,cargo build --release y un binario que puedes copiar a cualquier servidor.

  1. Clona y compila

    Motor en Rust estándar. TLS usa rustls y se descarga al compilar.

    git clone https://github.com/ulf-frostypaw/vaca.git
    cd vaca
    cargo build --release
  2. Ejecuta tu primer programa

    Los ejemplos cubren el lenguaje, la stdlib, los datos y los servidores.

    ./target/release/vaca ejemplos/hola.vaca
  3. Levanta un servidor

    Un servidor concurrente listo para probar con carga real.

    ./target/release/vaca ejemplos/servidor_concurrente.vaca

Herramientas

El taller completo.

Un solo binario con REPL, inspector de tokens, volcado de AST y soporte de editor. Lo justo para trabajar sin montar un ecosistema entero.

Comandos de la CLI de Vaca
ComandoQué hace
vaca archivo.vacaEjecuta un programa
vaca run archivo.vacaAlias explícito de ejecución
vaca replConsola interactiva multilínea
vaca tokens archivo.vacaMuestra el flujo de tokens
vaca ast archivo.vacaMuestra el árbol de sintaxis
vaca --versionVersión del intérprete
vaca repl
$ vaca repl
vaca> fn doble(n) { retornar n * 2 }
vaca> doble(21)
42
vaca> frutas = ["pera", "uva"]
vaca> frutas.agregar("higo")
vaca> frutas
["pera", "uva", "higo"]

Transparencia

Estado del proyecto.

Vaca se construyó por fases. Estas son las que están cerradas, las que están a medias y la que todavía falta. Sin promesas que el código no cumpla.

10fases completas
2parciales
1pendiente
Estado de las fases del proyecto Vaca
FaseContenidoEstado
00Andamiaje y tipos baseCompleta
01Lexer en españolCompleta
02Parser y AST con llavesCompleta
03Intérprete de árbolCompleta
04Compilador a bytecode y VMParcial
05Funciones, cierres, defaults y recursividadCompleta
06Listas, diccionarios y métodos nativosCompleta
07POO: clases, herencia, este e iniciarCompleta
08Módulos JSON, HTTP, mate, tiempo y sistemaCompleta
09DataFrames y CSVCompleta
10async, esperar y concurrencia realCompleta
11CLI, REPL, editor y empaquetadoParcial
12Type checker, optimizaciones y GCParcial

Siguiente objetivo

Un pool de hilos acotado o un event loop para reducir el coste por tarea y soportar decenas de miles de conexiones sin 8 MiB por hilo.

Ver roadmap completo