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

Uploaded CPython 3.13Windows x86-64

rable-0.1.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (369.0 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

rable-0.1.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (363.0 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

rable-0.1.6-cp313-cp313-macosx_11_0_arm64.whl (325.3 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

rable-0.1.6-cp313-cp313-macosx_10_12_x86_64.whl (336.0 kB view details)

Uploaded CPython 3.13macOS 10.12+ x86-64

File details

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

File metadata

  • Download URL: rable-0.1.6-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 223.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.6-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 a976e884f82742d143fb060c177532889af39cf6582865056dc48adee76743a8
MD5 97d059c674f8e8c7fbf5eb17f809d6ad
BLAKE2b-256 e3402a193b637e251c6a3cf8425ce294c9109c837eb047dc1fc87db9ac03defa

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 dab8a1d3a5baf9593715374b5295269b580483f5201fbbf56089698e3a2586c7
MD5 818eabb53f6a33d21224ca4e4f440b3e
BLAKE2b-256 6acc6f0676642ebdeb0568bcc6e6203d11abfddc12762db0307c6c5e3a0cf543

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 cf1a7612fd0cf89d504d1c0f75bb9fc57918af3601006de13431816f1d203eed
MD5 cb70c72e520bef4935250bca4807ef3a
BLAKE2b-256 085a15ad7dd61ba2c895c583e1c5a85950fb6054c469089b4f7ba9ef04f2a8ec

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.6-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 12b14bd730c21bd4d815857956ff03d1faeab193146c7469b1d35f498fda573a
MD5 106e84df3fb5266081c40264f38deee5
BLAKE2b-256 5091ceedcad756c7e53a71e5bff721fca76767bf786a200643cbd39dd9ba06d1

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for rable-0.1.6-cp313-cp313-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 8b40306687e40facef0b916d9a5e3859e8b9307bb9992161e615576fe56a903a
MD5 663822eb55f61bf09378b0cfc8b23e48
BLAKE2b-256 6a83818f58a394543819776593f0abed7c2e049342f3aa0b880e39558d5e4691

See more details on using hashes here.

Provenance

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