02 / 06

Primer programa

Un programa de Nyx es un archivo. No hay proyecto que crear, ni archivo de build que escribir, ni runtime que distribuir: un comando convierte el archivo en un binario nativo y lo ejecuta.

hello.nx

Todo programa arranca en fn main(). Pon esto en un archivo llamado hello.nx:

fn main() {
    print("Hello, world!")
}

Y ejecútalo:

$ nyx hello.nx
Hello, world!

Qué pasó recién

El comando nyx es un wrapper sobre toda la toolchain. Cuando le das un archivo hace cuatro cosas, en un directorio temporal que borra al salir:

  1. El compilador lee hello.nx y emite LLVM IR.
  2. clang compila ese IR junto con el runtime en C — el recolector de basura, strings, arrays, maps, sockets, hilos — en un solo ejecutable.
  3. El ejecutable corre, con los argumentos que hayas puesto después del nombre del archivo.
  4. El directorio temporal desaparece.

Nada se interpreta y nada queda dando vueltas. La salida es un binario nativo de verdad; la única razón por la que no lo ves es que este comando lo tira. El paso 03 muestra cómo conservarlo.

Si la compilación falla, todo el log del build se vuelca a la salida de error — los diagnósticos del compilador y, con NYX_DIAG=json, su forma legible por máquina. Un fallo nunca es una sola línea muda.

Chequear antes de ejecutar

Compilar y linkear toma segundos. Chequear tipos toma una fracción de eso, y ahí se atrapan casi todos los errores:

$ nyx check hello.nx

nyx check analiza y chequea tipos, y ahí se detiene — no linkea y no ejecuta. Cuando sale bien imprime un índice legible por máquina de lo que encontró (DEF: para definiciones, SYM: para símbolos en alcance) y sale 0; cuando falla imprime el diagnóstico y sale 1. El código de salida es el punto: nyx check && nyx hello.nx se puede encadenar sin riesgo.

Leer un error

Supongamos que greet.nx llama a una función que no existe:

fn greet(name: String) -> String {
    return "Hello, " + name + "!"
}

fn main() {
    print(greet("world"))
    print(greeting("Nyx"))
}
$ nyx greet.nx

  nyx v0.31.0

  lex     1538 tokens
  parse   OK
  ✗ error [NYX1002] in 'main' (line 7): 'greeting' not declared (did you mean 'greet'?)
  check   FAILED
error: compilation failed for greet.nx

Tres cosas para sacar de esa línea. El códigoNYX1002 — es estable y se puede buscar. La ubicación es la función que lo contiene y la línea dentro del archivo. Y la sugerencia entre paréntesis es el compilador comparando tu identificador contra los que sí conoce; cuando ofrece uno, casi siempre acierta.

Los nombres de fase arriba del error dicen hasta dónde llegó: el lexer produjo tokens, el parser terminó bien, y el fallo vino del chequeador. Un error de parser en vez de uno de chequeo significa que la forma del código está mal, no el significado.

Dos herramientas más

nyx vet lee el archivo buscando cosas que el compilador acepta pero que probablemente no querías: variables sin usar, código muerto, imports sin usar. También nombra las trampas conocidas del lenguaje en vez de dejar que se conviertan más tarde en un error de parser confuso — cada una como un aviso con su código, el archivo, la línea, y dónde está la explicación larga:

$ nyx vet shapes.nx
warning[W101] shapes.nx:2: Enum variants use `.`, not `::`: `Shape.Circle(5)`, never `Shape::Circle(5)`. (docs/nyx/LLM.md §5 enum-dot-not-colons)

nyx fmt imprime el archivo en su forma canónica por la salida estándar. No edita nada, así que no se pierde nada si el resultado te sorprende:

$ nyx fmt hello.nx
fn main() {
    print("Hello, world!")
}

Las dos usan src/main.nx cuando no les das un archivo — que es el punto de entrada de un proyecto, y ese es el paso siguiente.