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.
The Problem with Regex
Section titled “The Problem with Regex”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!".
JavaScript (Regex)
Section titled “JavaScript (Regex)”const text = "Item: SuperWidget (ID: 98765) - Only $49.99 today!";
// Regex is dense and relies on capturing groups that are extracted by indexconst 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.
The Janet Approach (PEG)
Section titled “The Janet Approach (PEG)”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.
Janet (PEG)
Section titled “Janet (PEG)”(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))))Why PEGs are better for web data:
Section titled “Why PEGs are better for web data:”- Readability: Rules are named (
:id-rule,:price-rule) and can reference each other. You don’t have to decipher a 50-character regex string. - Composition: You can build complex parsers by combining smaller, tested rules.
- 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.
- Data Structures: Janet’s
pegmodule can construct complex data structures (arrays, dictionaries) directly from the parse step, rather than just returning flat strings.
When to use which?
Section titled “When to use which?”- 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.