Capítulo 22 22 min de leitura

Regex

stdlib::regex é um motor de expressões regulares estilo PCRE, escrito em Glide puro, construído sobre uma VM de bytecode com retrocesso. Você compila um padrão em um *Regex, e então testa, busca, captura, substitui ou divide strings com ele. Os padrões operam sobre bytes brutos.

Importação

import stdlib::regex::*;

Visão geral

Item Tipo Resumo
Regex struct Um padrão compilado (pattern, flags, n_groups são pub).
Match struct Uma correspondência e seus spans de captura (input, start, end são pub).
CharClass struct Um conjunto de intervalos de bytes inclusivos, opcionalmente negado.
Regex::compile / compile_with fn Constrói um *Regex (retorna !*Regex).
matches / matches_full method Teste booleano (substring / string inteira).
find / find_at / find_all method Localiza correspondências.
replace / replace_all / replace_with method Substitui correspondências.
split method Divide nas correspondências.
full / group / group_opt / named / named_opt / n_groups method Lê um *Match.
regex_matches / regex_find / regex_replace_all fn Utilitários de uso único (compilam a cada chamada).
RxNode / RxParser / RxCompiler struct AST/parser/compilador internos (exportados, sem API estável).
RX_* const Constantes de opcode / flag / AST / tipo de asserção / limite.

Compilando padrões

Um padrão é compilado uma vez em um *Regex e reutilizado em todas as operações. A compilação analisa o padrão, reporta erros de sintaxe como o err do !*Regex, e produz bytecode para a VM. A correspondência inteira é envolvida em um grupo 0 implícito, portanto start/end/full() estão sempre disponíveis.

Regex::compile / Regex::compile_with

Função Assinatura Descrição
compile fn compile(pattern: string) -> !*Regex Compila sem flags.
compile_with fn compile_with(pattern: string, flag_str: string) -> !*Regex Compila com uma string de flags (qualquer subconjunto de imsxU).
import stdlib::regex::*;

fn main() -> i32 {
    let r: !*Regex = Regex::compile("(\\d+)-(\\d+)");
    if !r.ok {
        println!("bad pattern:", r.err);
        return 1;
    }
    let re: *Regex = r.val;

    if re.matches("phone: 555-1234") {
        println!("hit");
    }
    return 0;
}

compile_with converte cada caractere de flag em um bit de flag; um caractere desconhecido retorna err("unknown flag in flag string"). A compilação também rejeita padrões malformados (err("expected ')'"), "unterminated character class", "trailing characters in pattern", entre outros).

// Insensível a maiúsculas (i) + multilinha (m).
let r: !*Regex = Regex::compile_with("^err", "im");
import stdlib::regex::*;

fn main() -> i32 {
    // Um padrão malformado aparece em .err.
    let bad: !*Regex = Regex::compile("(unclosed");
    if !bad.ok { println!("err:", bad.err); }       // expected ')'

    // Flag desconhecida.
    let bf: !*Regex = Regex::compile_with("x", "q");
    if !bf.ok { println!("flagerr:", bf.err); }     // unknown flag in flag string
    return 0;
}

A struct Regex

pub struct Regex {
    pub pattern: string,   // o padrão-fonte original
    pub flags: i32,        // bits de flag resolvidos (veja RX_FLAG_*)
    pub n_groups: i32,     // número de grupos de captura (exclui o grupo 0)
    // ... bytecode/classes/tabela de nomes internos
}

Os três campos pub são metadados somente-leitura que você pode inspecionar após a compilação. flags reflete quaisquer flags resolvidas durante o parsing, incluindo configurações no estilo (?i) inline no nível superior.

import stdlib::regex::*;

fn main() -> i32 {
    let re: *Regex = Regex::compile("(?<a>\\d)(?<b>\\d)").val;
    println!("pattern", re.pattern);    // (?<a>\d)(?<b>\d)
    println!("flags", re.flags);        // 0
    println!("n_groups", re.n_groups);  // 2
    return 0;
}

Testando uma correspondência

Método Assinatura Descrição
matches fn matches(self: *Regex, s: string) -> bool true se qualquer substring corresponder.
matches_full fn matches_full(self: *Regex, s: string) -> bool true somente se o padrão corresponder à string inteira.
import stdlib::regex::*;

