Getting Started
Learn the core concepts of Parsanol and build your first parser in minutes.
Installation
Rust
Add to your project
Ruby
Add to your Gemfile
What is a Parser Combinator?
A parser combinator is a design pattern where small, simple parsing functions (called atoms) are combined using higher-order functions (called combinators) to build complex parsers.
The Key Insight
Instead of writing one massive parser, you build small, reusable pieces and compose them. This makes parsers modular, testable, and easy to reason about.
Atoms: The Building Blocks
Atoms match the smallest units of input:
str("hello")- Match a literal stringre("[0-9]+")- Match a regex patternany()- Match any single character
Combinators: The Glue
Combinators join atoms together:
.then()- Sequence: match A followed by B.or()- Alternative: match A or B.repeat()- Repetition: match multiple times.maybe()- Optional: match zero or one time
The Two-Stage Model
Parsanol separates parsing into two distinct stages. This separation is crucial for debuggability, flexibility, and reusability.
Stage 1: Parsing
- ✓ Input: Raw string
- ✓ Output: Generic parse tree
- ✓ Domain-agnostic structure
- ✓ Debuggable and inspectable
Stage 2: Transformation
- ✓ Input: Parse tree
- ✓ Output: Your typed data
- ✓ Pattern-based matching
- ✓ Recursive transformation
Why Two Stages?
- Debug the tree - See exactly what was parsed before transforming
- Transform multiple ways - Same tree, different outputs
- Test independently - Parser tests vs transform tests
Anatomy of a Parser
Let's see how atoms combine to build a complete parser. This diagram shows the flow from basic atoms through combinators to the final parse tree.
Key Methods
.as("name")- Label a capture for later access.ignore()- Match but don't include in tree.lookahead(bool)- Peek ahead without consuming
Understanding the Parse Tree
The parse tree is a generic, domain-agnostic structure that captures what was matched, not how to interpret it. This is what enables the two-stage model.
Node Types
Value
Leaf node containing matched text
Sequence
Ordered children from .then()
Named
Labeled node from .as()
Alternative
Choice made from .or()
Anatomy of a Transform
Transforms use pattern matching to convert the generic parse tree into your domain-specific types. This is where you give meaning to the parsed structure.
Pattern Types
simple(:x)
Matches leaf values (strings, numbers)
sequence(:x)
Matches arrays of values
subtree(:x)
Matches anything (whole tree)
{ key: pattern }
Matches hashes by keys
Your First Complete Parser
Let's build a complete parser that recognizes simple arithmetic expressions. This example demonstrates both stages working together.
Goal: Parse "1 + 2 * 3"
We want to respect operator precedence: 2 * 3 should be evaluated before 1 + ....
How it works:
- Parse - The grammar recognizes the structure
- Tree - A generic tree captures what was matched
- Transform - Patterns match tree nodes and build expressions
- Evaluate - The resulting AST can be executed
Understanding Operator Precedence
One of the most challenging aspects of parsing is handling operator precedence correctly. Parsanol provides built-in support for this common pattern.