Ir al contenido

Extracción de texto (PEG frente a regex)

Al automatizar navegadores, con frecuencia se encuentran datos “desordenados”: texto con formato inconsistente, erratas o mezcla de distintos tipos de información en una sola cadena.

JavaScript y Lua recurren a expresiones regulares para esta tarea, pero el motor Janet de Guida incluye un motor integrado de Parsing Expression Grammar (PEG). Las PEG son una alternativa potente a regex que hace que la extracción compleja de texto sea más fácil de leer, escribir y mantener.

Regex es excelente para patrones simples, pero se vuelve rápidamente ilegible, casi un lenguaje de solo escritura, cuando trata estructuras complejas o anidadas.

Imagine extraer el precio y el ID de un producto desde una cadena desordenada como "Item: SuperWidget (ID: 98765) - Only $49.99 today!".

const text = "Item: SuperWidget (ID: 98765) - Only $49.99 today!";
// Regex is dense and relies on capturing groups that are extracted by index
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}`);
}

Aunque esto funciona, la expresión regular es frágil. Si el formato cambia ligeramente, fallará, y modificarla exige gimnasia mental.

El módulo peg de Janet permite construir analizadores de forma composicional. En lugar de una cadena enorme y críptica, se define una gramática como diccionario de reglas legibles.

(def text "Item: SuperWidget (ID: 98765) - Only $49.99 today!")
# Define a clean, compositional grammar
(def product-grammar
(peg/compile
~{:id-rule (sequence "(ID: " (capture (some :d)) ")")
:price-rule (sequence "$" (capture (some (choice :d "."))))
# The main rule scans through the text looking for our rules
:main (any (choice
:id-rule
:price-rule
1))}))
# Run the grammar against the text
(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. Legibilidad: las reglas tienen nombre (:id-rule, :price-rule) y pueden referenciarse entre sí. No hay que descifrar una cadena regex de 50 caracteres.
  2. Composición: puede construir analizadores complejos combinando reglas pequeñas y probadas.
  3. Sin ambigüedad: a diferencia de regex, las PEG no retroceden del mismo modo impredecible. Coinciden exactamente con lo que se les indica, haciendo el rendimiento predecible.
  4. Estructuras de datos: el módulo peg de Janet puede construir estructuras complejas, como arrays y diccionarios, directamente desde el paso de análisis, en lugar de devolver solo cadenas planas.
  • Use JavaScript/Lua (regex) para validaciones simples, por ejemplo comprobar si una cadena parece un email.
  • Use Janet (PEG) cuando necesite extraer varias piezas de datos relacionados desde un bloque de texto desordenado, o cuando el formato del sitio objetivo sea muy variable.