fn main() -> i32 {
    let re: *Regex = Regex::compile("\\d+").val;
    if re.matches("hi 42 there") { println!("matches substring"); } // true
    if !re.matches("nothing")    { println!("no digits"); }         // true

    let rf: *Regex = Regex::compile("[a-z]+").val;
    if rf.matches_full("hello")        { println!("full ok"); }  // true
    if !rf.matches_full("hello world") { println!("full no"); }  // espaço/'world' sem correspondência
    return 0;
}

matches equivale a find(s).has. matches_full é bem-sucedido apenas quando a primeira correspondência começa no offset 0 e termina em s.len() — ele não ancora o padrão, então uma correspondência mais curta à esquerda pode fazê-lo retornar false mesmo quando existe uma correspondência de comprimento total. Ancore explicitamente com ^...$ (ou \A...\z) se precisar disso.

Buscando e encontrando

Método Assinatura Descrição
find fn find(self: *Regex, s: string) -> ?*Match Primeira correspondência, ou none().
find_at fn find_at(self: *Regex, s: string, from: i32) -> ?*Match Primeira correspondência a partir do offset de byte from.
find_all fn find_all(self: *Regex, s: string) -> *Vector<*Match> Todas as correspondências sem sobreposição (correspondências vazias avançam um byte).
import stdlib::regex::*;

fn main() -> i32 {
    let re: *Regex = Regex::compile("\\d+").val;

    // find_all: toda correspondência sem sobreposição, com spans.
    let all: *Vector<*Match> = re.find_all("a12 b3 c456");
    for let i: i32 = 0; i < all.len(); i++ {
        let m: *Match = all.get(i);
        println!("match", i, m.full(), m.start, m.end);
    }

    // find_at: inicia a busca em um offset.
    match re.find_at("a12 b3", 3) {
        some(m) => println!("from 3:", m.full(), m.start),  // 3 no offset 4
        none()  => println!("none"),
    }

    // find: sem correspondência -> none().
    match re.find("no digits here") {
        some(m) => println!("found", m.full()),
        none()  => println!("no match"),
    }
    return 0;
}

find equivale a find_at(s, 0). Ambos percorrem para frente a partir do offset inicial, testando a VM em cada posição de byte (a correspondência mais à esquerda vence). find_all chama find_at repetidamente, avançando além de cada correspondência; uma correspondência de largura zero avança um byte para que o laço termine.

O resultado Match e os grupos de captura

Um *Match descreve uma correspondência e seus grupos de captura. O grupo 0 é a correspondência inteira; os grupos 1..n são as capturas entre parênteses, em ordem.

pub struct Match {
    pub input: string,   // a string que foi pesquisada
    pub start: i32,      // offset de byte do início da correspondência
    pub end: i32,        // offset de byte logo após o fim da correspondência
    // ... spans de captura + tabela de nomes
}
Método Assinatura Descrição
full fn full(self: *Match) -> string A substring correspondida completa (igual a group(0)).
group fn group(self: *Match, i: i32) -> string Grupo de captura i (0 = correspondência completa); "" para ausente/não capturado.
group_opt fn group_opt(self: *Match, i: i32) -> ?string some se o grupo i participou, caso contrário none().
named fn named(self: *Match, name: string) -> string Busca por grupo de captura nomeado; "" se ausente.
named_opt fn named_opt(self: *Match, name: string) -> ?string some se o grupo nomeado participou, caso contrário none().
n_groups fn n_groups(self: *Match) -> i32 Número de grupos de captura (excluindo o grupo 0).
import stdlib::regex::*;

fn main() -> i32 {
    let re: *Regex = Regex::compile("(?<year>\\d{4})-(?<month>\\d{2})").val;

    match re.find("date 2026-05 end") {
        some(m) => {
            println!("full:", m.group(0), m.full());          // 2026-05  2026-05
            println!("g1:", m.group(1), "g2:", m.group(2));   // 2026  05
            println!("year:", m.named("year"), m.named("month"));
            println!("groups:", m.n_groups());                // 2
            println!("span:", m.start, m.end);
            println!("input:", m.input);
        }
        none() => println!("no match"),
    }
    return 0;
}
import stdlib::regex::*;

