Skip to main content

Gandora

Gandora is the project for gan, a compiler for an Elixir-flavored language that produces readable Python. You write Elixir-style modules, pattern matching, pipelines, and hygienic defmacro macros; deployment is ordinary Python managed by uv with the standard .venv layout. Where Elixir reaches its host platform through :erlang calls, Gandora reaches Python through the $ sigil: $math.sqrt(2.0) compiles to import math plus math.sqrt(2.0) with no wrapper code.

defmodule App.Mathy do
  @moduledoc "Math helpers showing patterns and interop."

  def fact(0), do: 1
  def fact(n) when n > 0, do: n * fact(n - 1)

  def norm(xs) do
    xs
    |> sum_squares()
    |> then_sqrt()
  end

  defp sum_squares(xs) do
    $functools.reduce(fn acc, x -> acc + x * x end, xs, 0)
  end

  defp then_sqrt(x), do: $math.sqrt(x)
end

compiles to plain Python with multi-clause dispatch as a match statement, private functions as _-prefixed functions, and the interop as direct imports — no Gandora runtime is needed to execute the output.

The practical guide is the language manual (中文版). Design decisions are recorded as Gandora Enhancement Proposals in geps/:

  • GEP-0000 — the proposal process and translation policy (中文)
  • GEP-0001 — language identity, surface syntax, module naming, configuration, and the gan CLI (中文)
  • GEP-0002 — the hygienic macro system (中文)
  • GEP-0003 — Python interop (中文)
  • GEP-0004 — structs (frozen dataclasses) and module attributes (中文)
  • GEP-0005 — sigils (~w, ~s, ~r, and the raw embedded-Python ~python) (中文)
  • GEP-0006 — publishing packages as ordinary PyPI wheels, with macros shipped as source and zero runtime (中文)
  • GEP-0007 — Markdown @doc with locale variants, doctests compiled to native Python doctests, gan doc / gan test (中文)
  • GEP-0008 — definition-generating macros, use/__using__, and the defattr/@on_definition attribute system (中文)
  • GEP-0009 — the ~<lang> embedded-language family (~sql, ~markdown, ...) with EEx-style <%= %> splices (中文)
  • GEP-0010 — the standard library (std/): Enum, String, Map, List, Keyword as an ordinary PyPI package (中文)
  • GEP-0011 — multi-arity name groups and \\ default parameters (中文)
  • GEP-0012 — gandora-core: the compiler as a Python extension, quoted terms as native data — tooling is written in Gandora itself (中文)
  • GEP-0013 — the gan task runner (mix/cargo role), a Gandora program in tools/gan; the Rust binary is the stage-0 compiler ganc (中文)
  • GEP-0014try/rescue/after, tail-call-optimized recursion with explicit recur, for comprehensions, and the in operator (中文)
  • GEP-0015gan-lsp, the language server written in Gandora (tools/lsp), plus the VS Code client (editors/vscode) (中文)

English GEPs are normative; the synchronized Chinese translations are generated with scripts/translate-gep.py (a DeepSeek-backed translator configured by .env) and human-reviewed per GEP-0000-R030.

Requirements

  • Rust 1.85 or newer (to build the compiler)
  • Python 3.11 or newer
  • uv for Python development (optional but recommended)

Quick start

uv tool install gandora-tool     # gan — the task runner (written in Gandora)
uv tool install gandora-lang     # ganc — the stage-0 compiler it delegates to

gan init my-app                  # adds gandora-std to dependencies
cd my-app
gan run src/main.gan

(Or build from source: cargo build --release.)

gan init creates a uv-compatible project: pyproject.toml owns Python metadata and dependencies, gandora.jsonc owns the compiler configuration, and sources live under src/. gan run compiles into .gandora/cache/ and executes with .venv/bin/python, uv run python, or python3 — whichever is available first.

gan check              # parse, expand, analyze; no output written
gan build              # compile every module into dist/
gan expand src/x.gan   # show a module after macro expansion
gan compile src/x.gan --out build/

The v0 surface

Modules (defmodule, one per file, path-derived names), def/defp with multi-clause pattern dispatch and when guards, defmacro with quote/unquote/unquote_splicing and default hygiene (var! escapes), atoms, strings with #{} interpolation, lists, tuples, maps, keyword lists, ranges, the |> pipe, if/unless/case/cond/with, anonymous functions and captures (&Mod.fun/1, &(&1 + 1)), destructuring =, alias/import/require, the interop forms of GEP-0003 (:module calls, pyimport, postfix expr.name(...), @decorate), and the GEP-0004 data declarations: defstruct (a frozen @dataclass with %Mod{...} literals, patterns, and %Mod{s | f: v} updates) plus module attributes (@app $flask.Flask("__main__") compiles to a module-level binding, so @decorate @app.route("/") works like hand-written Python). GEP-0005 adds sigils: ~w(a b c) word lists, ~s(...) strings, ~r/\d+/ compiled Python regexes, and ~python(sum(i*i for i in range(n))) — a raw sigil that splices one verbatim Python expression into the output, Gandora's rendering of Osiris's embedded-language sigils.

