O lexer
Texto em tokens: indentação vira INDENT e DEDENT, a interpolação vira partes, e // decide entre comentário e divisão.
O lexer lê o texto caractere por caractere e entrega tokens: cada um com tipo, valor, linha e coluna. É a linha e a coluna que fazem todo erro posterior apontar o lugar certo — um token sem posição produziria um erro sem seta.
adopt Arcane.Compilador as Comp
tipos := [t["tipo"] cycle t in Comp.tokens("given x bigger 1:\n out x\n")]
assert tipos.contains("INDENT") and tipos.contains("DEDENT")
assert Comp.tokens("total := 7")[2] is {"tipo": "INTEGER", "valor": 7, "linha": 1, "coluna": 10}A indentação vira token#
A linguagem marca bloco por recuo, e o parser não conta espaços: o lexer mantém uma pilha de recuos e emite INDENT quando o recuo cresce e um DEDENT para cada nível que fecha. Tabulação é recusada (SyncError) — dois editores mostram um tab com larguras diferentes, e um bloco pareceria certo em um e errado no outro.
`//`: comentário ou divisão?#
// é comentário por padrão, e vira divisão inteira só quando o que vem depois é um número, (, ou uma chamada/índice/membro. É por isso que total // 2 divide e total // nota comenta — e é a regra que exige cuidado com comentário que começa com número:
adopt Arcane.Compilador as Comp
divide := [t["tipo"] cycle t in Comp.tokens("x := a // 2")]
comenta := [t["tipo"] cycle t in Comp.tokens("x := a // metade")]
assert divide.contains("FLOOR_DIV")
assert not comenta.contains("FLOOR_DIV")