Pyla: a small pipeline-first scripting language (with an optional Gen Z dialect, no cap)
Project description
Pyla
A small, dynamically-typed, pipeline-first scripting language, implemented
in pure Python (standard library only, no dependencies). Data flows left to
right through the |> operator, so programs read in the order they execute:
range(1, 21)
|> list.filter(fn(x) { x % 2 == 0 })
|> list.map(fn(x) { x * x })
|> list.sum()
|> print; # 1540
Under the hood it is a complete language implementation with two execution engines that are held to identical behaviour by parity tests:
source text -> Lexer -> Pratt Parser -> AST -> Tree-walking Evaluator
\-> Bytecode Compiler -> Stack VM
The bytecode VM runs 2–3x faster than the tree-walker and, because it manages its own call frames, supports recursion far deeper than Python's own limit.
This repo also contains tinylm (tinylm.py): a character-level neural
language model built from scratch in numpy with hand-derived backpropagation
(verified by numerical gradient check), trained on the Pyla programs in
examples/ so it generates Pyla-looking code.
It supports first-class functions, closures, recursion, arrays, hashes,
if/else if/else, while, C-style for, break/continue, a numeric
tower (int promotes to float), short-circuit and/or, and error messages that
point at a line number.
Install
pip install pyla-lang # the language is Pyla; the command is pyla
(From a clone of this repo, pip install . does the same.)
pyla # interactive REPL
pyla examples/fib.pyla # run a program (bytecode VM, the default)
pyla test mytests/ # run every .pyla file in a dir; exit 1 if any fail
pyla --walk examples/fib.pyla # run on the tree-walking evaluator
pyla -c 'print(2 + 3)' # run a one-liner
pyla --version
Exit codes are a contract: 0 success, 1 parse/runtime error or failed
assert or failed tests, 2 usage error — so Pyla plugs directly into any
CI or grading harness. The full language fits in one document:
SPEC.md, whose claims are themselves executable
(pyla tests/spec_conformance.pyla).
Without installing, python pyla.py ... from this directory does the same.
Benchmark the two engines with python bench.py.
Run the test suite (79 tests, standard-library unittest):
python -m unittest discover -s tests
Train and sample the tiny language model (requires numpy):
python tinylm.py check # verify hand-written gradients numerically
python tinylm.py train # ~30s; saves tinylm_model.npz, prints a sample
python tinylm.py sample --temp 0.8 -n 400 --prompt "let fib = "
A taste of the language
# variables and functions are declared with `let`
let greet = fn(name) { "Hello, " + name + "!" };
print(greet("world"));
# recursion
let fib = fn(n) { if (n < 2) { n } else { fib(n-1) + fib(n-2) } };
print(fib(20)); # 6765
# closures keep private state
let counter = fn() { let n = 0; fn() { n = n + 1; n } };
let next = counter();
print(next(), next(), next()); # 1 2 3
# arrays, hashes, loops
let squares = [];
for (let i = 1; i <= 5; i = i + 1) { push(squares, i * i); }
print(squares); # [1, 4, 9, 16, 25]
let ages = {"ada": 36, "alan": 41};
print(ages["ada"]); # 36
print(ages.alan); # 41 -- h.key is sugar for h["key"]
# modules: import() returns a module's top-level bindings as a hash
let math = import("std/math");
let list = import("std/list");
print(math.sqrt(2)); # 1.414...
print(list.sort_by([3, 1, 2], fn(a, b) { a > b })); # [3, 2, 1]
The last expression in a function body (or block) is its value, so return is
optional. if/else is an expression and produces a value.
Language reference
Types
int, float, string, bool (true/false), nil, array, hash,
function. Only nil and false are falsy; everything else (including 0 and
"") is truthy.
Operators
- Arithmetic:
+ - * / %(+also concatenates strings). Integer/stays an integer when it divides evenly, otherwise becomes a float. - Comparison:
== != < > <= >=(work on numbers and strings). - Logical:
and,or(short-circuit),!(negation). - Pipeline:
x |> f(a, b)isf(x, a, b);x |> fisf(x). Left associative, binds looser than everything except=, sox + 1 |> fpipes the sum. The stdlib takes collections as first parameters so everything pipes naturally. - Assignment:
x = v,arr[i] = v,hash[k] = v(right-associative). Useletto declare; plain=reassigns an existing binding. - Indexing:
arr[i],str[i](negative indices count from the end; out-of-range reads returnnil),hash[key], andhash.keysugar.
Modules
import("path/to/module") loads a .pyla file once (cached by absolute
path), evaluates it, and returns its top-level bindings as a hash. Paths
resolve relative to the importing file, falling back to the interpreter's
bundled library. The standard library ships with std/list (map, filter,
reduce, sort_by, zip, ...) and std/math (sqrt, pow, gcd, factorial,
is_prime, ...) — both written in Pyla itself.
Control flow
if (cond) { ... } else if (cond) { ... } else { ... }
while (cond) { ... }
for (init; cond; post) { ... }
break; continue;
Builtins
print write len type push pop first last rest keys values
contains delete str int float range abs min max chr ord
input assert import split join upper lower trim replace
slice args read_file write_file append_file exists.
Diagnostics
Errors show the offending source line with a caret (parse errors) and a full Pyla call stack (runtime errors) — from both engines, identically. See DESIGN.md for the design rationale and the language post-mortems Pyla was built against.
Brainrot mode (the Gen Z dialect)
Pyla ships with an optional slang dialect. Files ending in .fr enable it
automatically; --brainrot forces it anywhere; :brainrot toggles it in the
REPL. Same grammar, same semantics, different drip:
| standard | brainrot | standard | brainrot | |
|---|---|---|---|---|
let |
fr |
true |
nocap |
|
fn |
cook |
false |
cap |
|
return |
yeet |
nil |
ghosted |
|
if / else |
vibecheck / nah |
print |
yap |
|
while / for |
grind / farm |
input |
spill |
|
break / continue |
dip / skip |
assert |
sheesh |
fr fib = cook(n) {
vibecheck (n < 2) { yeet n; }
yeet fib(n - 1) + fib(n - 2);
};
yap("fib(15) is lowkey", fib(15));
sheesh(fib(10) == 55, "math is capping");
The dialect is strictly opt-in: in normal mode every slang word is just an
ordinary identifier. Run pyla --zen for the language's guiding principles.
Editor support
editor/vscode-pyla is a VS Code extension with syntax highlighting, bracket
matching, auto-indent and comment toggling for .pyla and .fr files.
Project layout
| File | Responsibility |
|---|---|
pyla/tokens.py |
Token types and the Token record |
pyla/lexer.py |
Source text -> tokens (tracks line/column) |
pyla/ast_nodes.py |
AST node dataclasses |
pyla/parser.py |
Pratt parser: tokens -> AST |
pyla/objects.py |
Runtime value types |
pyla/environment.py |
Lexical scopes / closures |
pyla/evaluator.py |
Tree-walking evaluator (reference semantics) |
pyla/compiler.py |
AST -> bytecode compiler |
pyla/vm.py |
Stack-based bytecode virtual machine |
pyla/builtins.py |
Built-in functions |
pyla/modules.py |
Module loader (import), caching + circular-import detection |
pyla/std/ |
Standard library, written in Pyla (std/list, std/math) |
pyla/repl.py |
Interactive REPL |
pyla/cli.py |
The pyla command (VM by default, --walk for the tree-walker) |
pyla.py |
Repo-local wrapper around the CLI |
pyproject.toml |
Packaging; pip install . provides the pyla command |
bench.py |
Tree-walker vs VM benchmark |
tinylm.py |
From-scratch neural language model trained on Pyla code |
examples/ |
Sample programs (incl. a Brainfuck interpreter written in Pyla) |
tests/ |
unittest suite: lexer, parser, evaluator, VM parity |
Examples
examples/fib.pyla— recursion and iterationexamples/closures.pyla— closures and partial applicationexamples/higher_order.pyla—map/filter/reducewritten in Pylaexamples/fizzbuzz.pyla— control flow,break/continueexamples/data_structures.pyla— arrays, hashes, nesting, negative indexingexamples/modules_demo.pyla— imports, dot access, stdlib, struct-like objectsexamples/pipelines.pyla— the|>operator doing what it does bestexamples/brainrot.fr— the Gen Z dialect, in its natural habitatexamples/brainfuck.pyla— a Brainfuck interpreter written in Pyla
Credits
Pyla was created by Bhavya Sree Pyla, designed and implemented in collaboration with Claude (Anthropic's Fable 5 model) via Claude Code. The language-design post-mortems it was built against are documented in DESIGN.md. MIT licensed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyla_lang-0.4.0.tar.gz.
File metadata
- Download URL: pyla_lang-0.4.0.tar.gz
- Upload date:
- Size: 44.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
35cd294d3eac2257d019a65e9bdf469b2eb28bb23a53c11e448b36b34ae68e55
|
|
| MD5 |
70ed567d0e5db08e0e33c85aa1191ea4
|
|
| BLAKE2b-256 |
30706c9ccc58165e87bba23fca56793a3fe29d2254a4719ee61981bb2b2c9f06
|
File details
Details for the file pyla_lang-0.4.0-py3-none-any.whl.
File metadata
- Download URL: pyla_lang-0.4.0-py3-none-any.whl
- Upload date:
- Size: 36.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77b8d571b570a103d251ea33376e0d978b2423568eba8598b1167fcfa4a0f15c
|
|
| MD5 |
aeaafbe660e92181875f2cbd7a1b6afc
|
|
| BLAKE2b-256 |
12affa88aba2504822eeef3d86996bf26a95b95ebfd02e4b16677c78b1cde079
|