Anything outside the surface produces a diagnostic naming the construct (GEP-0001-R007) rather than a silent mistranslation. See examples/tour for a working multi-module program.

Publishing a package

A Gandora package is an ordinary PyPI wheel (GEP-0006):

gan init --package acme-text
cd acme-text            # write modules under src/acme_text/
gan build               # compiles into pkg/, emits marker + .gan sources
uv build && uv publish  # standard hatchling wheel, standard PyPI

A live example is gandora-text, installable straight from GitHub with uv add git+https://github.com/mjason/gandora-text. Consumers then use it from Gandora:

require AcmeText.Core     # macros: expanded at compile time from the
alias AcmeText.Core       #         .gan sources shipped in the wheel
Core.hello("world")       # functions: a plain `import acme_text.core`

The wheel introduces no runtime: its .py modules are self-contained, Python-only consumers can use them without knowing Gandora exists, and macro expansion leaves nothing behind at runtime. The compiler discovers installed packages by reading their gandora.toml markers from .venv — it never imports or executes package code.

Data mapping

Gandora Python
:atom "atom" (interned string)
nil / true / false None / True / False
list / tuple / map list / tuple / dict
keyword list [a: 1] [("a", 1)]
a..b range(a, b + 1)

Only false and nil are falsy (Elixir semantics); the generated code inserts explicit truthiness checks where needed.

Repository layout

  • crates/core — the compiler library; crates/gan — the stage-0 CLI ganc; crates/core-py — the gandora_core Python extension
  • tools/gan — the task runner; tools/lsp — the language server (both written in Gandora); editors/vscode — the VS Code client
  • docs/ — the language manual (+ docs/local/zh/ translations)
  • geps/ — Gandora Enhancement Proposals (+ geps/local/zh/ translations)
  • examples/tour — a runnable multi-module example
  • tests/e2e.rs — end-to-end tests driving the real binary
  • scripts/translate-gep.py — the GEP translation tool
cargo test             # unit + end-to-end tests

Status

v0.1.0: GEP-0001..0011 are implemented and tested — the language and CLI, hygienic macros with the full metaprogramming surface (definition-generating macros, use/__using__, defattr/ @on_definition), structs, sigils and embedded languages with <%= %> splices, wheel-based package publication, bilingual docs with native doctests, multi-arity functions with \\ defaults, and the standard library (std/, uv add gandora-std). Deferred: protocols, comprehensions, try/rescue, binaries, a formatter, an LSP.

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 Distribution

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

gandora_lang-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (680.8 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

File details

Details for the file gandora_lang-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gandora_lang-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 60c21d419e339b4dfa1400bf8e53c5a810ffa00a4fa97ba70df86dc7109bf52b
MD5 545a18ea931d9c249d8de184d54e0719
BLAKE2b-256 6621ee982962c0dd28a64dd32c6d8dd4aae4f8d52bf328c923dd1c90d2699074

See more details on using hashes here.

Provenance

The following attestation bundles were made for gandora_lang-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on mjason/gandora

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

Release history Release notifications | RSS feed

0.20.1

1 file

0.20.0

1 file

0.19.0

1 file

0.18.8

1 file

0.18.7

1 file

0.18.6

1 file

0.18.5

1 file

0.18.4

1 file

0.18.3

1 file

0.18.2

1 file

0.18.1

1 file

0.18.0

1 file

0.17.1

1 file

0.17.0

1 file

0.16.1

1 file

0.16.0

1 file

0.15.1

1 file

0.15.0

1 file

0.14.1

1 file

0.14.0

1 file

0.13.0

1 file

0.12.0

1 file

0.11.2

1 file

0.11.1

1 file

0.11.0

1 file

0.10.4

1 file

0.10.3

1 file

0.10.2

1 file

0.10.1

1 file

0.10.0

1 file

0.9.3

1 file

0.9.2

1 file

0.9.1

1 file

This release

0.9.0 This release

1 file

0.8.0

1 file

0.7.1

1 file

0.7.0

1 file

0.6.2

1 file

0.6.1

1 file

0.6.0

1 file

0.5.2

1 file

0.5.1

1 file

0.5.0

1 file

0.4.2

1 file

0.4.1

1 file

0.4.0

1 file

0.3.0

1 file

0.2.4

1 file

0.2.3

1 file

0.2.2

1 file

0.2.1

1 file

0.2.0

1 file

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page