Estructura léxica
Este capítulo describe cómo el compilador separa el texto fuente de un archivo
.hoop en tokens: espacios, comentarios, identificadores, palabras clave,
literales, operadores y puntuación. Lo que aquí se describe corresponde al
analizador léxico (crates/hoop/src/lexer.rs); cómo se combinan los tokens se
describe en Expresiones y Declaraciones.
Codificación y posiciones#
El fuente es texto Unicode (UTF-8). El analizador trabaja por caracteres
(puntos de código), no por bytes: las columnas de los diagnósticos
(archivo:línea:columna) cuentan caracteres desde 1, y cada salto de línea
\n incrementa la línea.
Cualquier carácter que Unicode considere espacio en blanco (espacio,
tabulación, \r, \n, …) separa tokens y se descarta. Los saltos de línea
no tienen significado sintáctico: una declaración puede ocupar varias líneas.
Un carácter que no inicia ningún token es un error:
error E000: unexpected character `@`Comentarios#
comentario_linea = "//" { cualquier carácter salvo "\n" } ;
comentario_bloque = "/*" { cualquier carácter } "*/" ;//comenta hasta el fin de la línea./* … */comenta un bloque, que puede abarcar varias líneas. Los bloques no se anidan: el primer*/cierra el comentario.- Un
/*sin su*/esE000: unclosed /* comment.
Los comentarios no afectan el significado; el formateador (hoop fmt, ver
Herramientas) los conserva.
// comentario de línea
const borde = 2.5mm /* comentario
de bloque */Identificadores#
identificador = ( letra | "_" ) { letra | dígito | "_" } ;Una letra es cualquier carácter alfabético Unicode y un dígito,
cualquier carácter alfanumérico Unicode; así, ñandú, borde_negro y _x1
son identificadores válidos. Se distinguen mayúsculas de minúsculas (Twill
no es twill).
const ñandú = 3mm
const borde_negro = 1.2mm
const _x1 = 2Nombres calificados#
Un identificador seguido de . y otro identificador sin ( a
continuación forma un nombre calificado: club.negro, escudo.center,
canvas.width. Se usan para los nombres importados con espacio de nombres
(ver Módulos) y para los miembros de una forma con nombre (ver
Expresiones § Miembros). Si después
del segundo identificador viene (, es una llamada a método
(escudo.inset(2mm)). Los miembros y métodos también se aplican al resultado de
cualquier expresión: canvas.inset(5mm).center, rect(size: (10mm, 5mm)).width.
Palabras clave#
El analizador léxico no reserva ninguna palabra: todas se leen como identificadores, y el analizador sintáctico las reconoce según el contexto. Las palabras con significado especial son:
| Contexto | Palabras |
|---|---|
| Inicio de una declaración | import, design, hoop, thread, param, const, shape, layer |
Dentro de import | from, as |
| Contenido de una capa | seq, inside, for, in, if, else, place, at |
| Primitivas | fill, satin, run, triple, text |
| Expresiones | and, or, not, mod, if, else, path (seguido de {) |
Segmentos de path | from, line, curve, close |
| Valores con nombre | true, false |
Consecuencias de que sean contextuales:
trueyfalsese evalúan siempre comoBool, aunque exista una constante con ese nombre.- Una palabra como
layeroforpuede usarse como nombre de constante (const layer = 2compila), pero no es recomendable: dentro de una capa,forseguido de un identificador inicia un bucle. - Al inicio de una declaración, cualquier otra palabra es
E000:xis not a declaration (import, design, hoop, thread, const, shape or layer).
Literales#
Números#
número = cifras [ unidad ] ;
cifras = ( dígito | "." ) { dígito | "." } ; (* sin ".." *)
unidad = "mm" | "cm" | "deg" | "%" ;Un número es una secuencia de dígitos ASCII y puntos, seguida
inmediatamente (sin espacio) de una unidad opcional. La secuencia se
interpreta como un número decimal: 2.5, .5 y 1. son válidos;
1.2.3 es E000: invalid number: 1.2.3. No hay notación exponencial ni
signo: -3mm es el operador - aplicado a 3mm (ver
Expresiones).
Un número termina antes de .., de modo que 0..4 son tres tokens
(0, .., 4).
La unidad determina el tipo (ver Tipos):
| Literal | Tipo | Valor interno |
|---|---|---|
2.5mm | Length | 2.5 mm |
1cm | Length | 10 mm |
45deg | Angle | 45 grados |
30% | Ratio | 0.30 |
3 | Int | 3 |
- Cualquier secuencia de letras (o
%) pegada al número que no sea una de las unidades esE001: unknown unitpx(use mm, cm, deg or %). - Un número sin unidad debe ser entero:
1.5esE001: 1.5 has no unit: add mm, cm, deg or %. - Con espacio entre número y unidad (
3 mm) la unidad se lee como un identificador aparte, lo que suele terminar en un error de sintaxis.
const a = 2.5mm
const b: Length = .5mm
const c: Length = 1cm
const d: Angle = 45deg
const e: Ratio = 30%
const f: Int = 3// error E001
const g = 1.5 // error E001Textos#
texto = '"' { carácter | escape } '"'
| "'" { carácter | escape } "'" ;
escape = "\" carácter ;Un texto (tipo String) se escribe entre comillas dobles o simples; termina
en la misma comilla con que empezó, así que dentro de "…" la comilla simple
es un carácter más, y viceversa. Un texto no puede contener un salto de línea
literal: un texto sin cerrar antes del fin de la línea es
E000: unclosed string (missing closing quote).
Escapes: \n es un salto de línea; \ seguido de cualquier otro
carácter produce ese carácter (\" → ", \' → ', \\ → \, \t →
t).
const a = "HANNOVER"
const b = 'Club "Los Andes"'
const c = "MARÍA\nJOSÉ"
const d = 'it\'s'Colores#
color = "#" hex hex hex hex hex hex ;Un color (tipo Color) es # seguido de exactamente seis dígitos
hexadecimales. Se normaliza a minúsculas: #1C1616 y #1c1616 son el mismo
valor. Cualquier otra cantidad de dígitos es
E000: a color is written #rrggbb (6 hex digits).
const rojo = #C8102E
const igual: Bool = #FF0000 == #ff0000Booleanos#
true y false no son tokens propios: son identificadores que el
verificador evalúa como valores Bool (ver Palabras clave).
Operadores y puntuación#
| Token | Uso |
|---|---|
{ } | bloques: propiedades, capas, cuerpos de for/if, path |
( ) | agrupación, puntos (x, y), argumentos |
[ ] | listas |
, | separa argumentos, elementos y propiedades (opcional en muchos sitios) |
: | nombre: valor, anotación de tipo |
= | en const, shape, param, thread |
. | nombre calificado, miembro, método |
.. | rango (0..4) |
+ - * / | aritmética; + - también unión y diferencia de formas |
& | intersección de formas |
== != < <= > >= | comparación |
Los tokens de dos caracteres (==, !=, <=, >=, ..) se reconocen
antes que los de uno. ! solo no es un token (not es la negación lógica).
Un . seguido de un dígito inicia un número (.5mm), no un acceso.
Los operadores con nombre (and, or, not, mod) son identificadores
reconocidos por el analizador sintáctico; su precedencia está en
Expresiones § Precedencia.