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

Uploaded CPython 3.13Windows x86-64

rable-0.1.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (369.1 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

rable-0.1.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (363.3 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

rable-0.1.7-cp313-cp313-macosx_11_0_arm64.whl (325.5 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

rable-0.1.7-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.7-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: rable-0.1.7-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 223.1 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.7-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 1f8d98156a417bd96974efb0a267deef8c3903e89d31d650c98385feedd25cc0
MD5 70dfac16234a63cf0d4274eeabb5f6f2
BLAKE2b-256 d1c1e4ee43c4a35683af33a3fed37c6353976f6e159dc1c5a9fab29ba3b52436

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cff20b6d71a8740943d8c478dc752ff9d74c970fe22ad4410a7c4673c777f01c
MD5 49f6e6e1c12a32a9f0c66a8ebdbf2bf8
BLAKE2b-256 e52c41959a1aa1b858c057320d83a81b46dde66b70a585a6f9e9f52b6aa4a102

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 4b744bd80b0a6388d4b516c4fc7b8328ef6f14b1537bafce6d9a6ba3004b326b
MD5 df15e212063646e11bb13ab12f1fa561
BLAKE2b-256 c3ffcc1c20f5444176a8a0004693eb18eac10331be627492391a658e5161cb84

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.7-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a1dd8781a07978cb51170fc3471e6f6e194bf0cca74f140d983e169297ad0a94
MD5 e546513c35edcb9d1cdebca66a1df830
BLAKE2b-256 e632cc78e33aebbc9da4f585346d36163c12bb2147ce952e431fe21be0159f15

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.7-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 a384bae63141c20c0af21b0e11df9defc85986e9aba6d71e74644918f67a11bb
MD5 56b97ed515f44ca4f72110bdda2d8817
BLAKE2b-256 15c3d72354ac182801d369fdfb50e4f9e20a56917d905b305182e9da7fecb373

See more details on using hashes here.

Provenance

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