fn main() -> i32 {
    // (b)? não participou ao corresponder apenas "a".
    let re: *Regex = Regex::compile("(a)(b)?").val;
    match re.find("a") {
        some(m) => {
            let g2: ?string = m.group_opt(2);
            if g2.has { println!("g2 present"); } else { println!("g2 missing"); }
            println!("g1", m.group(1));   // a
        }
        none() => println!("no match"),
    }

    // (x*) participou mas capturou a string vazia.
    let re2: *Regex = Regex::compile("(a)(x*)").val;
    match re2.find("a") {
        some(m) => {
            let g2: ?string = m.group_opt(2);
            if g2.has { println!("g2 empty-but-present:", g2.val.len()); }  // 0
        }
        none() => {}
    }
    return 0;
}

Substituindo

Método Assinatura Descrição
replace fn replace(self: *Regex, s: string, repl: string) -> string Substitui a primeira correspondência.
replace_all fn replace_all(self: *Regex, s: string, repl: string) -> string Substitui todas as correspondências sem sobreposição.
replace_with fn replace_with(self: *Regex, s: string, f: fn(*Match) -> string) -> string Substitui cada correspondência pelo resultado de f(match).

A string de substituição (replace / replace_all) suporta retrorreferências e um escape para o $ literal:

Token Expande para
$0 .. $9 O grupo de captura correspondente ($0 = correspondência completa).
${name} O grupo de captura nomeado name.
$$ Um $ literal.

Os tokens numerados e $$ são fáceis de escrever como literais de string Glide:

import stdlib::regex::*;

fn main() -> i32 {
    // $$ escapa um '$' literal; $1 é o primeiro grupo.
    let price: *Regex = Regex::compile("(\\d+)").val;
    println!(price.replace_all("a5 b6", "$$$1"));     // a$5 b$6

    // Reordena grupos.
    let pair: *Regex = Regex::compile("(\\w+)=(\\w+)").val;
    println!(pair.replace("k=v rest", "$2:$1"));      // v:k rest

    // $0 = correspondência inteira. replace toca apenas o primeiro resultado.
    let w: *Regex = Regex::compile("\\w+").val;
    println!(w.replace("hi there", "[$0]"));          // [hi] there
    return 0;
}
import stdlib::regex::*;

fn main() -> i32 {
    let date: *Regex =
        Regex::compile("(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})").val;
    let d: string = "$";
    let repl: string =
        d.concat("{d}/").concat(d).concat("{m}/").concat(d).concat("{y}");
    println!(date.replace("on 2026-05-30 ok", repl));   // on 30/05/2026 ok
    return 0;
}

Os grupos numerados ($1) e $$ não têm esse conflito. Se a substituição por nome for trabalhosa, prefira replace_with e leia m.named(...) no callback.

replace_with chama sua função para cada correspondência e substitui pela string retornada de forma literal (sem expansão de $):

import stdlib::regex::*;

fn mask(m: *Match) -> string {
    let n: i32 = m.full().len();
    let mut s: string = "";
    for let i: i32 = 0; i < n; i++ { s = s.concat("*"); }
    return s;
}

fn main() -> i32 {
    let digits: *Regex = Regex::compile("\\d+").val;
    println!(digits.replace_with("card 4242 9999", mask));  // card **** ****
    return 0;
}

Se não houver correspondência, replace/replace_all/replace_with retornam a string de entrada inalterada.

Dividindo

split

fn split(self: *Regex, s: string) -> *Vector<string>

Divide s em cada correspondência sem sobreposição do regex, retornando os pedaços entre as correspondências. O resultado sempre tem pelo menos um elemento (a string inteira quando nada corresponde).

import stdlib::regex::*;

fn main() -> i32 {
    let sep: *Regex = Regex::compile(",|;").val;
    let parts: *Vector<string> = sep.split("alpha,beta;gamma");
    for let i: i32 = 0; i < parts.len(); i++ {
        println!("part", i, parts.get(i));     // alpha / beta / gamma
    }

    // \s+ colapsa sequências de espaço em branco em separadores únicos.
    let ws: *Regex = Regex::compile("\\s+").val;
    let words: *Vector<string> = ws.split("the   quick  fox");
    println!("nwords", words.len());           // 3

    // Sem correspondência -> vetor de um único elemento (a string inteira).
    let z: *Regex = Regex::compile("z").val;
    let one: *Vector<string> = z.split("abc");
    println!("one", one.len(), one.get(0));    // 1  abc
    return 0;
}

API de conveniência com funções livres

Utilitários de uso único que compilam o padrão a cada chamada. Prefira manter um *Regex por perto em laços de alto desempenho.

