Owl: a parser generator where the grammar reads like a regular expression
A parser generator for visibly pushdown languages.
At a glance
- What is it?
- Owl generates C parsers for visibly pushdown languages, and its unusual claim is about the grammar language rather than the generated code: rules are written like regular expressions, recursion has to be declared, and the interpreter mode prints the parse tree as soon as your input ends so you can debug a grammar without writing any C. The generated parser is one header file with a linear time guarantee.
- Who is it for?
- Adopt Owl if you are writing a parser for a language with nested structure and you want the grammar to be readable by someone who has never studied parsing, because the token-based rules, the two explicit forms of recursion and the live parse tree output remove most of the usual learning curve.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 63 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A generator for visibly pushdown languages
Owl is a parser generator which targets the class of visibly pushdown languages, and the README states its three properties as a short list.
It is efficient, because parsers generated by Owl always run in linear time. It is understandable, because like regular expressions its parsing model and its error messages can be understood without talking about parser states, backtracking, lookahead, or any other implementation details. And it is easy to use, because using Owl's interpreter mode you can design, test and debug your grammar without writing any code, while an Owl grammar compiles to a single C header file which provides a straightforward parse tree API.
The target class is worth pausing on. A visibly pushdown language is one where the nesting is explicit in the syntax, which is what lets a parser know when to stop descending without guessing. The consequence for the user is that the grammar has to spell out its nesting, and the benefit is that the parser never has to backtrack.
The project is C, MIT licensed, on the master branch, with the last push on 2026-08-21 and no GitHub releases. The repository carries a COPYING.txt, a Makefile, the source in src/, the documentation in doc/, example grammars in example/, a test directory, a try/ directory for the web version, and grammar.owl at the root, which is the grammar of the grammar language itself.
Interpreter mode prints the parse tree as you type
The interpreter mode is the feature that makes the tool approachable, and it is simple to describe.
Owl reads your grammar file, then parses standard input on the fly, producing a visual representation of the parse tree as soon as you hit the end of input:
$ owl test/expr.owl
1 + 2
^D
. 1 + 2
expr:literal expr:literal
expr:plus------------------The tree is drawn with the rule names inline, so you can see which rule matched which part of the input and how the operator nodes sit above their operands.
You can point it at a file instead of standard input with the --input or -i option:
$ echo "8 * 7" > multiply.txt
$ owl test/expr.owl -i multiply.txt
. 8 * 7
expr:literal expr:literal
expr:times-----------------And you can use the interpreter on the web, at the try URL linked from the README, which is the try/ directory built for the browser with Emscripten.
The point of all this is the third claim in the list. Designing a grammar is the part that is hard, and an interpreter that shows you the tree turns that from writing code and compiling into typing a few lines and looking at a picture.
Compilation mode produces one header and one macro
The other mode reads the grammar file and does not parse anything at the moment. Instead it generates C code with functions that let you parse the input later.
$ owl -c test/expr.owl -o parser.hThe generated header is included into your C program the way any other header is:
#include "parser.h"And the implementation is pulled in by defining a macro before the include, so that the header can be included in several translation units without duplicating the definitions:
#define OWL_PARSER_IMPLEMENTATION
#include "parser.h"The README points at the document on using the generated parser for the details of the API.
So the whole integration is a single file with a conventional include guard pattern, which is about as small a footprint as a parser can have. Integration is one generated header with no separate library to link against. The trade is the one the README lists under its limitations, that only C is supported right now, so the generator cannot emit a parser in another language yet.
The grammar language looks like regular expressions
The rules in Owl are written like regular expressions with a few extra features, and the first example is a rule that matches a comma-separated list of numbers:
number-list = number (',' number)*The important difference from a regular expression is what the matching is over. Owl operates on tokens, like ',' and number, and not on individual characters like a regular expression would. The built-in token classes are number, identifier, integer and string, and keywords written in quotes, like 'like' or 'this', match their literal text.
Rules that refer to each other build a parse tree, and the example shows two rules and an alternation:
variable = 'var' identifier (':' type)?
type = 'int' | 'string'There is a constraint on how rules may refer to each other that looks like a limitation and is actually the core of the design. Rules can only refer to later rules, not earlier ones, so plain recursion is not allowed, and only two restricted kinds of recursion are available, guarded recursion and expression recursion.
That ordering rule is what keeps the language visibly pushdown, and it is why the parser can be linear time. The nesting has to be written down, and the two recursion forms are the two ways to write it down.
Two kinds of recursion, and what the brackets are for
Guarded recursion is recursion inside guard brackets, and the example parses a nested list of elements:
element = array | string
array = [ '{' element (',' element)* '}' ]The symbols just inside the brackets, the brace and the closing brace in this case, are the begin and end tokens of the guard bracket, and they cannot appear anywhere else in the grammar except as other begin and end tokens. That is the mechanism that guarantees the language is visibly pushdown, because all recursion is explicitly delineated by special symbols.
Expression recursion is the other form, and it uses the .operators keyword to declare unary and binary operators:
expression =
identifier | number | parens : value
.operators prefix
'-' : negate
.operators infix left
'+' : add
'-' : subtract
parens = [ '(' expression ')' ]The precedence rules are simple and stated directly: operators in the same .operators clause have the same precedence level, and clauses nearer the top of the list are higher in precedence.
The README also makes the point that these forms look limiting but go a long way. If you are willing to use ? and : as the begin and end tokens, the C ternary operator can be written as an infix operator:
expr =
...
.operators infix left
[ '?' middle ':' ] : ternaryThere is a fuller guide to the grammar format in the grammar reference document, and the example directory holds several grammars to read, including a calculator, an egg language, a hex colour grammar, a JSON-like grammar and a list comprehension grammar.
What Owl is not, and the four limitations that matter
The section titled what owl isn't is the most useful part of the README, because it is written by someone who knows where the edges are.
The non-goals are two. Parsing arbitrary context-free languages or other, more general languages is out of scope, and so is constant-space streaming-style parsing, because the way Owl parses operators depends on having a syntax tree representation.
The current limitations are four. Large grammars are a problem, because Owl uses precomputed DFAs and action tables, which can blow up in size as grammars get more complex, and the README says it would be nice to build the DFAs incrementally in the future.
Memory use is the limitation to read carefully. Generated parsers store a small, single digit bytes, amount of information for every token while parsing, in order to resolve nondeterminism. If a decision about what to match depends on a token which appears much later in the text, the parser needs to store enough information to go back and make that decision at the point the token appears. Instead of analyzing how long to wait before making these decisions, it just waits until the end, gathering data for the entire input before creating the parse tree.
That is a real constraint on input size, and it is the price of the guarantee that the parser does not have to backtrack. A tool that streams a gigabyte of input is not what this is.
Error messages can show the token where the error happened, but they cannot suggest how to fix it. And only C is supported for code generation.
The footnotes add one more thing about the error messages, that this includes errors caused by ambiguities, where the grammar can match the same text in two different ways, and Owl shows the text in question alongside its two conflicting matches, with a message that the grammar is ambiguous.
The build is a Makefile, and the tests are golden files
Building Owl is a make, and the README gives the three commands:
$ git clone https://github.com/ianh/owl.git
$ cd owl/
$ makeRun make install to copy the owl tool into /usr/local/bin/owl. On Windows, MSYS2 is the supported way to build it, and in the MSYS shell you run pacman -S make gcc git to install the dependencies first; to install there you use make PREFIX=/usr install, which copies to /usr/bin/owl instead.
The Makefile itself shows what portability costs. It detects the operating system with uname and picks the dynamic library flag for it, with a case for FreeBSD, a case for Windows and a default that links against libdl, and it defines the platform macros to match, including one that tells the code it is not on Unix. The C flags are -O3 with the C11 standard, -pedantic and -Wall, and two warnings switched off, for missing braces and overlong strings.
The test target is the interesting part, because it is how the project proves that the interpreter and the compiler agree. For every .owltest file in the test directory it runs the tool in interpreter mode, then runs it again in compilation mode with the output going to a temporary file, capturing standard output and standard error into the results directory both times, and then it runs git diff --stat --exit-code on test/results. If either mode changes its output, the diff is non-empty and the target fails. The tests are therefore golden files, and the two modes are checked against each other rather than against hand-written expectations.
There is a separate target that builds the browser version with Emscripten, exporting the tool under the name Owl as a modularised module with memory growth allowed, and a clean-js target that removes the generated JavaScript and WebAssembly files.
Editorial conclusion
Adopt Owl if you are writing a parser for a language with nested structure and you want the grammar to be readable by someone who has never studied parsing, because the token-based rules, the two explicit forms of recursion and the live parse tree output remove most of the usual learning curve. Do not adopt it for a general context-free grammar, since the README lists that as an explicit non-goal, and do not expect it to stream a large input in constant space, since the parser gathers data for the whole input before building the tree. Before you commit, read the memory use limitation in the README, because it decides whether the generated parser suits your input size, and remember that only C output is supported today, so if you need a parser in another language this is not the tool.
Frequently asked questions
What is the Owl parser generator?
A parser generator that targets the class of visibly pushdown languages and produces C code. The README states that parsers generated by Owl always run in linear time, and that an Owl grammar compiles to a single C header file providing a straightforward parse tree API.
How do I debug an Owl grammar?
Use interpreter mode, where Owl reads the grammar, parses standard input on the fly and prints the parse tree as soon as you end the input. You can point it at a file with the --input or -i option, or use the browser version of the interpreter linked from the README.
What kinds of recursion does an Owl grammar allow?
Only two. Guarded recursion, which is recursion inside guard brackets whose begin and end tokens may not appear anywhere else, and expression recursion, which uses the .operators keyword to declare unary and binary operators and their precedence.
What are the limitations of Owl?
The README lists four: large grammars can blow up in size because of precomputed DFAs and action tables, generated parsers gather data for the entire input before creating the parse tree, error messages can show where the error happened but cannot suggest a fix, and only C is supported for code generation. Parsing general context-free languages and constant-space streaming are explicit non-goals.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ianh-owl)Was this useful?