HoopLang Docs
/
Playground
Referencia del lenguaje

Módulos: import, bibliotecas y componentes

Un proyecto de HoopLang se reparte en varios archivos .hoop. Hay dos maneras de usar un archivo desde otro:

  • import trae los nombres de una biblioteca: hilos (thread), constantes (const) y formas (shape). Así se escriben una sola vez los colores de un club, la carta de una marca o las medidas comunes.
  • place trae un diseño entero como componente, con valores para sus param, escalado, girado y ubicado (en un hoop o en una capa de otro design).

Este capítulo describe las formas de import, qué se puede importar, cómo se resuelven las rutas, los ciclos, las bibliotecas de paleta de marca y los componentes con parámetros. Los diagnósticos citados están en Diagnósticos.

Formas de import#

Salida del compilador
import_decl := "import" IDENT "from" STRING
             | "import" "{" IDENT ( "as" IDENT )? ( "," IDENT ( "as" IDENT )? )* "}" "from" STRING

Un import es una declaración de nivel superior. Como TypeScript, cada import dice qué trae y con qué nombre: no existe "importar todo sin prefijo".

Para los ejemplos, la biblioteca lib/colores.hoop:

HoopLang
// file: lib/colores.hoop
// Biblioteca del club
thread negro    = #1c1616 "Negro" { brand: "Madeira", code: "1800" }
thread amarillo = #fcd414 "Amarillo" { brand: "Madeira", code: "1124" }
const borde = 3mm
shape escudo = polygon((0mm, 0mm), (40mm, 0mm), (20mm, 50mm))

Espacio de nombres: import x from "archivo"#

Trae todos los thread, const y shape de la biblioteca, cada uno con el prefijo x.:

HoopLang
import club from "lib/colores.hoop"
design "N" { canvas: rect(size: (40mm, 50mm)) }
layer a { fill club.amarillo { shape: club.escudo.inset(club.borde) } }
layer b { satin club.negro { border: club.escudo, width: club.borde } }
  • club.negro es un nombre calificado (sin paréntesis); con paréntesis, club.escudo.inset(2mm), es una llamada a método sobre la forma importada.
  • Los miembros de forma también funcionan sobre nombres calificados: club.escudo.center, club.escudo.width.

Selección: import { a, b as c } from "archivo"#

Trae sólo los nombres listados, sin prefijo; as les da otro nombre en este archivo (para evitar un choque o abreviar):

HoopLang
import madeira from "lib/madeira.hoop"
import { escudo, borde as marco } from "lib/colores.hoop"

thread negro = #111111 "Negro local"

design "Insignia" { canvas: rect(size: (40mm, 50mm)) }
layer fondo { fill madeira.c1172 { shape: escudo.inset(marco) } }
layer borde { satin madeira.c1800 { border: escudo, width: marco } }
layer centro { fill negro { shape: circle(center: escudo.center, r: 3mm) } }

con lib/madeira.hoop:

HoopLang
// file: lib/madeira.hoop
thread c1800 = #1c1c1c "Black" { brand: "Madeira", code: "1800" }
thread c1172 = #d8a21b "Gold" { brand: "Madeira", code: "1172" }
  • La lista no puede estar vacía (E000 import { … } needs at least one name); los nombres se separan con comas.
  • Un nombre que la biblioteca no tiene es E007 archivohas nox, con sugerencia si hay uno parecido: E007: lib/colores.hoop has no negor; did you mean negro?``.

La forma eliminada: import "archivo"#

Una versión anterior traía todo sin prefijo. Hoy es un error de sintaxis que sugiere las dos formas válidas:

HoopLang
// error E000
import "colores.hoop"
design "D" { canvas: rect(size: (20mm, 20mm)) }
Terminal
e000c.hoop:1:8: error E000: importing everything mixes names: use `import club from "file"` (then club.name) or `import { name } from "file"`

Las comillas de la ruta pueden ser simples o dobles; hoop fmt las deja dobles.

Qué es una biblioteca#

Una biblioteca es un archivo .hoop sin design, sin hoop y sin layer. Puede contener thread, const, shape, param e import.

  • Importar un archivo que tiene design, hoop o layer es E007 … is not a library: it has a design (only thread, const, shape and import) (o a hoop, a layer).
  • Se exporta: cada thread (con su color, nombre, marca y código), cada const y cada shape declarados en la biblioteca.
  • No se exporta: lo que la biblioteca importa (una biblioteca no reexporta) ni sus param, que sí puede usar por dentro:

Archivo lib/conparam.hoop:

HoopLang
// file: lib/conparam.hoop
param p = 2mm
const q = p * 2

import { q } from "lib/conparam.hoop" verifica; import { p } … da E007 lib/conparam.hoophas nop; did you mean q?.