Função Assinatura Descrição
regex_matches fn regex_matches(pat: string, s: string) -> bool Regex::compile(pat).val.matches(s); false em padrão inválido.
regex_find fn regex_find(pat: string, s: string) -> ?*Match Regex::compile(pat).val.find(s); none() em padrão inválido.
regex_replace_all fn regex_replace_all(pat: string, s: string, repl: string) -> string Regex::compile(pat).val.replace_all(s, repl); retorna s em padrão inválido.
import stdlib::regex::*;

fn main() -> i32 {
    if regex_matches("\\d+", "abc 42") { println!("matched"); }

    match regex_find("(\\w+)@(\\w+)", "x@y") {
        some(m) => println!(m.group(1), m.group(2)),   // x  y
        none()  => {},
    }

    println!(regex_replace_all("a1b2c3", "\\d", "#"));  // a#b#c#

    // Padrão inválido: silenciosamente retorna false / none() / a entrada.
    if !regex_matches("(unclosed", "x") { println!("bad pattern swallowed"); }
    return 0;
}

Sintaxe suportada

Literais e escapes

A maioria dos caracteres corresponde a si mesma. Escapes com barra invertida:

Escape Corresponde a
\n \t \r \f \v nova linha, tabulação, retorno, avanço de página, tabulação vertical
\a \e \0 sino (0x07), escape (0x1B), NUL (0x00)
\xHH o byte com valor hexadecimal HH (ex.: \x41 = A)
\. \\ \( ... um metacaractere literal

Classes de caracteres

Sintaxe Corresponde a
[abc] qualquer um de a, b, c
[a-z] um intervalo de bytes
[^a-z] negação (qualquer byte fora do conjunto)
[a-zA-Z0-9_] união de intervalos
[\d\s] classes predefinidas são permitidas dentro de [...]
[]a] um ] colocado primeiro é um ] literal

Classes predefinidas (utilizáveis de forma isolada ou dentro de [...]):

Classe Corresponde a Negada
\d dígitos 0-9 \D
\w bytes de palavra [A-Za-z0-9_] \W
\s espaço em branco \t\n\v\f\r e espaço \S
. qualquer byte exceto \n (qualquer byte com a flag s)
import stdlib::regex::*;

fn main() -> i32 {
    let hex: *Regex = Regex::compile("[0-9a-fA-F]+").val;
    println!(hex.find("DEADbeef!").val.full());    // DEADbeef

    let neg: *Regex = Regex::compile("[^aeiou ]+").val;
    println!(neg.find("the fox").val.full());      // th

    let byte: *Regex = Regex::compile("\\x41\\x42").val;  // corresponde a "AB"
    if byte.matches("xABy") { println!("hex byte ok"); }

    let mix: *Regex = Regex::compile("[\\d.]+").val;       // dígitos ou ponto
    println!(mix.find("v3.14!").val.full());               // 3.14
    return 0;
}

Quantificadores

Quantificador Repetições Forma preguiçosa
* 0 ou mais *?
+ 1 ou mais +?
? 0 ou 1 ??
{n} exatamente n {n}?
{n,} n ou mais {n,}?
{n,m} entre n e m {n,m}?

Os quantificadores são gananciosos por padrão; adicionar ? os torna preguiçosos. A flag U (ou inline (?U)) inverte a ganância globalmente.

import stdlib::regex::*;

fn main() -> i32 {
    let greedy: *Regex = Regex::compile("<.+>").val;
    println!(greedy.find("<a><b>").val.full());     // <a><b>

    let lazy: *Regex = Regex::compile("<.+?>").val;
    println!(lazy.find("<a><b>").val.full());        // <a>

    let bounded: *Regex = Regex::compile("a{2,3}").val;
    println!(bounded.find("aaaa").val.full());       // aaa

    let exact: *Regex = Regex::compile("\\d{3}").val;
    println!(exact.find("12345").val.full());        // 123

    // A flag U inverte a ganância padrão: <.+> se comporta como <.+?>.
    let ung: *Regex = Regex::compile_with("<.+>", "U").val;
    println!(ung.find("<a><b>").val.full());         // <a>
    return 0;
}

Grupos, alternância e âncoras

Sintaxe Significado
(...) grupo de captura
(?:...) grupo sem captura
(?<name>...) / (?P<name>...) grupo de captura nomeado
`a\ b\ c` alternância
^ $ início / fim de string (ou de linha com a flag m)
\A \z \Z início / fim absoluto de string (\Z é tratado como \z)
\b \B limite de palavra / não-limite de palavra
import stdlib::regex::*;

