VestaShell es el lenguaje de scripting embebido en VestaVM. Permite automatizar tareas en el REPL, glue code entre modulos y scripting de sistema con acceso completo al filesystem, shell y matematicas.
Los scripts tienen extension .vsh y se ejecutan con:
// Dentro del REPL (comando "script")
vesta> script mi_script.vsh
// Abrir el REPL interactivo VestaShell desde dentro del REPL
vesta> vsh
// Linea de comandos (sin abrir el REPL)
./vm --script mi_script.vsh
// REPL interactivo VestaShell al estilo Python (desde linea de comandos)
./vm --interprete
Los ficheros de estado (vm_env.txt, vm_history.txt, vm_aliases.txt) se
crean siempre junto al ejecutable, independientemente del directorio de trabajo
actual.
Los builtins son visibles como valores de primera clase: escribir help sin
parentesis en el REPL lo invoca automaticamente (comportamiento estilo Python).
- Tipos de datos
- Variables y anotaciones de tipo
- Operadores
- Strings e interpolacion
- Control de flujo
- Funciones
- Listas
- Mapas
- Clases y POO
- Manejo de errores
- Importacion de modulos
- Funciones integradas
- Sockets y red
- FFI: llamadas a librerias nativas
- ANSI: colores y secuencias de escape
- REPL interactivo VestaShell
- Integracion con el REPL
- Comentarios y continuacion de linea
| Tipo | Descripcion | Ejemplo |
|---|---|---|
null |
Ausencia de valor | null |
bool |
Booleano | true, false |
int |
Entero de 64 bits con signo | 42, -7, 1_000 |
float |
Flotante IEEE 754 de 64 bits | 3.14, -0.5 |
string |
Cadena de texto UTF-8 | "Hola", 'mundo' |
list |
Lista dinamica de valores | [1, "dos", true] |
map |
Diccionario string -> valor | {"k": 1, "v": 2} |
function |
Funcion de primera clase (con closure) | fn(x) { return x*2 } |
class |
Descriptor de clase | class Punto { ... } |
instance |
Instancia de clase | Punto(1, 2) |
let nombre = "VestaShell" // declaracion
nombre = "vsh" // reasignacion (la variable ya debe existir)
let x = 10
let y = x + 5 // expresion en la inicializacion
let z = null // null explicitoLas variables son dinamicamente tipadas por defecto. Opcionalmente se puede
anotar el tipo con : tipo para que el interprete lo verifique en tiempo de
ejecucion:
let n: int = 42 // solo acepta int
let s: str = "hola" // solo acepta str / string
let f: float = 3.14 // solo acepta float
let b: bool = true // solo acepta bool
let lst: list = [1, 2, 3] // solo acepta list
let mp: map = {"k": 1} // solo acepta map
// Error si el tipo no coincide:
let x: int = "texto" // RuntimeError: variable 'x': se esperaba tipo 'int'Nombres de tipo aceptados en anotaciones: null, bool, int, float,
str/string, list, map, fn/function, class, instance,
o el nombre de cualquier clase definida en el script.
Los parametros de funcion tambien admiten anotaciones de tipo:
fn doble(x: int) { return x * 2 }
fn greet(nombre: str, edad) { // 'edad' sin tipo: acepta cualquier valor
return nombre + " tiene " + str(edad)
}
doble(5) // ok
doble("text") // RuntimeError: parametro 'x': se esperaba tipo 'int'Las subclases pasan el check de tipo de la clase padre:
class Animal { fn __init__(self, n) { self.nombre = n } }
class Perro : Animal { }
fn cuidar(a: Animal) { println(a.nombre) }
cuidar(Perro("Rex")) // ok: Perro es subclase de Animal| Operador | Descripcion | Ejemplo |
|---|---|---|
+ |
Suma / concatenacion | 3 + 4, "a"+"b" |
- |
Resta / negacion | 10 - 3, -x |
* |
Multiplicacion | 3 * 4 |
/ |
Division | 10 / 3 -> 3 |
% |
Modulo | 10 % 3 -> 1 |
** |
Potencia (der) | 2 ** 10 -> 1024 |
a == b a != b a < b a <= b a > b a >= bLos tipos int y float son iguales si el valor numerico coincide: 1 == 1.0.
not expr // negacion booleana
a and b // conjuncion (cortocircuito)
a or b // disyuncion (cortocircuito)x += 5 x -= 3 x *= 2 x /= 4 x %= 3let s = "Hola mundo"
let t = 'tambien funciona con comilla simple'
// Interpolacion: ${expresion} dentro de la cadena
let nombre = "Ada"
let n = 36
let msg = "Hola ${nombre}, tienes ${n} anos"
// Las expresiones pueden ser arbitrarias (sin comillas anidadas)
let pi = 3.14159
let area = "El area es ${pi * 4.0}"Nota: evitar usar comillas del mismo tipo dentro de ${}.
Usar variables intermedias cuando sea necesario:
let arg = "mundo"
let r = "${saludo(arg)}" // correcto
// let r = "${saludo("mundo")}" // produce error de sintaxis| Secuencia | Caracter |
|---|---|
\n |
Nueva linea |
\t |
Tabulacion |
\r |
Retorno carro |
\\ |
Barra invertida |
\" |
Comilla doble |
\$ |
Literal $ |
if condicion {
// ...
} elif otra_condicion {
// ...
} else {
// ...
}let i = 0
while i < 10 {
i += 1
if i == 5 { continue }
if i == 8 { break }
println(str(i))
}Itera sobre listas, mapas (claves) y strings (caracteres):
// Lista
for elem in [1, 2, 3, 4, 5] {
println(str(elem))
}
// Mapa (da claves)
let config = {"host": "localhost", "port": 8080}
for clave in config {
println(clave + " = " + str(config[clave]))
}
// String (da caracteres)
for c in "hola" {
println(c)
}
// Rango numerico
for i in range(10) { // 0..9
println(str(i))
}
for i in range(1, 6) { // 1..5
println(str(i))
}
for i in range(0, 10, 2) { // 0,2,4,6,8
println(str(i))
}
for i in range(5, 0, -1) { // 5,4,3,2,1
println(str(i))
}break sale del bucle mas cercano. continue salta a la siguiente iteracion.
Funcionan dentro de while, for/in y dentro de try/catch en un bucle.
// Declaracion nombrada
fn suma(a, b) {
return a + b
}
// Funcion anonima (lambda)
let doble = fn(x) { return x * 2 }
// Sin return explicito devuelve null
fn efecto() {
println("hola")
}
// Las funciones son valores de primera clase
fn aplicar(f, x) { return f(x) }
let resultado = aplicar(doble, 21) // 42La primera sentencia del cuerpo de una funcion puede ser un string literal. Ese
string se convierte en la documentacion de la funcion y es accesible mediante
doc() y help():
fn factorial(n) {
"Calcula n! de forma recursiva. factorial(0) = 1."
if n <= 1 { return 1 }
return n * factorial(n - 1)
}
doc(factorial) // "Calcula n! de forma recursiva. factorial(0) = 1."
help(factorial) // imprime la firma y el docstring; tambien devuelve el textoReglas:
- Solo string literal simple (no interpolado) como primera sentencia.
- Las funciones sin docstring devuelven
""endoc(). - Los docstrings se propagan a traves de
import.
fn contador() {
let n = 0
return fn() {
n = n + 1
return n
}
}
let c = contador()
c() // 1
c() // 2
c() // 3let ops = {
"add": fn(a, b) { return a + b },
"mul": fn(a, b) { return a * b },
}
ops["add"](10, 5) // 15let lista = [1, "dos", true, null, [3, 4]]
// Indexado (0-based; negativo desde el final)
lista[0] // 1
lista[-1] // [3, 4]
// Modificar
lista[1] = "TWO"
// Funciones
len(lista) // longitud
append(lista, valor) // anadir al final (modifica in-place)
pop(lista) // eliminar y devolver el ultimo elemento
contains(lista, valor) // true si valor esta en listalet m = {
"nombre": "Eva",
"edad": 30,
}
// Acceso (lanza error si la clave no existe)
m["nombre"] // "Eva"
// Asignacion (crea la clave si no existe)
m["activo"] = true
// Funciones
keys(m) // lista de claves
values(m) // lista de valores
contains(m, "edad") // true si la clave existe
len(keys(m)) // numero de claves
// Acceso seguro a clave posiblemente inexistente
let val = null
try {
val = m["clave_opcional"]
} catch e {
val = "por_defecto"
}let datos = {
"config": { "debug": true, "workers": 4 },
"tags": ["prod", "backend"],
}
datos["config"]["workers"] # 4
datos["tags"][0] # "prod"class Punto {
"Representa un punto 2D." // docstring de clase
fn __init__(self, x, y) {
self.x = x
self.y = y
}
fn distancia_al_origen(self) {
"Devuelve la distancia al origen."
return sqrt(self.x ** 2 + self.y ** 2)
}
}
let p = Punto(3.0, 4.0)
println(p.distancia_al_origen()) // 5.0
println(p.x) // 3.0
p.x = 0.0 // asignacion de atributo- El primer parametro de cada metodo es
selfpor convencion. __init__es el constructor; se llama automaticamente al crear la instancia.- Los atributos se crean dinamicamente asignando
self.campo = valor.
class Punto3D : Punto {
fn __init__(self, x, y, z) {
self.x = x
self.y = y
self.z = z
}
fn suma(self) {
return self.x + self.y + self.z
}
}
let q = Punto3D(1, 2, 3)
println(isinstance(q, Punto3D)) // true
println(isinstance(q, Punto)) // true (herencia)
println(classname(q)) // "Punto3D"super esta disponible dentro de los metodos como referencia a la clase padre:
class Animal {
fn __init__(self, nombre) { self.nombre = nombre }
fn hablar(self) { return "..." }
}
class Perro : Animal {
fn __init__(self, nombre) { self.nombre = nombre }
fn hablar(self) { return "Guau!" }
}is_class(Punto) // true
is_instance(p) // true
isinstance(p, Punto) // true
classname(p) // "Punto"
doc(Punto) // docstring de la clase
help(Punto) // imprime metodos y docstring de la clasetry {
// codigo que puede fallar
let contenido = read_file("archivo.txt")
} catch e {
// e es el mensaje de error como string
println("Error: " + e)
}error("descripcion del error") // lanza VshRuntimeError
assert(condicion, "mensaje") // lanza si condicion es falsa
assert(x > 0) // mensaje por defecto "asercion fallida"Los errores de tipo VshParseError (sintaxis) NO son capturables con try/catch.
VestaShell incluye la clase Error como base. Se pueden crear jerarquias de
errores personalizadas:
class ValorError : Error {
fn __init__(self, msg, valor) {
self.message = msg
self.valor = valor
}
}
class RangoError : ValorError {
fn __init__(self, msg, valor, min, max) {
self.message = msg
self.valor = valor
self.minimo = min
self.maximo = max
}
}
// Lanzar con throw
fn validar(x) {
if x < 0 or x > 100 {
throw RangoError("fuera de rango", x, 0, 100)
}
return x
}
// Capturar con catch tipado
try {
validar(150)
} catch e : RangoError {
println("Rango: " + e.message) // variable e es la instancia
println("Valor: " + str(e.valor))
} catch e : ValorError {
println("Valor: " + e.message)
} catch e {
println("Error: " + e) // catch generico (recibe string)
}Reglas del catch tipado:
- Se evaluan en orden; se ejecuta el primer catch cuyo tipo coincide.
- Un catch con tipo de clase padre atrapa instancias de subclases.
- Un catch sin tipo atrapa cualquier error (incluidos strings lanzados con
throw). throw exprcon una instancia de clase envia la instancia al catch.throw exprcon un valor no-instancia lanza un error de string.
try {
operacion_riesgosa()
} catch e {
if not starts_with(e, "error esperado") {
error("error inesperado: " + e)
}
}import "ruta/al/modulo.vsh"- La ruta es relativa al directorio de trabajo actual (CWD).
- El modulo se ejecuta en el scope global: sus funciones y variables quedan disponibles.
- La importacion circular se detecta y lanza un error.
- Importar el mismo fichero dos veces re-ejecuta sus definiciones (idempotente en la practica para funciones).
Convencion recomendada: poner la libreria en el mismo directorio que el script principal y usar rutas relativas a donde se lanza el binario vm.
| Funcion | Descripcion |
|---|---|
echo(v1, v2, ...) |
Imprime valores separados por espacio + newline |
print(v1, v2, ...) |
Imprime sin newline final |
println(v1, v2, ...) |
Imprime con newline final |
input([prompt]) |
Lee una linea de stdin |
| Funcion | Descripcion |
|---|---|
str(v) |
Convierte a string |
int(v) |
Convierte a int64 (trunca float) |
float(v) |
Convierte a float64 |
bool(v) |
Convierte a bool (truthy) |
type(v) |
Devuelve el tipo como string |
| Funcion | Descripcion |
|---|---|
len(s) |
Longitud en bytes |
upper(s) |
Todo en mayusculas |
lower(s) |
Todo en minusculas |
trim(s) |
Eliminar espacios al inicio y al final |
lstrip(s) |
Eliminar espacios solo al inicio |
rstrip(s) |
Eliminar espacios solo al final |
split(s, sep) |
Dividir por separador -> lista |
join(lista, sep) |
Unir lista de strings con separador |
starts_with(s, pref) |
True si s empieza con pref |
ends_with(s, suf) |
True si s termina con suf |
replace(s, old, new) |
Sustituir todas las ocurrencias de old |
substr(s, inicio [, len]) |
Subcadena (indices negativos cuentan desde el final) |
find_str(s, sub [, inicio]) |
Indice de sub en s (-1 si no existe) |
count_str(s, sub) |
Numero de ocurrencias de sub en s |
repeat(s, n) |
Repetir s n veces |
pad_left(s, n [, char]) |
Rellenar por la izquierda hasta longitud n |
pad_right(s, n [, char]) |
Rellenar por la derecha hasta longitud n |
contains(s, sub) |
True si sub esta en s |
char_code(s) |
Codigo ASCII/byte del primer caracter |
from_char(n) |
Caracter con codigo n |
is_numeric(s) |
True si s se puede parsear como numero |
hex(n) |
Representacion hex del entero ("0x1f") |
bin_str(n) |
Representacion binaria del entero ("0b1010") |
| Funcion | Descripcion |
|---|---|
len(lista) |
Numero de elementos |
append(lista, v) |
Anadir v al final (in-place via shared ref) |
pop(lista) |
Eliminar y devolver el ultimo elemento |
contains(lista, v) |
True si v esta en la lista (por valor) |
index_of(lista, v) |
Indice de v en la lista (-1 si no existe) |
range(n) |
Lista [0, 1, ..., n-1] |
range(s, e [, step]) |
Lista desde s hasta e-1 con paso step |
sort(lista) |
Copia de la lista ordenada |
reverse(lista) |
Copia de la lista al reves |
slice(lista, s [, e]) |
Sublista [s, e) con indices negativos soportados |
flat(lista) |
Aplanar un nivel (lista de listas) |
zip(a, b) |
Lista de pares [[a0,b0], [a1,b1], ...] |
enumerate(lista) |
Lista de pares [[0,v0], [1,v1], ...] |
sum(lista) |
Suma de todos los elementos numericos |
any_of(lista) |
True si algun elemento es truthy |
all_of(lista) |
True si todos los elementos son truthy |
unique(lista) |
Copia sin duplicados (preserva orden) |
// Ordenar y transformar
let nums = [3, 1, 4, 1, 5, 9, 2, 6]
println(sort(nums)) // [1, 1, 2, 3, 4, 5, 6, 9]
println(unique(sort(nums))) // [1, 2, 3, 4, 5, 6, 9]
println(reverse(slice(nums, 0, 4))) // [4, 1, 3]
println(sum(nums)) // 31
// Herramientas utiles
for pair in enumerate(["a", "b", "c"]) {
println(pair[0] + ": " + pair[1]) // 0: a, 1: b, 2: c
}
let coords = zip([1,2,3], [4,5,6]) // [[1,4],[2,5],[3,6]]
println(flat([[1,2],[3],[4,5]])) // [1, 2, 3, 4, 5]
println(any_of([false, false, true])) // true
println(all_of([1, 2, 3])) // true
println(index_of([10,20,30], 20)) // 1| Funcion | Descripcion |
|---|---|
keys(m) |
Lista de claves del mapa |
values(m) |
Lista de valores del mapa |
contains(m, clave) |
True si la clave existe en el mapa |
is_null(v) is_bool(v) is_int(v) is_float(v)
is_str(v) is_list(v) is_map(v) is_fn(v)
is_class(v) is_instance(v)type(v) devuelve el tipo como string: "null", "bool", "int", "float",
"string", "list", "map", "function", "class", "instance".
| Funcion | Descripcion |
|---|---|
isinstance(obj, clase) |
True si obj es instancia de clase o subclase |
classname(obj) |
Nombre de la clase de obj (o de la clase misma) |
is_class(v) |
True si v es un descriptor de clase |
is_instance(v) |
True si v es una instancia |
| Funcion | Descripcion |
|---|---|
abs(x) |
Valor absoluto |
min(a, b) |
Minimo de dos valores |
max(a, b) |
Maximo de dos valores |
clamp(x, lo, hi) |
Limitar x al rango [lo, hi] |
sign(x) |
-1, 0 o 1 segun el signo de x |
floor(x) |
Redondear hacia abajo |
ceil(x) |
Redondear hacia arriba |
round(x) |
Redondear al entero mas cercano |
sqrt(x) |
Raiz cuadrada |
pow(base, exp) |
Potencia |
log(x) |
Logaritmo natural (base e) |
log2(x) |
Logaritmo en base 2 |
log10(x) |
Logaritmo en base 10 |
sin(x) / cos(x) / tan(x) |
Trigonometria (radianes) |
gcd(a, b) |
Maximo comun divisor |
lcm(a, b) |
Minimo comun multiplo |
pi() |
Constante pi (3.14159...) |
inf() |
Infinito positivo (float) |
is_nan(x) |
True si x es NaN |
is_inf(x) |
True si x es infinito |
| Funcion | Descripcion |
|---|---|
rand() |
Float aleatorio en [0.0, 1.0) |
rand_int(min, max) |
Entero aleatorio en [min, max] (extremos incluidos) |
rand_float(min, max) |
Float aleatorio en [min, max) |
rand_choice(lista) |
Elemento aleatorio de la lista |
rand_shuffle(lista) |
Copia de la lista barajada aleatoriamente |
rand_seed(n) |
Inicializar el generador con semilla n (reproducible) |
println(rand()) // 0.73812...
println(rand_int(1, 6)) // dado: 1..6
println(rand_float(0.0, 100.0)) // 42.71...
println(rand_choice([10, 20, 30])) // 20
let deck = rand_shuffle(range(52))
rand_seed(42) // resultados reproducibles| Funcion | Descripcion |
|---|---|
exists(ruta) |
True si la ruta existe |
is_file(ruta) |
True si es un fichero regular |
is_dir(ruta) |
True si es un directorio |
basename(ruta) |
Nombre del fichero con extension |
dirname(ruta) |
Directorio padre |
stem(ruta) |
Nombre sin extension |
extension(ruta) |
Extension (incluye el punto) |
abspath(ruta) |
Ruta absoluta normalizada |
normpath(ruta) |
Normaliza . y .. sin resolver symlinks |
join_path(a, b, ...) |
Une componentes de ruta con el separador del SO |
file_size(ruta) |
Tamano del fichero en bytes (int) |
glob(patron) |
Lista de rutas que coinciden con el patron |
read_file(ruta) |
Leer fichero a string; lanza si no existe |
write_file(ruta, texto) |
Escribir string en fichero (sobreescribe) |
| Funcion | Descripcion |
|---|---|
getcwd() |
Devuelve el directorio de trabajo actual como string |
chdir(ruta) |
Cambia el directorio de trabajo actual |
listdir([ruta]) |
Lista los nombres de entradas en ruta (defecto .) |
listdir_full([ruta]) |
Como listdir pero devuelve rutas completas |
mkdir(ruta) |
Crea un directorio (un solo nivel) |
makedirs(ruta) |
Crea el arbol de directorios completo (como mkdir -p) |
rmdir(ruta) |
Elimina un directorio vacio o un fichero |
remove_file(ruta) |
Elimina un fichero |
remove_all(ruta) |
Elimina un arbol de directorios completo (recursivo) |
rename_path(origen, destino) |
Renombra o mueve un fichero o directorio |
copy_file(origen, destino) |
Copia un fichero (sobreescribe destino si existe) |
Ejemplos:
// Directorio actual y navegacion
let cwd = getcwd()
println("Estoy en: " + cwd)
chdir("..")
println("Ahora en: " + getcwd())
// Listar directorio
let entradas = listdir(".") // ["build", "main.cpp", ...]
let rutas = listdir_full("src") // ["src/foo.cpp", "src/bar.h", ...]
// Filtrar solo ficheros
let ficheros: list = []
for nombre in listdir(".") {
if (is_file(nombre)) {
append(ficheros, nombre)
}
}
println(ficheros)
// Rutas
let ruta = join_path("build", "out", "programa.velb") // build/out/programa.velb
println(abspath("../config")) // ruta absoluta
println(normpath("a/b/../c")) // a/c
// Crear y eliminar directorios
makedirs("build/out/ir")
copy_file("src/main.vel", "build/main.vel")
rename_path("build/viejo.vel", "build/nuevo.vel")
remove_all("build/tmp")
// Tamano de fichero
println(file_size("programa.velb") + " bytes")Nota sobre
appendy variables de bucle: Elfor var in listadefinevaren un scope hijo. Si tienes una lista acumuladora con el mismo nombre, sera ocultada dentro del bucle. Usa nombres distintos para el acumulador y la variable de iteracion:// INCORRECTO: 'dir' es string dentro del for, no la lista let dir: list = [] for dir in entradas { append(dir, ...) } // error: dir es string // CORRECTO: nombres distintos let resultado: list = [] for entrada in entradas { if (is_file(entrada)) { append(resultado, entrada) } }
| Funcion | Descripcion |
|---|---|
shell(cmd) |
Ejecutar comando; devuelve stdout+stderr como string |
shell_ex(cmd) |
Ejecutar comando; devuelve {"output": str, "code": int} |
sleep(ms) |
Pausar N milisegundos |
exit([codigo]) |
Salir del proceso con codigo de salida |
pid() |
PID del proceso actual |
cpu_count() |
Numero de CPUs logicas disponibles |
platform() |
"windows", "linux" o "macos" |
getenv(nombre) |
Valor de la variable de entorno (null si no existe) |
setenv(nombre, valor) |
Asignar variable de entorno |
// Obtener codigo de salida de un comando
let res = shell_ex("vestad -h")
println(res["output"])
println("Codigo: " + res["code"]) // 0 = exito
// Plataforma y entorno
println(platform()) // "windows"
println(getenv("PATH"))
setenv("MY_VAR", "hola")
// Informacion del proceso
println("PID: " + pid())
println("CPUs: " + cpu_count())| Funcion | Descripcion |
|---|---|
time_ms() |
Timestamp Unix en milisegundos (int) |
time_s() |
Timestamp Unix en segundos (float) |
time_now([fmt]) |
Fecha/hora actual formateada (defecto: "%Y-%m-%d %H:%M:%S") |
let t0 = time_ms()
// ... operacion ...
println("Tardo: " + (time_ms() - t0) + " ms")
println(time_now()) // "2026-04-26 14:30:00"
println(time_now("%d/%m/%Y")) // "26/04/2026"
println(time_now("%H:%M")) // "14:30"| Funcion | Descripcion |
|---|---|
format(tmpl, ...) |
Plantilla con {} o {0}, {1}... sustituidos por args |
json_str(v) |
Serializar valor a JSON (null/bool/int/float/str/list/map) |
println(format("Hola {} en {}", "mundo", 2026)) // "Hola mundo en 2026"
println(format("{0} + {0} = {1}", 3, 6)) // "3 + 3 = 6"
let datos = {"nombre": "Vesta", "version": 2, "activo": true}
println(json_str(datos))
// {"nombre":"Vesta","version":2,"activo":true}
let lista = [1, 2, [3, 4], null, false]
println(json_str(lista))
// [1,2,[3,4],null,false]| Funcion | Descripcion |
|---|---|
assert(cond [, msg]) |
Lanzar error si cond es falso |
error(msg) |
Lanzar VshRuntimeError con msg |
doc(fn_o_clase) |
Docstring de funcion o clase (o "") |
help(fn_o_clase) |
Imprime firma + docstring; devuelve el texto como string |
VestaShell incluye soporte multiplataforma para TCP, UDP y TLS, ademas de
funciones de alto nivel para HTTP/HTTPS. Todas las funciones de socket devuelven
un handle (entero) que identifica la conexion. Siempre cierra el socket con
socket_close(h) cuando ya no lo necesites.
// Conectar a un servidor TCP
let h = tcp_connect("httpbin.org", 80)
let sent = socket_send(h, "GET / HTTP/1.0\r\nHost: httpbin.org\r\n\r\n")
let resp = socket_recv_all(h) // leer hasta EOF
socket_close(h)
// Recibir en chunks (loop manual)
let chunk = socket_recv(h, 4096)
while len(chunk) > 0 {
procesar(chunk)
chunk = socket_recv(h, 4096)
}// Crear servidor TCP (acepta una conexion sincrona)
let srv = tcp_listen(8080) // escuchar en puerto 8080
let srv2 = tcp_listen(0) // puerto dinamico asignado por el SO
let client = tcp_accept(srv) // bloquea hasta conexion entrante
let data = socket_recv_all(client)
socket_send(client, "HTTP/1.0 200 OK\r\n\r\nHola!")
socket_close(client)
socket_close(srv)| Funcion | Descripcion |
|---|---|
tcp_connect(host, port) |
Conectar TCP; devuelve handle |
tcp_listen(port [, backlog]) |
Crear socket servidor; port=0 -> SO asigna puerto libre |
tcp_accept(handle) |
Aceptar conexion entrante (bloqueante); devuelve handle cliente |
socket_send(h, datos) |
Enviar string; devuelve bytes enviados |
socket_recv(h [, maxlen]) |
Recibir hasta maxlen bytes (defecto 4096); "" = EOF |
socket_recv_all(h) |
Leer hasta EOF y devolver todo como string |
socket_close(h) |
Cerrar socket y liberar recursos |
// Enviar datagram UDP
let u = udp_socket()
let n = udp_sendto(u, "payload", "127.0.0.1", 9) // port 9 = discard
socket_close(u)
// Receptor UDP (bind + recvfrom)
let u = udp_socket()
udp_bind(u, 5000) // escuchar en puerto 5000
let r = udp_recvfrom(u, 1024) // [datos, ip_remota, puerto_remoto]
println("de " + r[1] + ":" + str(r[2]))
println("datos: " + r[0])
socket_close(u)| Funcion | Descripcion |
|---|---|
udp_socket() |
Crear socket UDP; devuelve handle |
udp_bind(h, port) |
Enlazar a puerto local |
udp_sendto(h, datos, host, port) |
Enviar datagram; devuelve bytes enviados |
udp_recvfrom(h [, maxlen]) |
Recibir datagram; devuelve [datos, ip, puerto] |
// Conexion TLS manual (equivale a HTTPS raw)
let h = tls_connect("example.com", 443)
socket_send(h, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
let resp = socket_recv_all(h)
socket_close(h)| Funcion | Descripcion |
|---|---|
tls_connect(host, port) |
Conexion TCP + TLS handshake; devuelve handle cifrado |
Los handles TLS son compatibles con socket_send, socket_recv,
socket_recv_all y socket_close.
Estas funciones gestionan la conexion, peticion y cierre automaticamente y devuelven directamente el cuerpo de la respuesta como string.
// GET
let html = http_get("http://example.com/api/users")
let json = https_get("https://httpbin.org/get")
// POST
let resp = http_post("http://api.example.com/items", "nombre=VestaShell&v=1")
let resp2 = https_post("https://httpbin.org/post", '{"x":1}', "application/json")
// PUT
let resp3 = http_put("http://api.example.com/items/1", "nombre=Nuevo")
let resp4 = https_put("https://httpbin.org/put", '{"y":2}', "application/json")
// DELETE
let resp5 = http_delete("http://api.example.com/items/1")
let resp6 = https_delete("https://httpbin.org/delete")let r = http_request("PATCH", "https://httpbin.org/patch",
'{"campo":"valor"}', "application/json")
// r es un mapa con tres claves:
println(str(r["status"])) // ej: 200
println(r["headers"]) // cabeceras HTTP de la respuesta
println(r["body"]) // cuerpo de la respuesta| Funcion | Firma | Descripcion |
|---|---|---|
http_get(url) |
(url) -> str |
HTTP GET; devuelve cuerpo |
https_get(url) |
(url) -> str |
HTTPS GET; devuelve cuerpo |
http_post(url, body [, ct]) |
-> str |
HTTP POST |
https_post(url, body [, ct]) |
-> str |
HTTPS POST |
http_put(url, body [, ct]) |
-> str |
HTTP PUT |
https_put(url, body [, ct]) |
-> str |
HTTPS PUT |
http_delete(url) |
-> str |
HTTP DELETE |
https_delete(url) |
-> str |
HTTPS DELETE |
http_request(method, url [, body [, ct]]) |
-> map |
Peticion generica; devuelve {status, headers, body} |
El parametro ct es el Content-Type (defecto: application/x-www-form-urlencoded
para las funciones especializadas; application/json para http_request).
// Obtener IP publica via HTTPS
let r = https_get("https://httpbin.org/ip")
if contains(r, "origin") {
println("IP publica detectada en la respuesta")
}
// Peticion con control total de estado
let resp = http_request("POST", "https://httpbin.org/post",
"usuario=admin&pass=1234",
"application/x-www-form-urlencoded")
if resp["status"] == 200 {
println("POST exitoso")
}// Servidor echo de una sola conexion en puerto 7777
let srv = tcp_listen(7777)
println("Escuchando en :7777 ...")
let cli = tcp_accept(srv)
let msg = socket_recv(cli, 1024)
socket_send(cli, msg) // eco
socket_close(cli)
socket_close(srv)VestaShell puede cargar y llamar funciones de cualquier libreria dinamica del
sistema (.dll en Windows, .so en Linux) sin necesidad de compilar un plugin.
Es la forma de acceder a la Win32 API, librerias C de sistema o cualquier ABI
nativo desde un script .vsh.
| Funcion | Descripcion |
|---|---|
ffi_open(ruta) |
Carga la libreria y devuelve un handle (int) |
ffi_sym(handle, nombre) |
Busca el simbolo en la libreria; devuelve su direccion como int |
ffi_call(sym [, args...]) |
Llama a la funcion; retorna int64. Args: int/str/float/bool/null |
ffi_call_f(sym [, args...]) |
Igual que ffi_call pero retorna double |
ffi_close(handle) |
Descarga la libreria |
ffi_call_sym(ruta, nom [, args]) |
Atajo: open+sym+call+close en una sola expresion |
ffi_str(ptr_int) |
Lee un C-string desde un puntero retornado por ffi_call |
| Tipo VSH | Como se pasa a la funcion nativa |
|---|---|
int |
Directamente como int64_t |
bool |
1 o 0 como int64_t |
float |
Bits IEEE-754 empaquetados en int64_t (convenio de registros) |
string |
Puntero char* al buffer interno (vivo durante la llamada) |
null |
0 (puntero nulo) |
Limite: maximo 8 argumentos por llamada. Para funciones con mas parametros escribe un wrapper C fino y cargalo como FFI.
// Mostrar un dialogo en Windows
let user32 = ffi_open("user32.dll")
let msgbox = ffi_sym(user32, "MessageBoxA")
// HWND=0, texto, titulo, MB_OK=0
ffi_call(msgbox, 0, "Hola desde VestaShell!", "FFI Demo", 0)
ffi_close(user32)let r = ffi_call_sym("kernel32.dll", "GetTickCount")
echo("Uptime ms: " + str(r))let libc = ffi_open("libc.so.6")
let strlen = ffi_sym(libc, "strlen")
let n = ffi_call(strlen, "hola mundo") // str -> char*, retorna int
echo("longitud: " + str(n))
ffi_close(libc)// GetCommandLineA devuelve un LPSTR (char*)
let ptr = ffi_call_sym("kernel32.dll", "GetCommandLineA")
echo("Linea de comandos: " + ffi_str(ptr))VestaShell activa automaticamente el soporte VT100 en Windows
(ENABLE_VIRTUAL_TERMINAL_PROCESSING) al iniciarse, por lo que los codigos
ANSI funcionan en cmd.exe y PowerShell sin configuracion adicional.
Disponible como variable global ANSI. Uso: ANSI["RED"], ANSI["RESET"], etc.
| Clave | Secuencia | Efecto |
|---|---|---|
RESET |
\033[0m |
Restaura todo a defecto |
BOLD |
\033[1m |
Negrita |
DIM |
\033[2m |
Tenue |
ITALIC |
\033[3m |
Cursiva |
UNDERLINE |
\033[4m |
Subrayado |
BLINK |
\033[5m |
Parpadeo |
REVERSE |
\033[7m |
Video inverso |
STRIKE |
\033[9m |
Tachado |
BLACK..WHITE |
\033[30m..37m |
Colores fg normales |
BR_BLACK..BR_WHITE |
\033[90m..97m |
Colores fg brillantes |
BG_BLACK..BG_WHITE |
\033[40m..47m |
Colores de fondo |
BG_BR_* |
\033[100m..107m |
Fondos brillantes |
CLEAR |
\033[2J\033[H |
Limpiar pantalla y mover cursor al inicio |
CLEAR_LINE |
\033[2K\r |
Borrar la linea actual |
Estas funciones aceptan un argumento opcional. Con argumento envuelven el texto y añaden reset al final; sin argumento devuelven solo el codigo de escape.
echo(red("Error critico")) // texto en rojo con reset automatico
echo(green("OK")) // texto en verde
echo(bold("Titulo")) // negrita
echo(yellow() + "Atencion" + ANSI["RESET"]) // solo el codigo de apertura| Funcion | Color/estilo |
|---|---|
red(s?) |
Rojo fg |
green(s?) |
Verde fg |
yellow(s?) |
Amarillo fg |
blue(s?) |
Azul fg |
magenta(s?) |
Magenta fg |
cyan(s?) |
Cian fg |
white(s?) |
Blanco fg |
bold(s?) |
Negrita |
dim(s?) |
Tenue |
italic(s?) |
Cursiva |
underline(s?) |
Subrayado |
strike(s?) |
Tachado |
| Funcion | Descripcion |
|---|---|
colorize(texto, fg [, bg [, bold]]) |
Envuelve el texto con fg, bg opcional y negrita opcional |
ansi_rgb(r, g, b) |
Codigo fg en True Color (24 bits) |
ansi_rgb_bg(r, g, b) |
Codigo bg en True Color (24 bits) |
ansi_code(n) |
Codigo arbitrario \033[{n}m |
ansi_enable() |
Activa VT100 en Windows; no-op en Linux |
strip_ansi(texto) |
Elimina todas las secuencias ANSI de un string |
| Funcion | Descripcion |
|---|---|
ansi_cursor_up(n?) |
Mueve el cursor N lineas arriba |
ansi_cursor_down(n?) |
Mueve el cursor N lineas abajo |
ansi_cursor_left(n?) |
Mueve el cursor N columnas a la izquierda |
ansi_cursor_right(n?) |
Mueve el cursor N columnas a la derecha |
ansi_cursor_pos(fila,col) |
Posiciona el cursor en (fila, col) |
ansi_clear() |
Limpia la pantalla |
ansi_clear_line() |
Borra la linea actual |
// Barra de progreso simple
fn progreso(pct: int) {
let lleno = pct / 5
let vacio = 20 - lleno
let barra = green(repeat("#", lleno)) + dim(repeat("-", vacio))
echo("\r[" + barra + "] " + str(pct) + "%")
}
// Tabla coloreada
echo(bold("Nombre") + " " + bold("Estado"))
echo(cyan("servidor-01") + " " + green("OK"))
echo(cyan("servidor-02") + " " + red("FALLO"))
// True Color
echo(ansi_rgb(255, 128, 0) + "Naranja" + ANSI["RESET"])
// colorize con fondo
echo(colorize("AVISO", "black", "yellow", true))
// Limpiar consola y escribir en posicion fija
echo(ansi_clear())
echo(ansi_cursor_pos(5, 10) + "Texto en fila 5 col 10")
// Eliminar escapes para guardar en fichero de log limpio
let log_line = red("[ERROR]") + " mensaje de error"
write_file("app.log", strip_ansi(log_line) + "\n")./vm --interpreteAbre un shell interactivo de VestaShell al estilo Python:
VestaShell REPL interactivo - escribe 'exit' o 'quit' para salir
>>> let x = 5
>>> x * 3
15
>>> fn suma(a, b) { return a + b }
>>> suma(2, 3)
5
>>> class Punto {
... fn __init__(self, x, y) { self.x = x; self.y = y }
... }
>>> let p = Punto(1, 2)
>>> p.x
1
>>> exit
Comportamiento:
- Las expresiones simples imprimen su valor (si no es
null) comorepr. - Los bloques multilinea se detectan contando
{/}. - El prompt cambia a
...cuando se espera continuacion. - Los errores de sintaxis y ejecucion se muestran sin terminar el REPL.
- Las definiciones (
fn,class,let) persisten durante toda la sesion. doc(fn)yhelp(fn)son especialmente utiles en el REPL para explorar el API.
Cuando el script se ejecuta desde dentro del REPL con script archivo.vsh,
todos los comandos del REPL son accesibles como si fueran funciones:
// Compilar y ejecutar un programa Vesta desde un script .vsh
build("src/main.vel", "-o", "programa.velb")
run("mi-app", "programa.velb", "--schedulers", "4")
// Navegar el sistema de ficheros
cd("src/")
ls("*.vel")
pwd()
// Ver el estado de las VMs
vms()Los argumentos se pasan como strings y se reensamblan en la linea de comando antes de enviarse al despachador del REPL.
// Comentario de linea: desde // hasta el fin de linea
/* Comentario de bloque:
puede abarcar multiples lineas. */
let valor = 1 + 2 + \
3 + 4 // barra invertida continua la sentenciaLos comentarios // y /* */ son compatibles con el preprocesador de Vesta,
lo que permite usar directivas como #include en ficheros .vsh procesados
por vpp antes de interpretarlos.
- Indentacion: 4 espacios (recomendado).
- Nombres de variables y funciones:
snake_case. - Constantes globales:
UPPER_SNAKE_CASE. - Terminadores de sentencia: NEWLINE o
;(ambos equivalentes). - Los bloques
{}siempre en la misma linea que la palabra clave que los abre.
Ver la carpeta examples_codes_vsh/ del repositorio para scripts de ejemplo
completos que cubren toda la sintaxis y los casos de uso tipicos:
| Fichero | Contenido |
|---|---|
01_tipos_basicos.vsh |
Tipos, variables, operadores |
02_strings.vsh |
Strings, interpolacion, builtins |
03_control_flujo.vsh |
if/elif/else, while, for/in, range |
04_funciones.vsh |
Funciones, closures, orden superior, docstrings |
05_listas_mapas.vsh |
Listas, mapas, indexado, iteracion |
06_errores.vsh |
try/catch, error(), assert() |
07_matematicas.vsh |
Builtins matematicos |
08_archivos.vsh |
Sistema de ficheros |
09_lib_utils.vsh + 09_import.vsh |
Libreria y sistema de importacion |
10_clases.vsh |
Clases, herencia, POO, isinstance, classname, help |
11_errores_tipados.vsh |
Errores personalizados, throw, catch tipado, jerarquia |
12_tipado.vsh |
Anotaciones de tipo en let y parametros de funcion |
13_sockets.vsh |
TCP, UDP, TLS, HTTP/HTTPS GET/POST/PUT/DELETE |
14_ffi.vsh |
Llamadas a librerias nativas, Win32 API, ffi_call |
15_ansi.vsh |
Colores, estilos, cursor, True Color, progreso |
Ejecutar la suite completa (desde el directorio raiz del proyecto):
for f in examples_codes_vsh/0[1-9]_*.vsh examples_codes_vsh/1[0-9]_*.vsh; do
./build/vm.exe --script "$f"
done