proc main(): void {
command := ["move", "north", "2"]
if command is ["move", dir, ...steps] {
echo "{dir}, {len(steps)} a mais"
} else if command is ["stop"] {
echo "parar"
}
}proc main(): void {
if "/users/42/posts/7" is "/users/{id}/posts/{postId}" {
echo "{id} / {postId}" // 42 / 7
}
if "a/b/c" is "{x}/{y}" {
echo "{x} | {y}" // a | b/c — o menor primeiro buraco
}
if "/users/42/extra" is "/users/{id}" {
echo id // 42/extra — um último buraco pega o resto
}
if "/users/42/extra" is "/users/{id is (\d+)}" {
echo "nunca: só dígitos, e o modelo cobre a string inteira"
}
}func rota(caminho: Str): Str {
// Só dígitos chegam em id, então /usuarios/ninguem cai para
// o próximo ramo em vez de casar com este.
if caminho is "/usuarios/{id is (\d+)}" {
return "usuário #{id}"
}
// Os dois tipos de buraco se misturam à vontade — são uma
// construção só.
if caminho is "/api/v{versao is (\d+)}/{resto}" {
return "v{versao}, depois {resto}"
}
// Chaves dentro dos parênteses são da própria regex: isto são
// quatro dígitos, não uma interpolação.
if caminho is "/logs/{ano is (\d{4})}" {
return "logs de {ano}"
}
return "sem correspondência"
}
proc main(): void {
echo rota("/usuarios/42") // usuário #42
echo rota("/usuarios/ninguem") // sem correspondência
echo rota("/api/v2/coisas") // v2, depois coisas
echo rota("/logs/2026") // logs de 2026
}proc main(): void {
path := "/users/42"
// Cada um destes é recusado antes de o programa ser construído:
// if path is "/users/{id}{name}" { } // dois buracos abertos lado a lado
// if path is "/users/{len(path)}" { } // um buraco vincula, não calcula
// if path is "{a is (a{2,1})}" { } // {2,1} conta para trás
// if path is "{a is ((?=b)c)}" { } // sem lookaround
// if path is "{a is ((b)\1)}" { } // e sem retrorreferência
// Vale: o primeiro buraco diz onde termina.
if path is "/users/{id is (\d+)}{rest}" {
echo "{id}[{rest}]"
}
}type Route {
Page { segments: Str[dyn] }
Missing
}
func resolve(path: Str): Result<Route, Str> {
if path == "" {
return Result.Error("vazio")
}
return Result.Ok(Route.Page(split(path, ",")))
}
proc main(): void {
path := "foo/abc/bar,x,y"
// Um Result com uma Page, com um vetor cujo primeiro elemento
// é uma string com um buraco: um padrão só.
if resolve(path) is Result.Ok(Route.Page(["foo/{padding}/bar", ...rest])) {
echo "{padding}, depois mais {len(rest)}" // abc, depois mais 2
}
// O mesmo casamento, escrito como a cadeia que ele abrevia.
if resolve(path) is Result.Ok(route) && route is Route.Page(parts) && parts is [head, ...rest] && head is "foo/{padding}/bar" {
echo "{padding}, depois mais {len(rest)}"
}
// Um literal fica onde ficaria um nome: o campo precisa ser igual a ele.
if resolve("") is Result.Error("vazio") {
echo "nada para resolver"
}
}Um padrão de vetor casa elemento por elemento. Um elemento é um literal — um Int, Float, Str, Atom ou Bool — um nome que o vincula, ou um padrão próprio, e um ...rest no final vincula o que vier depois como um vetor. Sem ele, os tamanhos precisam ser iguais.
Um padrão de string é um modelo cujos {buracos} vinculam o texto entre os seus trechos literais. O modelo cobre a string inteira, e cada buraco pega o menor texto que deixa o resto casar — então "a/b/c" is "{x}/{y}" dá x a e y b/c, e um buraco no final pega tudo o que sobrou: "/users/42/extra" is "/users/{id}" vincula id a 42/extra. Essa é a armadilha de rotas que um buraco que diz o que aceita evita: {id is (\d+)} vincula só o que a expressão regular casa.
Nada entre esses parênteses é escapado pela string — {year is (\d{4})} são quatro dígitos, e não uma interpolação de 4 — e a expressão regular é lida em tempo de compilação, então uma malformada é um erro de compilação dizendo o que há de errado. Um padrão precisa ter uma leitura só: dois buracos abertos não podem ficar lado a lado sem texto entre eles, embora um buraco que diz o que aceita termine a si mesmo; e um buraco é um nome simples, já que um padrão vincula e não calcula. Uma string sendo construída, e não casada, não pode ter um buraco de regex.
Padrões se aninham. Onde quer que um padrão vincule um nome — o campo de uma variante, o elemento de um vetor — ele pode conter um padrão no lugar, e o todo continua sendo um casamento só: r is Result.Ok(Route.Page(["home", ...rest])). Ele é exatamente a cadeia de && que abrevia, o valor de fora primeiro, e compila para o mesmo programa. O campo de uma variante também pode conter um literal ao qual ele precisa ser igual. Um padrão vincula um nome uma vez só, e um padrão de variante precisa ser do tipo com que é casado; o contrário é um erro de compilação.