Cómo se evalúa#

  • Cada biblioteca se verifica sola: no ve los nombres del archivo que la importa.
  • Se importan sus valores ya evaluados. Una const de la biblioteca que usa otra sigue valiendo lo mismo aunque afuera se llame distinto (o no se importe).
  • Una biblioteca que se importa varias veces (por dos import o por dos caminos) se lee y verifica una sola vez por compilación.
  • Si la biblioteca tiene errores, se informan en la línea del import, con el archivo y la posición, y no se importa nada de ella:

Archivo lib/mala.hoop:

HoopLang
// error E001
// file: lib/mala.hoop
const ok = 1mm
const x: Angle = 3mm
HoopLang
// error E001
import mala from "lib/mala.hoop"
design "D" { canvas: rect(size: (20mm,20mm)) }
Terminal
imp_lib.hoop:1:1: error E001: in `lib/mala.hoop` 2:10: error: `x` is declared Angle but its value is Length

Nombres repetidos#

Todo nombre importado debe ser único en el archivo:

SituaciónDiagnóstico
Un nombre local igual a uno importado (el import puede estar antes o después)E007 negrois already imported fromlib/colores.hoop; rename one of them (as in the import) en la declaración local
El mismo nombre traído por dos importE007 negrois already imported fromlib/colores.hoop`` en el segundo
Dos declaraciones locales igualesE007 negro is already defined

Con un espacio de nombres no hay choques: madeira.c1800 y un thread c1800 local conviven.

Hilos importados y archivo de máquina#

Al archivo de máquina llegan los hilos declarados en el propio archivo y, de los importados, sólo los que se usan en alguna primitiva (una paleta de marca trae cientos). Su nombre, marca y código viajan al PES y a los demás formatos que los admiten.

Resolución de rutas#

Las rutas de import y de place son cadenas con / como separador y se resuelven relativas a la carpeta del archivo principal, es decir, el archivo que se pasa a hoop check / hoop build (o el archivo que se compila en el playground). Esto vale también para los import y place anidados: un archivo en sub/ que importa "colores.hoop" busca colores.hoop junto al archivo principal, no en sub/.

Con esta estructura:

Salida del compilador
ruta.hoop
sub/insignia.hoop     (import { negro } from "colores.hoop")
sub/colores.hoop

Archivo ruta.hoop:

HoopLang
// no-check Demostración de rutas con archivos sub/insignia.hoop y sub/colores.hoop cuyo contenido completo no se muestra.
hoop "Ruta" {
  frame: Brother_100x100
  place "sub/insignia.hoop" at (50mm, 50mm)
}
Terminal
$ hoop check ruta.hoop
ruta.hoop:3:3: error E007: in `sub/insignia.hoop` 1:1: error: cannot read `colores.hoop`: No such file or directory (os error 2)
ruta.hoop:3:3: error E003: in `sub/insignia.hoop` 3:11: error: undeclared thread `negro` (declare it with thread)
$ hoop check sub/insignia.hoop
sub/insignia.hoop: ok — 1 threads, 1 layers, 1 objects

Detalles:

  • En la herramienta de línea de comandos, la ruta se une a la carpeta del archivo principal tal cual (las rutas absolutas y .. funcionan como en el sistema de archivos).
  • En el playground (y en la API WASM), los archivos del proyecto se nombran por su ruta dentro del proyecto; la ruta se une a la carpeta del archivo principal y se resuelven . y .. (por ejemplo, desde galeria/insignia.hoop, "../bibliotecas/janome.hoop" es bibliotecas/janome.hoop). Un archivo que no está da cannot read …: not found in the project (…).
  • Un archivo que no se puede leer es E007 cannot read …: <motivo> en la línea del import o del place.
  • Las funciones de la biblioteca hooplang no hacen E/S: quien llama entrega un cargador (ruta → texto). Sin cargador (hooplang::check), un import es E007 import needs file access (compile with a file loader).

Ciclos#

Un ciclo de import es error, con la cadena completa, informado en cada nivel (cada archivo lo informa en su import):

Archivo lib/ca.hoop:

HoopLang
// error E007
// file: lib/ca.hoop
import { b } from "lib/cb.hoop"
const a = 1mm

Archivo lib/cb.hoop:

HoopLang
// error E007
// file: lib/cb.hoop
import { a } from "lib/ca.hoop"
const b = 1mm
HoopLang
// error E007
design "D" { canvas: rect(size: (20mm,20mm)) }
import ca from "lib/ca.hoop"
Terminal
imp_lib.hoop:2:1: error E007: in `lib/ca.hoop` 1:1: error: in `lib/cb.hoop` 1:1: error: import cycle: lib/ca.hoop → lib/cb.hoop → lib/ca.hoop

Lo mismo con place: un diseño que se coloca a sí mismo, directa o indirectamente, es E007 place cycle: …, informado tal cual (la cadena ya nombra los archivos) en el place de más afuera:

Archivo ciclo.hoop:

HoopLang
// error E007
// file: ciclo.hoop
design "Ciclo" { canvas: rect(size: (40mm, 40mm)) }
layer a { place "ciclo.hoop" at (20mm, 20mm) }
Terminal
ciclo.hoop:2:11: error E007: place cycle: ciclo.hoop → ciclo.hoop

Bibliotecas de paleta de marca#

Una carta de hilos de una marca es una biblioteca con un thread por color, con brand y code:

HoopLang
thread c1800 = #1c1c1c "Black" { brand: "Madeira", code: "1800" }

El repositorio incluye docs/lenguaje/bibliotecas/brother.hoop (paleta PEC, sin códigos) y docs/lenguaje/bibliotecas/janome.hoop (con su código Janome); sus RGB son aproximados y su cabecera lo dice. Se usan con un espacio de nombres o seleccionando:

HoopLang
import { c002 as negro } from "../bibliotecas/janome.hoop"

(así empieza docs/lenguaje/galeria/polera.hoop).

La herramienta palette#

palette genera bibliotecas ya formateadas:

Terminal
cargo run -q --release --bin palette -- csv carta.csv Madeira > madeira.hoop
cargo run -q --release --bin palette -- brother > brother.hoop
cargo run -q --release --bin palette -- janome > janome.hoop
  • csv <archivo.csv> <Marca> lee filas código,nombre,color, con el color como #rrggbb o rrggbb, con o sin cabecera; los campos pueden ir entre comillas (para nombres con comas). Una fila con menos de tres campos o un color inválido detiene la herramienta con line N: … y código de salida
  • El nombre de cada hilo sale del código: minúsculas, _ entre palabras y, si empieza con un dígito, c delante (1800 → c1800). Los nombres repetidos se omiten (queda el primero).
  • La cabecera dice la marca, la cantidad de colores, la fuente de los datos y cómo importarla:
Salida del compilador
// Madeira thread library (2 colors).
// Source: carta.csv
// Usage: import madeira from "madeira.hoop"  (then madeira.<name>)

Componentes: param y place con argumentos#

Un design puede declarar parámetros: valores por defecto que quien lo coloca puede cambiar.

Salida del compilador
param_decl := "param" IDENT ( ":" TIPO )? "=" expr

Archivo parche.hoop:

HoopLang
// file: parche.hoop
param nombre = "ANA"
param ancho: Length = 50mm
design "Parche" { canvas: rect(size: (ancho, 20mm), radius: 4mm) }
thread fondo = #f2f2f2 "Fondo"
thread borde = #1f3a8a "Azul"
layer base { fill fondo { shape: canvas } }
layer texto { text borde { value: nombre, height: 8mm, at: canvas.center + (0mm, 3mm), align: center } }
layer marco { satin borde { border: canvas, width: 2mm } }
  • El tipo es el declarado o, si no se declara, el del valor por defecto. Un valor por defecto de otro tipo que el declarado es E001 x is declared T but its default is U.
  • Un param es un nombre más del archivo (no puede repetir otro nombre) y se usa como una const. Compilado solo, el diseño usa los valores por defecto.

Quien coloca el diseño da valores con argumentos con nombre entre paréntesis, pegados a la ruta (hoop fmt los escribe así; separados por un espacio también vale):

HoopLang
hoop "Dos parches" {
  frame: Brother_200x200
  fabric: Twill
  margin: 10mm
  place "parche.hoop" at (100mm, 60mm)
  place "parche.hoop"(nombre: "LUIS", ancho: 70mm) at (100mm, 120mm) anchor: top_center
}

Los argumentos se evalúan en el archivo que coloca (pueden usar sus nombres y las variables de un for) y el diseño se verifica y compila con esos valores: cada place con argumentos distintos es un diseño distinto.

Validación de argumentos#

HoopLang
// error E001 E007
hoop "H" {
  frame: Brother_200x200
  place "parche.hoop"(nombr: "X") at (100mm, 50mm)
  place "parche.hoop"(nombre: 3mm) at (100mm, 100mm)
  place "cuadro.hoop"(nombre: "X") at (100mm, 150mm)
}
Terminal
h_noparam.hoop:3:30: error E007: `parche.hoop` has no param `nombr`; did you mean `nombre`?
h_noparam.hoop:4:3: error E001: in `parche.hoop` 1:1: error: `nombre` is String but it was given Length
h_noparam.hoop:5:31: error E007: `cuadro.hoop` has no params
  • Un argumento que no es param del diseño: E007, en la posición del valor del argumento, con sugerencia, con la lista de parámetros (has no param x (a, b)) o diciendo que no tiene ninguno.
  • Un argumento de otro tipo: E001 informado dentro del diseño, en la línea de su param, y reportado en el place; el diseño sigue con el valor por defecto para no producir errores en cascada.
  • Un argumento sin nombre: E001 design arguments are named: place "x.hoop"(nombre: "ANA").
  • Si un mismo nombre se da dos veces, vale el último (no hay diagnóstico).

Véase también#