Pular para o conteúdo

Extração de texto (PEG vs Regex)

Ao automatizar navegadores, você frequentemente encontra dados “bagunçados”: texto com formatação inconsistente, erros de digitação ou vários tipos de informação misturados na mesma string.

Enquanto JavaScript e Lua dependem de expressões regulares (Regex) para essa tarefa, o mecanismo Janet do Guida inclui um mecanismo integrado de Parsing Expression Grammar (PEG). PEGs são uma alternativa poderosa a Regex e tornam extrações complexas mais fáceis de ler, escrever e manter.

Regex é excelente para padrões simples, mas rapidamente fica ilegível quando lida com estruturas complexas ou aninhadas.

Imagine extrair o preço e o ID de um produto de uma string confusa como "Item: SuperWidget (ID: 98765) - Only $49.99 today!".

const text = "Item: SuperWidget (ID: 98765) - Only $49.99 today!";
// Regex é densa e depende de grupos capturados por índice
const regex = /\\(ID:\\s*(\\d+)\\).*?\\$([0-9.]+)/;
const match = text.match(regex);
if (match) {
const id = match[1];
const price = match[2];
g.log(`Found ID: ${id}, Price: ${price}`);
}

Isso funciona, mas a regex é frágil. Se o formato mudar um pouco, ela falha; modificá-la exige raciocínio cuidadoso sobre grupos, escapes e quantificadores.

O módulo peg do Janet permite construir parsers por composição. Em vez de uma única string enorme e críptica, você define uma gramática como um dicionário de regras legíveis.

(def text "Item: SuperWidget (ID: 98765) - Only $49.99 today!")
# Definir uma gramática limpa e composicional
(def product-grammar
(peg/compile
~{:id-rule (sequence "(ID: " (capture (some :d)) ")")
:price-rule (sequence "$" (capture (some (choice :d "."))))
# A regra principal varre o texto procurando nossas regras
:main (any (choice
:id-rule
:price-rule
1))}))
# Executar a gramática contra o texto
(def results (peg/match product-grammar text))
(if results
(do
(def id (get results 0))
(def price (get results 1))
(g.log (string "Found ID: " id ", Price: " price))))
  1. Legibilidade: regras têm nome (:id-rule, :price-rule) e podem referenciar umas às outras. Você não precisa decifrar uma regex de 50 caracteres.
  2. Composição: parsers complexos podem ser montados a partir de regras menores e testadas.
  3. Sem ambiguidade: diferente de Regex, PEGs não fazem backtracking do mesmo modo imprevisível. Elas correspondem exatamente ao que você define, tornando desempenho e comportamento mais previsíveis.
  4. Estruturas de dados: o módulo peg do Janet pode construir arrays e dicionários diretamente na etapa de parse, em vez de retornar apenas strings planas.
  • Use JavaScript/Lua (Regex) para validações simples, como verificar se uma string parece um e-mail.
  • Use Janet (PEG) quando precisar extrair várias partes relacionadas de um bloco de texto confuso ou quando a formatação do site alvo variar bastante.