Parsing Expression Grammar vs. Regexes: Building Org Parser in Lisp, Export HTML
OrgWebAlchemy: Transitioning from Regex to PEG for Org-to-HTML Conversion
Written on: 24/09/2026 | Estimated Reading Time: 10 minutes
Hello everyone! I want to take you on a journey regarding the development of OrgWebAlchemy, a project dedicated to parsing Org mode content using Parsing Expression Grammars (PEG) within Guile Scheme (specifically utilizing the (ice-9 peg) module) to generate HTML via SXML.
The Evolution of the Parser
My journey began with a naive simplistic approach using regular expressions. However, I quickly discovered that regexes are insufficient for the complexity of Org mode. As the requirements grew, the logic became increasingly "adventurous."
"You end up with things like: match this, unless that follows it, except inside this block, unless it is a description, but don't consume the newline, unless the previous line was a list item..."
At that point, I wasn't describing a language anymore; I was fighting a battle against string patterns. To solve this, I dove into the world of Parsing Expression Grammars (PEG), aided by GNU's excellent tutorials and some AI assistance for debugging. While the core development of OrgWebAlchemy is my own "spaghetti" code, rigorous unit testing and AST (Abstract Syntax Tree) pretty-printing helped me refine the implementation.
The Technical Pipeline
The transformation process follows a linear flow:
Why PEG in Guile Scheme?
By using the (ice-9 peg) module, the grammar definition begins to mirror the actual documentation of the language. In Guile, we can express these grammars as S-expressions, which is a dream for any Lisper.
Example Grammar Pattern:
(define-peg-pattern element body
(or empty-line heading separator table src-block
quote-block example-block export-html-block
description-list unordered-list ordered-list paragraph))
One of the greatest advantages of this architecture is the loose coupling. The generation of SXML and the subsequent rendering of HTML are treated as presentation concerns. This means we can easily extend the project to export to other formats, such as Markdown, in the future.
Data Transformation Example
A description list like this:
name :: Josepproject :: orgwebalchemylanguage :: Scheme
Is transformed into an AST similar to:
(description-list (unordered-item (desc-key "name") (line-content "Josep")) ...)
Feature Set & Current Status
The project has reached a stable v1.0. While Org mode is massive and I cannot support every single feature, the core constructs are fully operational.
Supported Elements
| Element | Description |
|---|---|
| Headings | Lines starting with asterisks (*) |
| Paragraphs | Any text not identified as a special construct |
| Lists | Nested Unordered, Ordered, and Description lists |
| Inline Markup | Italics, Bold, and inline code |
| Links | Supports both raw links and links with descriptions |
| Separators | Horizontal lines (5 or more dashes: -----) |
| Tables | Standard Org-style tables |
| Blocks | #+begin_src, #+begin_example, and #+begin_quote |
| HTML Export | Raw HTML blocks that bypass the parser for literal output |
Implementation Checklist
- Basic PEG Grammar
- AST Generation
- SXML Rendering
- Recursive List Support
- Full Org-mode Specification Compliance (Work in Progress)
The Challenge of Recursive Lists
Handling nested lists was one of the most rewarding challenges. In Org mode, a structure like:
- Item 1
- Item 1.1
- Item 1.2
- Item 2
Must be treated as a quasi-tree. Mathematically, the parser first treats the input as a flat sequence :
The AST processing phase then analyzes the indentation levels to convert this flat sequence into a hierarchical tree structure. This allows the HTML renderer to naturally produce nested <ul> and <li> tags.
Note: I am still refining the logic for mixing different list types (e.g., an ordered list inside an unordered one) within nested structures.
HTML Customization via SXML
Since the project is written in Lisp, it makes sense to represent HTML as Lisp data (SXML). This makes building the markup tree significantly more intuitive. To avoid hard-coding a specific aesthetic, I've implemented customization via Guile parameters. For instance, heading levels can be customized using:
(heading-classes ...)
Resources & Source Code
Project Source: Codeberg - orgwebalchemy
Further Reading:
Visuals

I would love to get feedback from the community on this implementation!