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.5-cp313-cp313-win_amd64.whl (221.4 kB view details)

Uploaded CPython 3.13Windows x86-64

rable-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (367.5 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

rable-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (361.7 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

rable-0.1.5-cp313-cp313-macosx_11_0_arm64.whl (323.0 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

rable-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl (334.2 kB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

File details

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

File metadata

  • Download URL: rable-0.1.5-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 221.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.5-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 9af70fb9ad679bcb1129e65447fbe68b1a43721307abfff393b2c02fd3bc3f40
MD5 66b777ac5cd892dbbd44ae5c73605e47
BLAKE2b-256 7376b0d3e01482bf0272164a7bd5547d289a10962adbbe818c8f501dfb076ce9

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.5-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.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rable-0.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 4ca4a9ebc28c278bc0eb384f55f547748d71971cd5a64425298cb804ebdc9f3e
MD5 b503e63a0ed1c1adcb542e48c14f5161
BLAKE2b-256 b19d9010e62fdb842f01b19c8e4d6f0c1b22eff2bd114e69411f1ce6874b9921

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.5-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.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for rable-0.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 1461f862c009d0b2d94ebea78f0df9cc45f4eb4a6d6541c64b54cd2fffc9387c
MD5 d3b2f4c58a7a65b0d8a3afcc1a8a1c70
BLAKE2b-256 336b3ae45e452b8887ea716e7e10add2b5ff08f2dbfeb73420c995986042faf3

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.5-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.5-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rable-0.1.5-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a6aae2fd3e71182f70528d45b7c4ae0a91b1b86f535fbeeab9a98674ca6faa69
MD5 503c2cfa3be60c1f8352787c92742276
BLAKE2b-256 a31467b022ecb84fa6ad02806694ad43867e528a76b61a68a1e691760e56bcb9

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.5-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.5-cp313-cp313-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for rable-0.1.5-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 64f5c7618eab4f818767d803bfb9653024546d0d351dcc535813cca21cc3c9e7
MD5 3899117ca5d94b66bc47dca51806c246
BLAKE2b-256 a0f9b8e94640210c969fc0ad037e246cf22a736a17b4a1c4d3e7f16779adebf8

See more details on using hashes here.

Provenance

The following attestation bundles were made for rable-0.1.5-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