Skip to main content

A Rust implementation of the Parable bash parser

Project description

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-oracle approach that validates parser output against bash itself
  • Maintaining the extensive .tests corpus 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

[dependencies]
rable = "0.1"

As a Python package

pip install rable

Or build from source:

just setup       # creates venv, builds bindings, installs Parable
just develop     # rebuild after code changes

Usage

Rust

use rable::parse;

fn main() {
    let nodes = parse("echo hello | grep h", false).unwrap();
    for node in &nodes {
        println!("{node}");
    }
    // Output: (pipe (command (word "echo") (word "hello")) (command (word "grep") (word "h")))
}

Python

from rable import parse, ParseError, MatchedPairError

# Parse bash source into AST nodes
nodes = parse('if [ -f file ]; then cat file; fi')
for node in nodes:
    print(node.to_sexp())
# Output: (if (command (word "[") (word "-f") (word "file") (word "]")) (command (word "cat") (word "file")))

# Errors are raised as exceptions
try:
    parse('if')
except ParseError as e:
    print(f"Syntax error: {e}")

# Enable extended glob patterns
nodes = parse('echo @(foo|bar)', extglob=True)

The Python API is a drop-in replacement for Parable:

# Before (Parable)
from parable import parse, ParseError, MatchedPairError

# After (Rable) — same API, ~10x faster
from rable import parse, ParseError, MatchedPairError

Development

Prerequisites

  • Rust 1.93+ (see rust-toolchain.toml)
  • Python 3.12+ (for Python bindings)
  • just (task runner)

Quick start

just              # format, lint, test
just check        # same as above
just test-parable # run full Parable compatibility suite
just setup        # set up Python environment + benchmarks
just benchmark    # compare performance vs Parable

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

  1. Compatibility is correctness — output matches Parable's S-expressions exactly
  2. If it is not tested, it is not shipped — 1,604 integration tests + unit tests
  3. Simplicity is king — solve problems with least complexity
  4. Correctness over speed — match bash-oracle behavior, optimize later

Contributing

Contributions are welcome! Please ensure:

  1. All tests pass: just check must succeed
  2. Parable compatibility: just test-parable must show 1604/1604
  3. Code quality: No clippy warnings (just clippy)
  4. Formatting: Code is formatted (just fmt)

Adding new features

If you're adding support for new bash syntax:

  1. Add test cases to the appropriate .tests file in tests/parable/
  2. Implement the lexer/parser changes
  3. Verify S-expression output matches what Parable would produce
  4. Run just test-parable to 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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rable-0.1.4-cp313-cp313-win_amd64.whl (214.4 kB view details)

Uploaded CPython 3.13Windows x86-64

rable-0.1.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (358.2 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

rable-0.1.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (352.7 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

rable-0.1.4-cp313-cp313-macosx_11_0_arm64.whl (315.2 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

rable-0.1.4-cp313-cp313-macosx_10_12_x86_64.whl (326.2 kB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

File details

Details for the file rable-0.1.4-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: rable-0.1.4-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 214.4 kB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rable-0.1.4-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 8f0f669569bded7122163cf48d1a739ab358894eeb7551dc4dca639106d1a358
MD5 10c4cfff4005a60a789a29938ec02d15
BLAKE2b-256 7b267723227a7a45080f4ed7741bbee0333b7a37a366b5deb41ae0be035a2b33

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.4-cp313-cp313-win_amd64.whl:

Publisher: publish.yml on mpecan/rable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rable-0.1.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rable-0.1.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 fd056848c4fcb21cd091fabc451d877411241018c9a665e78724a42129ff41c4
MD5 f0c91ad09869872f117638c187feefce
BLAKE2b-256 067785566d5a82375b34d93f777bec661b9a5fb977a729269ec0b997a709dbab

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on mpecan/rable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rable-0.1.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for rable-0.1.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 118760d0abc95110687f3ef9438715f31dbd9fbe2a34e0e0099c8658849dcf2d
MD5 e417f35739e856c2edbe45c58ca3ce60
BLAKE2b-256 5ac209a424f935a9c7f388a3fb52b089dd4af20e82ab9704a56b631f2efd3790

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on mpecan/rable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rable-0.1.4-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rable-0.1.4-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d0e31776e82841abc8b985c01556c61e4726290aac5cc7901180fc39ee93dc82
MD5 06b65ca6983d24a728bc47aeb4f7679b
BLAKE2b-256 174d85f65db7b501b456e2bf36d955c6d8f6abebd93574f62802e399b6aa305a

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.4-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: publish.yml on mpecan/rable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rable-0.1.4-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for rable-0.1.4-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 556a77f1b98c0e3a10fb79aab8e047f839b7ae1419ffb1c63781761c4da9be9d
MD5 03b766fe38c584d5ea65b125cabe9314
BLAKE2b-256 3ce659709eeb92943232a8c9e2936b7839d0efac03ae99ef5d1d1eaca5ef9250

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.4-cp313-cp313-macosx_10_12_x86_64.whl:

Publisher: publish.yml on mpecan/rable

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page