Skip to main content

Snail programming language interpreter

Project description

Snail logo

Snail

What do you get when you shove a snake in a shell?

Snail, while I hope it is useful to myself and others, is my attempt at improving my knowledge of AI code developement. Things are probably broken in interesting and horrible ways.


Snail is a programming language that compiles to Python, combining Python's power with Perl/awk-inspired syntax for quick scripts and one-liners. No more whitespace sensitivity—just curly braces and concise expressions.

✨ What Makes Snail Unique

Curly Braces, Not Indentation

Write Python logic without worrying about tabs vs spaces:

def process(items) {
    for item in items {
        if item > 0 { print(item) }
        else { continue }
    }
}

Built-in Subprocess Pipelines

Shell commands are first-class citizens with $() capture and | piping:

# Capture command output with interpolation
name = "world"
greeting = $(echo hello {name})

# Pipe data through commands
result = "foo\nbar\nbaz" | $(grep bar) | $(cat -n)

# Check command status
@(make build)?  # returns exit code on failure instead of raising

Compact Error Handling

The ? operator makes error handling terse yet expressive:

# Swallow exception, get the error object
err = risky_operation()?

# Provide a fallback value (exception available as $e)
value = js(data):{}?
details = fetch_url(url):"Error: {$e}"?

# Access attributes directly
name = risky()?.__class__.__name__
args = risky()?.args[0]

Regex Literals

Pattern matching without import re:

if email in /^[\w.]+@[\w.]+$/ {
    print("Valid email")
}

# Compiled regex for reuse
pattern = /\d{3}-\d{4}/
match = pattern.search(phone)

Awk Mode

Process files line-by-line with familiar awk semantics:

#!/usr/bin/env -S snail --awk -f
BEGIN { total = 0 }
/^[0-9]+/ { total = total + int($f[0]) }
END { print("Sum:", total) }

Built-in variables: $l (line), $f (fields), $n (line number), $fn (per-file line number), $p (file path), $m (last match).

Pipeline Operator

The | operator enables data pipelining through pipeline-aware callables:

# Pipe data to subprocess stdin
result = "hello\nworld" | $(grep hello)

# Chain multiple transformations
output = "foo\nbar" | $(grep foo) | $(wc -l)

# Custom pipeline handlers
class Doubler {
    def __call__(self, x) { return x * 2 }
}
doubled = 21 | Doubler()  # yields 42

# Use placeholders to control where piped values land in calls
greeting = "World" | greet("Hello ", _)  # greet("Hello ", "World")
excited = "World" | greet(_, "!")        # greet("World", "!")
formal = "World" | greet("Hello ", suffix=_)  # greet("Hello ", "World")

When a pipeline targets a call expression, the left-hand value is passed to the resulting callable. If the call includes a single _ placeholder, Snail substitutes the piped value at that position (including keyword arguments). Only one placeholder is allowed in a piped call. Outside of pipeline calls, _ remains a normal identifier.

JSON Queries with JMESPath

Parse and query JSON data with the js() function and structured pipeline accessor:

# Parse JSON and query with $[jmespath]
data = js($(curl -s api.example.com/users))
names = data | $[users[*].name]
first_email = data | $[users[0].email]

# Inline parsing and querying
result = js('{"foo": 12}') | $[foo]

# JSONL parsing returns a list
names = js('{"name": "Ada"}\n{"name": "Lin"}') | $[[*].name]

Full Python Interoperability

Snail compiles to Python AST—import any Python module, use any library:

import pandas as pd
from pathlib import Path

df = pd.read_csv(Path("data.csv"))
filtered = df[df["value"] > 100]

🚀 Quick Start

# Install from PyPI
pip install snail-lang

# Run a one-liner
snail "print('Hello, Snail!')"

# Execute a script
snail -f script.snail

# Awk mode for text processing
cat data.txt | snail --awk '/error/ { print($l) }'

🏗️ Architecture

Snail compiles to Python through a multi-stage pipeline:

flowchart TB
    subgraph Input
        A[Snail Source Code]
    end

    subgraph Parsing["Parsing (Pest PEG Parser)"]
        B1[crates/snail-parser/src/snail.pest<br/>Grammar Definition]
        B2[crates/snail-parser/<br/>Parser Implementation]
    end

    subgraph AST["Abstract Syntax Tree"]
        C1[crates/snail-ast/src/ast.rs<br/>Program AST]
        C2[crates/snail-ast/src/awk.rs<br/>AwkProgram AST]
    end

    subgraph Lowering["Lowering"]
        D1[crates/snail-lower/<br/>AST → Python AST Transform]
        D2[python/snail/runtime/<br/>Runtime Helpers]
    end

    subgraph Execution
        E1[python/snail/cli.py<br/>CLI Interface]
        E2[pyo3 extension<br/>in-process exec]
    end

    A -->|Regular Mode| B1
    A -->|Awk Mode| B1
    B1 --> B2
    B2 -->|Regular| C1
    B2 -->|Awk| C2
    C1 --> D1
    C2 --> D1
    D1 --> D2
    D1 --> E1
    D2 --> E1
    E1 --> E2
    E2 --> F[Python Execution]

    style A fill:#e1f5ff
    style F fill:#e1ffe1
    style D2 fill:#fff4e1

Key Components:

  • Parser: Uses Pest parser generator with PEG grammar defined in src/snail.pest
  • AST: Separate representations for regular Snail (Program) and awk mode (AwkProgram) with source spans for error reporting
  • Lowering: Transforms Snail AST into Python AST, emitting helper calls backed by snail.runtime
    • ? operator → __snail_compact_try
    • $(cmd) subprocess capture → __SnailSubprocessCapture
    • @(cmd) subprocess status → __SnailSubprocessStatus
    • Regex literals → __snail_regex_search and __snail_regex_compile
  • Execution: Compiles Python AST directly for in-process execution
  • CLI: Python wrapper (python/snail/cli.py) that executes via the extension module

