logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·01.11.26
← Back to news

Parsing Expression Grammar vs. Regexes: Building Org Parser in Lisp, Export HTML

Original article
jointhefreeworld.org|73 points|14 comments|by jjba23|Sep 24, 2026

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 :: Josep
  • project :: orgwebalchemy
  • language :: 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

ElementDescription
HeadingsLines starting with nnn asterisks (*)
ParagraphsAny text not identified as a special construct
ListsNested Unordered, Ordered, and Description lists
Inline MarkupItalics, Bold, and inline code
LinksSupports both raw links and links with descriptions
SeparatorsHorizontal lines (5 or more dashes: -----)
TablesStandard Org-style tables
Blocks#+begin_src, #+begin_example, and #+begin_quote
HTML ExportRaw 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 SSS: S={item1,item1.1,item1.2,item2}S = \{item_1, item_{1.1}, item_{1.2}, item_2\}S={item1​,item1.1​,item1.2​,item2​} 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:

  • Wikipedia: Parsing Expression Grammar
  • GNU Emacs Lisp PEG Tutorial
  • GNU Guile PEG Tutorial

Visuals

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