Skip to content

Text Extraction (PEG vs Regex)

When automating browsers, you frequently encounter “messy” data—text that is inconsistently formatted, contains typos, or mixes different types of information into a single string.

While JavaScript and Lua rely on Regular Expressions (Regex) for this task, Guida’s Janet engine includes a built-in Parsing Expression Grammar (PEG) engine. PEGs are a powerful alternative to Regex that make complex text extraction easier to read, write, and maintain.

Regex is excellent for simple patterns, but it quickly becomes unreadable (a “write-only” language) when dealing with complex or nested structures.

Imagine extracting a product’s price and ID from a messy string like "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}`);
}

While this works, the regex is brittle. If the format changes slightly, the regex will fail and modifying it requires mental gymnastics.

Janet’s peg module allows you to build parsers compositionally. Instead of one massive, cryptic string, you define a grammar as a dictionary of readable rules.

(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. Readability: Rules are named (:id-rule, :price-rule) and can reference each other. You don’t have to decipher a 50-character regex string.
  2. Composition: You can build complex parsers by combining smaller, tested rules.
  3. No Ambiguity: Unlike Regex, PEGs do not backtrack in the same unpredictable way. They match exactly what you tell them to match, making performance predictable.
  4. Data Structures: Janet’s peg module can construct complex data structures (arrays, dictionaries) directly from the parse step, rather than just returning flat strings.
  • Use JavaScript/Lua (Regex) for simple validations (e.g., “does this string look like an email?”).
  • Use Janet (PEG) when you need to extract multiple pieces of related data from a messy block of text, or when the formatting of the target website is highly variable.