Rable
A complete GNU Bash 5.3-compatible parser, written in Rust.
Rable is a from-scratch reimplementation of Parable — the excellent Python-based bash parser by @ldayton. It produces identical S-expression output and provides a drop-in replacement Python API via PyO3.
Acknowledgments
This project would not exist without Parable.
Parable is a remarkable piece of work — a complete, well-tested bash parser that produces clean S-expression AST output validated against bash's own internal parser. Its comprehensive test suite (1,604 tests across 36 files) defines the gold standard for bash parsing correctness, and Rable's compatibility is measured entirely against it.
We are deeply grateful to @ldayton for:
- Building a high-quality, MIT-licensed bash parser that others can learn from and build upon
- Creating the
bash-oracleapproach that validates parser output against bash itself - Maintaining the extensive
.testscorpus that made Rable's development possible - Designing the clean S-expression output format that Rable faithfully reproduces
Rable exists because Parable showed the way. Thank you.
Compatibility
| Metric | Value |
|---|---|
| Parable test compatibility | 1,604 / 1,604 (100%) |
| Test files at 100% | 36 / 36 |
| S-expression output | Identical to Parable |
Performance
Rable is approximately 9.5x faster than Parable across all test inputs:
| Input Type | Parable | Rable | Speedup |
|---|---|---|---|
| Simple command | 41us | 5us | 8.1x |
| Pipeline (5 stages) | 144us | 14us | 10.6x |
| Nested compound | 265us | 27us | 10.0x |
| Complex real-world script | 640us | 67us | 9.5x |
| Overall | 2.1ms | 221us | 9.5x |
Run just benchmark to reproduce these results on your machine.
Installation
As a Rust library
[]
= "0.1"
As a Python package
Or build from source:
Usage
Rust
use parse;
Python
# Parse bash source into AST nodes
=
# Output: (if (command (word "[") (word "-f") (word "file") (word "]")) (command (word "cat") (word "file")))
# Errors are raised as exceptions
# Enable extended glob patterns
=
The Python API is a drop-in replacement for Parable:
# Before (Parable)
# After (Rable) — same API, ~10x faster
Development
Prerequisites
- Rust 1.93+ (see
rust-toolchain.toml) - Python 3.12+ (for Python bindings)
- just (task runner)
Quick start
Available commands
| Command | Description |
|---|---|
just |
Format, lint, and test (default) |
just fmt |
Format all Rust code |
just clippy |
Run clippy with strict settings |
just test |
Run all Rust tests |
just test-parable |
Run Parable compatibility suite |
just test-file NAME |
Run a specific test file |
just setup |
Full Python environment setup |
just develop |
Build and install Python bindings |
just test-python |
Run Parable's test runner with Rable |
just benchmark |
Performance benchmark vs Parable |
just ci |
Run exactly what CI runs |
just clean |
Clean build artifacts |
Architecture
Rable is a hand-written recursive descent parser with a context-sensitive lexer:
| Module | Responsibility |
|---|---|
lexer/ |
Context-sensitive tokenizer with heredoc, quote, and expansion handling |
parser/ |
Recursive descent parser for all bash constructs |
ast.rs |
50+ AST node types covering the full bash grammar |
sexp/ |
S-expression output with word segment processing |
format/ |
Canonical bash reformatter (used for command substitution content) |
python.rs |
PyO3 bindings (feature-gated) |
Design principles
- Compatibility is correctness — output matches Parable's S-expressions exactly
- If it is not tested, it is not shipped — 1,604 integration tests + unit tests
- Simplicity is king — solve problems with least complexity
- Correctness over speed — match bash-oracle behavior, optimize later
Contributing
Contributions are welcome! Please ensure:
- All tests pass:
just checkmust succeed - Parable compatibility:
just test-parablemust show 1604/1604 - Code quality: No clippy warnings (
just clippy) - Formatting: Code is formatted (
just fmt)
Adding new features
If you're adding support for new bash syntax:
- Add test cases to the appropriate
.testsfile intests/parable/ - Implement the lexer/parser changes
- Verify S-expression output matches what Parable would produce
- Run
just test-parableto confirm no regressions
Code limits
| Limit | Value |
|---|---|
| Line width | 100 chars |
| Function length | 60 lines |
| Cognitive complexity | 15 |
| Function arguments | 5 |
| Clippy | deny(unwrap_used, expect_used, panic, todo) |
License
MIT License. See LICENSE for details.
Disclosure
Rable is a complete reimplementation of Parable in Rust. It was built by studying Parable's test suite and output format, not by translating Parable's Python source code. The test corpus (tests/parable/*.tests) originates from the Parable project and is used under its MIT license.