fn main() -> i32 {
    let alt: *Regex = Regex::compile("cat|dog|bird").val;
    println!(alt.find("I have a dog").val.full());   // dog

    let anch: *Regex = Regex::compile("^\\d+$").val;
    if anch.matches("12345")  { println!("all digits"); }
    if !anch.matches("12a45") { println!("not all"); }

    // \b: palavra inteira, não uma substring.
    let word: *Regex = Regex::compile("\\bcat\\b").val;
    if word.matches("a cat sat")  { println!("whole word"); }
    if !word.matches("category")  { println!("no substring"); }

    // flag m: ^ corresponde após cada nova linha.
    let ml: *Regex = Regex::compile_with("^x", "m").val;
    let hits: *Vector<*Match> = ml.find_all("ax\nx\nx");
    println!("ml hits", hits.len());                 // 2
    return 0;
}

Retrorreferências

Sintaxe Significado
\1 .. \9 corresponde ao mesmo texto capturado por um grupo numerado
\k<name> corresponde ao mesmo texto capturado por um grupo nomeado

Lookaround

Sintaxe Significado
(?=...) lookahead positivo
(?!...) lookahead negativo
(?<=...) lookbehind positivo
(?<!...) lookbehind negativo
import stdlib::regex::*;

fn main() -> i32 {
    // Lookahead positivo: foo apenas quando seguido de bar (bar não é consumido).
    let la: *Regex = Regex::compile("foo(?=bar)").val;
    println!(la.find("foobar").val.full());        // foo

    // Lookahead negativo.
    let nla: *Regex = Regex::compile("\\d+(?!px)").val;
    if nla.matches("10em") { println!("nla ok"); }

    // Lookbehind: dígitos precedidos por '$'.
    let lb: *Regex = Regex::compile("(?<=\\$)\\d+").val;
    println!(lb.find("price $42 yen").val.full());  // 42

    // Retrorreferência: uma palavra duplicada.
    let dup: *Regex = Regex::compile("\\b(\\w+)\\s+\\1\\b").val;
    if dup.matches("the the cat") { println!("dup"); }

    // Retrorreferência nomeada: correspondência entre tags de abertura e fechamento.
    let tag: *Regex = Regex::compile("<(?<t>\\w+)>.*?</\\k<t>>").val;
    if tag.matches("<b>hi</b>") { println!("tag ok"); }
    return 0;
}

Flags

Passe como string de flags para compile_with, ou inline no padrão via (?flags) (define as flags a partir desse ponto) ou (?flags:subpadrão) (com escopo); (?flags-flags:...) desativa flags dentro do escopo.

Flag Constante Efeito
i RX_FLAG_I insensível a maiúsculas/minúsculas (somente ASCII)
m RX_FLAG_M ^/$ correspondem em quebras de linha
s RX_FLAG_S . corresponde a \n (dot-all)
x RX_FLAG_X estendido: espaço em branco sem escape e comentários # são ignorados
U RX_FLAG_U não-ganancioso: inverte a ganância padrão
import stdlib::regex::*;

fn main() -> i32 {
    // (?i:...) — insensível a maiúsculas/minúsculas apenas dentro do grupo.
    let scoped: *Regex = Regex::compile("(?i:hello) world").val;
    if scoped.matches("HELLO world")   { println!("scoped i"); }
    if !scoped.matches("HELLO WORLD")  { println!("outside still sensitive"); }

    // (?x) — estendido: espaço em branco e comentários # são ignorados.
    let ext: *Regex = Regex::compile("(?x) \\d+  # the number \n -  \\d+").val;
    if ext.matches("12-34") { println!("extended ok"); }
    return 0;
}
import stdlib::regex::*;

fn main() -> i32 {
    // Flags inline no nível superior + lookahead.
    let re: *Regex = Regex::compile("(?i)foo(?=bar)").val;
    if re.matches("FOObar") { println!("lookahead ok"); }
    return 0;
}

Blocos de construção de baixo nível

Esses itens públicos sustentam o motor. A maioria dos programas nunca os acessa diretamente, mas são exportados e documentados aqui por completude.

CharClass

Um conjunto de intervalos de bytes inclusivos, opcionalmente negado. Usado internamente para classes; você pode construir um manualmente.