📚 Documentation

🔌 Editor Support

Vim/Neovim plugin with syntax highlighting, formatting, and run commands:

Plug 'sudonym1/snail', { 'rtp': 'extras/vim' }

See extras/vim/README.md for details. Tree-sitter grammar available in extras/tree-sitter-snail/.

🛠️ Building from Source

Prerequisites

Python 3.10+ (required at runtime)

Snail runs in-process via a Pyo3 extension module, so it uses the active Python environment.

Installation per platform:

  • Ubuntu/Debian: sudo apt install python3 python3-dev
  • Fedora/RHEL: sudo dnf install python3 python3-devel
  • macOS: brew install python@3.12 (or use the system Python 3)
  • Windows: Download from python.org

No Python packages required: Snail vendors jmespath under snail.vendor.

Rust toolchain (cargo and rustc)

Install Rust using rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

This installs cargo (Rust's package manager) and rustc (the Rust compiler). After installation, restart your shell or run:

source $HOME/.cargo/env

Verify installation:

cargo --version  # Should show cargo 1.70+
rustc --version  # Should show rustc 1.70+
python3 --version  # Should show Python 3.10+

maturin (build tool)

pip install maturin

Build and Install

# Clone the repository
git clone https://github.com/sudonym1/snail.git
cd snail

# Create and activate a venv (recommended)
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Build and install into the venv
maturin develop

# Or build wheels for distribution
maturin build --release

Running Tests

# Run all Rust tests (parser, lowering, awk mode; excludes proptests by default)
cargo test

# Run tests including property-based tests (proptests)
cargo test --features run-proptests

# Check code formatting and linting
cargo fmt --check
cargo clippy -- -D warnings

# Build with all features enabled (required before committing)
cargo build --features run-proptests

# Run Python CLI tests
python -m pytest python/tests

Note on Proptests: The snail-proptest crate contains property-based tests that are skipped by default to keep development iteration fast. Use --features run-proptests to run them. Before committing, verify that cargo build --features run-proptests compiles successfully.

Troubleshooting

Using with virtual environments:

Activate the environment before running snail so it uses the same interpreter:

# Create and activate a venv
python3 -m venv myenv
source myenv/bin/activate  # On Windows: myenv\Scripts\activate

# Install and run
pip install snail-lang
snail "import sys; print(sys.prefix)"

📋 Project Status

See docs/PLANNING.md for the development roadmap.

Project details


Download files

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

Source Distribution

snail_lang-0.3.7.tar.gz (70.7 kB view details)

Uploaded Source

Built Distributions

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

snail_lang-0.3.7-cp310-abi3-win_amd64.whl (497.2 kB view details)

Uploaded CPython 3.10+Windows x86-64

snail_lang-0.3.7-cp310-abi3-manylinux_2_34_x86_64.whl (4.4 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.34+ x86-64

snail_lang-0.3.7-cp310-abi3-macosx_11_0_arm64.whl (535.9 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file snail_lang-0.3.7.tar.gz.

File metadata

  • Download URL: snail_lang-0.3.7.tar.gz
  • Upload date:
  • Size: 70.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for snail_lang-0.3.7.tar.gz
Algorithm Hash digest
SHA256 4766b21c8015c643a10e02003e4e0abf7ec5ff5fd34301473b3026c66aa62d71
MD5 e0f43d3fac5a3bd87dab1f7f2af09274
BLAKE2b-256 e69fad6bce0d06fb953440302d1c38039a5f3426ca9f32bb626a4902a8ae23f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for snail_lang-0.3.7.tar.gz:

Publisher: release.yml on sudonym1/snail

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

File details

Details for the file snail_lang-0.3.7-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: snail_lang-0.3.7-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 497.2 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for snail_lang-0.3.7-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 928637afb5f877892f954581fa94b76f9563089f44344daf0d526fd256271cd6
MD5 52855eea91ffa6a35a51d5ec6de05bc1
BLAKE2b-256 7f7f051785ed80a2974d603ab3122639a98e7260feca2b8a8149b262c0871b18

See more details on using hashes here.

Provenance

The following attestation bundles were made for snail_lang-0.3.7-cp310-abi3-win_amd64.whl:

Publisher: release.yml on sudonym1/snail

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

File details

Details for the file snail_lang-0.3.7-cp310-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for snail_lang-0.3.7-cp310-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 fefefc592ee014885d2cc3178abd54b70ef4a7b9f3a61cd68717b9f417d3e424
MD5 62a37198c521fe7833e400f9856a5059
BLAKE2b-256 2679c49bd276ecce70427c78fc26a905d9016fd3267d4763ec0bf65599e7a301

See more details on using hashes here.

Provenance

The following attestation bundles were made for snail_lang-0.3.7-cp310-abi3-manylinux_2_34_x86_64.whl:

Publisher: release.yml on sudonym1/snail

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

File details

Details for the file snail_lang-0.3.7-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for snail_lang-0.3.7-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5933ea111e759181a61dedf0b518f2cbfa02f76e7e7a5c8ee24b43e1bb3b3fac
MD5 9bd5fe9f526b25ece2fa3132bcf6cf2c
BLAKE2b-256 3bf77d7a5b64b50f83378f19890aa3b9a7a6aa32fdcae5142f172657a6f133f3

See more details on using hashes here.

Provenance

The following attestation bundles were made for snail_lang-0.3.7-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on sudonym1/snail

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