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 fn becomes a public spec entry; non-pub fns are package-body- private.
  • The fn returns no value, so it lowers to PROCEDURE, not FUNCTION.
  • The parameter name gets a p_ prefix — pell uses naming conventions to avoid clashes with column names inside sql!{} blocks.
  • log::info(...) lowers to log.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:

  • json lowers to VARCHAR2(32767) instead of the native JSON type.
  • bool lowers to NUMBER(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

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. Either cd to the repo or use the full path to the pell script.
  • ModuleNotFoundError: No module named 'pell' — the venv isn’t active or pip install -e . wasn’t run.
  • pell: examples/01_hello.pell: ParseError with a column number — the lexer or parser doesn’t recognize something. Run pell tokens or pell parse on 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.