pub struct CharClass {
    ranges: *Vector<i32>,   // [lo1, hi1, lo2, hi2, ...] inclusivo
    negated: bool,
}
Método Assinatura Descrição
new fn new() -> *CharClass Classe vazia, não negada.
add fn add(self: *CharClass, lo: i32, hi: i32) Adiciona um intervalo de bytes inclusivo.
contains fn contains(self: *CharClass, b: i32) -> bool Testa o byte b (respeita negated).
import stdlib::regex::*;

fn main() -> i32 {
    let cc: *CharClass = CharClass::new();
    cc.add(48, 57);   // '0'..'9'
    cc.add(65, 70);   // 'A'..'F'
    if cc.contains(53)  { println!("'5' in class"); }   // true
    if !cc.contains(103) { println!("'g' not in class"); } // true
    return 0;
}

Constantes exportadas

Estas nomeiam os opcodes internos, tipos de nó AST, bits de flag, tipos de asserção e limites. São de interesse principalmente ao inspecionar re.flags ou ao estender o motor.

Grupo Constantes
Bits de flag RX_FLAG_I (1), RX_FLAG_M (2), RX_FLAG_S (4), RX_FLAG_X (8), RX_FLAG_U (16)
Tipos de asserção RX_AHEAD_POS (0), RX_AHEAD_NEG (1), RX_BEHIND_POS (2), RX_BEHIND_NEG (3)
Limites RX_MAX_GROUPS (64)
Opcodes da VM RX_OP_CHAR, RX_OP_ANY, RX_OP_ANY_NL, RX_OP_CLASS, RX_OP_BOL, RX_OP_EOL, RX_OP_STR_BEG, RX_OP_STR_END, RX_OP_WORDB, RX_OP_NWORDB, RX_OP_JMP, RX_OP_SPLIT, RX_OP_SAVE, RX_OP_BACKREF, RX_OP_ASSERT, RX_OP_ASRT_END, RX_OP_MATCH
Tipos de nó AST RX_AST_LIT, RX_AST_ANY, RX_AST_CLASS, RX_AST_CONCAT, RX_AST_ALT, RX_AST_QUANT, RX_AST_GROUP, RX_AST_ANCHOR, RX_AST_BACK, RX_AST_LOOK

Os bits de flag são combináveis com OR, então você pode testar re.flags:

import stdlib::regex::*;

fn main() -> i32 {
    let re: *Regex = Regex::compile_with("abc", "is").val;
    if (re.flags & RX_FLAG_I) != 0 { println!("case-insensitive"); }
    if (re.flags & RX_FLAG_S) != 0 { println!("dot-all"); }
    return 0;
}

Tipos internos

RxNode (nó AST), RxParser (parser de padrões) e RxCompiler (compilador de AST para bytecode) são structs exportadas usadas internamente por compile. Elas não possuem uma superfície de métodos públicos estável e devem ser tratadas como detalhe de implementação — não dependa de seus campos.

Não suportado / limitações conhecidas

Documentado honestamente para que você não busque recursos que se comportam de forma silenciosamente diferente:

Recurso Status
Classes de propriedade Unicode (\p{...}), \w/./dobramento de maiúsculas compatível com Unicode Não suportado — o motor é orientado a bytes; i dobra apenas ASCII.
Classes bracket POSIX ([[:alpha:]]) Não suportado — use intervalos explícitos ou \d/\w/\s.
Grupos atômicos (?>...), quantificadores possessivos (a++, a*+) Não suportado.
Condicionais (?(1)...), recursão (?R), chamadas de subrotina (?1) Não suportado.
Comentários via (?#...) Não suportado (use a flag x com #).
Semântica de \Z (antes de uma nova linha final) Tratado de forma idêntica a \z (fim absoluto).
Escapes octais além de \0, escapes de controle \cX, \Q...\E Não suportado.
Token de substituição ${name} como literal de string Glide puro Conflita com a interpolação de string Glide — veja o callout em Substituindo.

Veja também

  • stringssplit, contains, substring, to_lower para manipulação de texto sem regex (mais barato quando você não precisa de um padrão).
  • vectors — o *Vector<*Match> / *Vector<string> retornado por find_all / split.
  • collections — o HashMap que sustenta a tabela nome→índice dos grupos nomeados.
  • prelude — as convenções de ?*Match / !*Regex (some/none/ok/err) usadas em todo o texto.