HoopLang Docs
/
Playground
Referencia del lenguaje

Contenido de las capas y control

Una capa (layer, ver Declaraciones) contiene elementos de capa: primitivas de bordado y construcciones que las ordenan (seq), las contienen (inside), las repiten (for), las eligen (if) o traen otro diseño (place). Todo se resuelve al compilar: un for se despliega en copias y un if se reduce a la rama elegida. Las primitivas (fill, satin, run, triple, text) se describen en Primitivas y Texto.

Gramática#

ebnf
item_de_capa = primitiva
             | "seq" "{" { item_de_capa } "}"
             | "inside" expr "{" { item_de_capa } "}"
             | "for" identificador [ "," identificador ] "in" expr [ ".." expr ]
                 "{" { item_de_capa } "}"
             | si
             | place ;
si           = "if" expr "{" { item_de_capa } "}"
               [ "else" ( si | "{" { item_de_capa } "}" ) ] ;
place        = "place" texto [ "(" args ")" ] "at" expr
               { identificador ":" expr } ;
primitiva    = ( "fill" | "satin" | "run" | "triple" | "text" )
               nombre_de_hilo props ;

Las palabras seq, inside, for, if y place se reconocen por contexto: seq seguido de {; inside seguido de algo distinto de {; for seguido de un identificador; place seguido de un texto. En cualquier otro caso la palabra se lee como el inicio de una primitiva, lo que da E000: inside is not a primitive (fill, satin, run, triple or text).

Orden de bordado#

  • Las capas se bordan en el orden escrito.
  • Dentro de una capa, el compilador elige el orden de los objetos (menos cortes y cambios de hilo).
  • Un seq fija el orden de su contenido.

seq#

ebnf
seq = "seq" "{" { item_de_capa } "}" ;   (* sin seq ni place *)

Los objetos de un seq se bordan seguidos y en el orden escrito, sin otro objeto entre medio, aunque cueste un cambio de hilo. El bloque completo puede ir antes o después de los demás objetos de la capa.

  • Un seq contiene primitivas y también for, if e inside: se expanden en orden y todo lo que producen forma parte del mismo bloque (las primitivas de un inside quedan además en su contenedor). Otro seq o un place dentro de un seq es E000 (`seq` is not allowed inside `seq` (…), `place` is not allowed inside `seq` (a placed design keeps its own order)). Un seq también puede ir dentro de un for, un if o un inside.
  • Cada seq escrito, y cada vuelta de un for que contiene un seq, forma un bloque independiente.
HoopLang
shape parche = rect(at: (20mm, 20mm), size: (60mm, 40mm), radius: 4mm)
layer aplique {
  seq {
    run negro { path: parche }
    triple negro { path: parche, passes: 3 }
    satin negro { border: parche, width: 3mm }
  }
}

inside#

ebnf
inside = "inside" expr "{" { item_de_capa } "}" ;

Declara que lo de adentro debe quedar dentro de una forma. La expresión debe ser una forma (o un camino cerrado); si no, E001: expected a Shape, not Length.

  • Si la huella cosida de un objeto de adentro sale de la forma, el compilador da la advertencia W009, con cuánto y por dónde (fill overflows the inside shape at 4:11 by 2.8 mm at the bottom). No es un error: un detalle puede salir de la forma a propósito.
  • Los inside se anidan; cada objeto se compara con el más cercano que lo contiene.
  • Pueden contener cualquier elemento de capa (seq, for, if, place, otros inside). Un place dentro de un inside también se revisa contra esa forma.
  • El lienzo (canvas) se verifica aparte y es error (E006); inside es para las formas del diseño.
HoopLang
shape escudo = rect(at: (10mm, 10mm), size: (80mm, 80mm), radius: 6mm)
layer frente {
  fill blanco { shape: escudo }
  inside escudo.inset(2mm) {
    fill negro { shape: circle(center: escudo.center, r: 20mm) }
  }
}

for#

ebnf
for = "for" identificador [ "," identificador ] "in" expr [ ".." expr ]
      "{" { item_de_capa } "}" ;

Repite su cuerpo, una vez por valor:

  • for x in a..b recorre los enteros desde a hasta b sin incluir b. Ambos extremos deben ser Int (E001: a range is Int..Int (for i in 0..4)). Si b ≤ a, el cuerpo no se ejecuta.
  • for x in lista recorre los elementos de una List, en orden. Cualquier otro valor es E001: for goes over a range (0..4) or a List, not Int.
  • for x, i in … además da el índice de la vuelta en i (Int, desde 0). Vale tanto para rangos como para listas.

Reglas:

  • Un for da a lo más 1000 vueltas; más es E002: 1001 iterations: a loop goes up to 1000 y no se despliega.
  • La variable y el índice no pueden tapar un nombre existente (constante, forma, parámetro, hilo, otra variable de un for exterior): E007: the loop variable i hides another name: choose another.
  • Las variables existen solo dentro del cuerpo; al terminar el bucle dejan de existir.
  • El cuerpo admite cualquier elemento de capa, incluidos otros for.
  • Un mismo diagnóstico en la misma posición se informa una sola vez, aunque ocurra en varias vueltas.
HoopLang
shape punta = polygon((50mm, 10mm), (54mm, 40mm), (46mm, 40mm))
layer sol {
  for i in 0..8 {
    fill negro { shape: punta.rotate(i * 45deg, (50mm, 50mm)) }
  }
  for c, i in [(20mm, 80mm), (50mm, 80mm), (80mm, 80mm)] {
    fill blanco { shape: circle(center: c, r: 3mm + i * 1mm) }
  }
}

Con grid (ver Expresiones § grid) un for ubica copias en filas y columnas:

HoopLang
layer puntos {
  for i in 0..6 {
    fill negro { shape: circle(center: grid(i, cols: 3, step: (30mm, 30mm), start: (20mm, 30mm)), r: 4mm) }
  }
}

if#

ebnf
si = "if" expr "{" { item_de_capa } "}"
     [ "else" ( si | "{" { item_de_capa } "}" ) ] ;

Elige qué bordar. El else es opcional y else if encadena.

  • La condición debe ser Bool: E001: a condition is a Bool (a comparison like len(name) > 8), not Int.
  • Se verifican las dos ramas (los errores de la rama que no se toma también se informan), pero solo se borda la elegida.
  • Si la condición no se puede evaluar (tiene un error), no se borda ninguna rama.

Para elegir un valor en vez de elementos de capa, use la expresión if c { a } else { b } (ver Expresiones § Expresión if).

HoopLang
param nombre = "MARÍA JOSÉ"
layer texto {
  if len(nombre) > 8 {
    text blanco { value: nombre, box: canvas.inset(5mm) }
  } else if len(nombre) > 4 {
    text blanco { value: nombre, height: 12mm, at: canvas.center, align: center }
  } else {
    text blanco { value: nombre, height: 16mm, at: canvas.center, align: center }
  }
  for i in 0..6 {
    if i mod 2 == 1 {
      fill negro { shape: circle(center: (10mm + i * 14mm, 90mm), r: if i > 3 { 4mm } else { 3mm }) }
    }
  }
}

place#

ebnf
place = "place" texto [ "(" args ")" ] "at" expr { identificador ":" expr } ;

Trae otro diseño (otro archivo .hoop) como componente, en el punto at. La ruta es relativa al archivo que contiene el place.

ParteTipoPor defectoSignificado
( … )argumentos con nombre—valores para los param del diseño colocado
atPointobligatoriodónde va el ancla
anchorNamecenterqué punto del lienzo del componente va en at: center, top_left, top_center, top_right, center_left, center_right, bottom_left, bottom_center, bottom_right
rotateAngle0deggiro del componente
scaleRatio (> 0)100%escala del componente

Semántica:

  • El componente se compila con la tela (fabric) de quien lo coloca; su lienzo da el ancla. Sus hilos y sus seq vienen con él, y el compilador optimiza el orden de todo junto.
  • Lo cosido del componente se revisa contra el lienzo y contra los inside del archivo que lo coloca.
  • Los errores del componente se informan en la posición del place, con la posición original dentro del otro archivo.

Errores:

  • at que no es un punto: E001: at takes a Point (x, y), not Length.
  • Argumento sin nombre: E001: design arguments are named: place "x.hoop"(nombre: "ANA").
  • Argumento para un param que no existe: E007: parche.hoophas no paramnombr; did you mean nombre?.
  • Argumento de otro tipo que el param: E001: in `parche.hoop` 1:1: error: `nombre` is String but it was given Int.
  • Propiedad desconocida: E007: place has no foo (anchor, rotate, scale); anchor fuera de la lista es E002; rotate o scale de otro tipo, E001; scale en cero o negativo, E002: scale must be greater than 0%.
  • Un modificador o argumento repetido: E007: duplicate scale``.
  • Archivo que no se puede leer: E007: cannot read nope.hoop: ….
  • Un diseño que se coloca a sí mismo, directa o indirectamente, es un ciclo: E007: place cycle: a.hoop → a.hoop, en el place de más afuera.
HoopLang
layer insignias {
  place "parche.hoop"(nombre: "LUIS") at (50mm, 20mm) anchor: top_center scale: 90%
  for nombre, i in ["ANA", "SOFÍA"] {
    place "parche.hoop"(nombre: nombre, alto: 5mm) at grid(i, cols: 1, step: (0mm, 25mm), start: (50mm, 60mm)) rotate: 5deg
  }
}

En el ejemplo, parche.hoop es el archivo de la sección param.

En un hoop#

Un hoop (ver Bastidor) admite place, for e if con las mismas reglas, pero no primitivas, seq ni inside: dentro de un if o for del hoop son E007: a hoop only places designs (place, for); stitches go in a design, y directamente en el hoop, E000: a hoop only places designs (move fill into a design file).