This chapter takes you from “I have nothing installed” to “I just compiled my first pell program and read the PL/SQL it produced.” If you’ve got Python 3.11+ and a clone of this repo, you’re 30 seconds from the first compile.
Installation
pell is a single Python package. From the repo root:
cd compiler
python -m venv .venv
.venv/bin/pip install -e .
That’s it. No native dependencies, no extension modules. If you want
the optional pell exec / pell repl subcommands (which talk to a
real Oracle database), install with the [repl] extra:
.venv/bin/pip install -e .[repl]
The pell shim script in the repo root works without the venv too —
it picks up whatever python3 is on your PATH:
./pell --help
You should see:
usage: pell [-h] {build,parse,tokens,runtime,exec,repl} ...
pell compiler v0.0.1
Hello world
Make a file called hello.pell:
module hello;
pub fn greet(name: text) {
log::info(name);
}
Compile it:
./pell build hello.pell
You’ll get:
CREATE OR REPLACE PACKAGE hello AS
PROCEDURE greet(p_name IN VARCHAR2);
END hello;
/
CREATE OR REPLACE PACKAGE BODY hello AS
PROCEDURE greet(p_name IN VARCHAR2) IS
BEGIN
log.info(p_name);
END greet;
END hello;
/
This is the deployable artifact. Hand it to SQL*Plus, SQLcl, SQLDeveloper
— whatever you use to apply DDL to Oracle — and you get a hello
package with a greet procedure in it.
Things worth noticing about the lowering:
module hello;becomes a CREATE PACKAGE + CREATE PACKAGE BODY pair. Pell auto-generates both halves; you never duplicate the signature.pub fnbecomes a public spec entry; non-pubfns are package-body- private.- The fn returns no value, so it lowers to
PROCEDURE, notFUNCTION. - The parameter
namegets ap_prefix — pell uses naming conventions to avoid clashes with column names insidesql!{}blocks. log::info(...)lowers tolog.info(...)because pell uses::for namespace separators and PL/SQL uses..
Build options
# write to a file instead of stdout
./pell build hello.pell -o /tmp/hello.sql
# compile every .pell in a directory into matching .sql files
./pell build my_project/ -d my_project_built/
# target Oracle 19c instead of 23ai (default is 23)
./pell build hello.pell --target 19c
The --target 19c flag downgrades a few constructs that don’t exist
in 19c:
jsonlowers toVARCHAR2(32767)instead of the native JSON type.boollowers toNUMBER(1)at SQL-level crossings (OBJECT attributes, table columns).
Everything else is identical.
Reading the output
The first thing you should do after compiling is read the PL/SQL. pell tries hard to emit code that’s idiomatic, indented, and reads like something a human might have written — not like the compressed machine-generated output you might fear from a transpiler.
Pell emits a header preamble at the top of every output file:
-- ======================================================================
-- Generated by pell 0.0.1 from module hello
-- Source: hello.pell
-- SHA-256: e39af9c9e3454212b4ae2ec64054f840c96f4957f53311612cbda8af2ab81791
-- pell git: 2b77ce0
-- Target: Oracle 23
-- Schema: (none — unqualified)
-- Built at: 2026-05-23 12:34:56 UTC
-- DO NOT EDIT — regenerate with `pell build`
-- ======================================================================
The SHA-256 fingerprints the source so you can tell at a glance whether
two deployments came from the same input. The git SHA pins the compiler
version that produced the file. Use --reproducible to omit the
timestamp and uncommitted flag when you need byte-stable snapshots
(this is what the test suite does):
./pell build hello.pell --reproducible
What to read next
- Modules and functions — fn signatures, parameters, modes, return types
- Records and types — primitives, records, Option, Result, lists
- SQL blocks — the
sql!{}macro,.one(),.collect(),.first()
If you’d rather skim the entire surface first, jump to Compilation model — that chapter explains how all the pieces fit together at the package level.
Troubleshooting
pell: command not found— you’re outside the repo root. Eithercdto the repo or use the full path to thepellscript.ModuleNotFoundError: No module named 'pell'— the venv isn’t active orpip install -e .wasn’t run.pell: examples/01_hello.pell: ParseErrorwith a column number — the lexer or parser doesn’t recognize something. Runpell tokensorpell parseon the file to see the token stream / AST and pinpoint the issue.- The compiled PL/SQL won’t install on Oracle — open an issue with the pell source, the emitted SQL, and the exact Oracle error. The emitter is the part most likely to have bugs.