Hybrid algorithmic + LLM source-to-source transpiler. N-to-M across many languages through a single shared IR pipeline.
10 frontends: Python, C, C++, Java, C#, TypeScript, JavaScript, Fortran, Visual Basic, and Assembly (via PyGhidra → C-like pseudocode). 5 targets: Rust, C, Mojo, Python, Fortran.
It is built for migrating large C++ codebases (EnergyPlus → Mojo and Python is the main driver), so besides the translator itself it ships batch drivers, a verification stack, a migration-progress tool, an NVIDIA fine-tuning toolchain for a C++/Python → Mojo model, and the machinery to make that model's drafts usable: a deterministic post-pass, best-of-n selection against an oracle, and a bit-exactness evaluation harness.
- Requirements · Quick start · Command-line tools
- How it works · Fidelity dial · Status
- Verification and reporting
- Migrating a large codebase
- Using the local fine-tuned model
- Mojo fine-tuning (NVIDIA) · Documentation
- Layout · Development · Roadmap · Licence
- Python 3.14 or newer and uv. just and direnv are optional conveniences.
- Target toolchains only for
--verifyand the end-to-end matrix:rustc, a C compiler,gfortran,mojo. Cells whose toolchain is missing reportSKIP, notPASS. - API keys, only for the LLM paths:
ANTHROPIC_API_KEYfortranspile --infer-with-llmand--llm-rename.- An OpenRouter key (
OPENROUTER_API_KEY) fortranspile-llmand the stateless runner'sopenrouterbackend.transpile-llm --endpoint URLtalks to a local OpenAI-compatible server instead (Ollama, LM Studio, llama.cpp, vLLM) and needs no key.
- Optional extras:
clangfor--ir-augment; thecodebase-memory-mcpCLI for--cbm;uv sync --group apifortranspile-api. - A CUDA build of PyTorch and an NVIDIA GPU only for fine-tuning (see Mojo fine-tuning).
direnv allow # one-time: auto-activates .venv on cd
just setup # uv sync
just example # transpile examples/add.py to Rust and verify it
just check # lint + full test suiteWithout direnv/just:
uv sync
uv run transpile examples/add.py --target rust # print the Rust translation
uv run transpile examples/add.py --target rust --verify # also compile it with rustc
uv run pytestuv sync installs these entry points:
| Command | What it does |
|---|---|
transpile |
Translate one file through the strict HIR → MIR → LIR pipeline. Choose --source/--target, --verify, --fidelity, --path. |
transpile-levels |
Run at object · file · module · folder granularity, with --engine {strict|lift}. |
transpile-llm |
Batch C++/Fortran → Mojo from a JSON config, through OpenRouter or a local server (--endpoint). Repair loop, parallel workers, --postpass, best-of-n with --samples/--verify-cmd, --domain-prompts. |
transpile-postpass |
Deterministic fixes for LLM-drafted Mojo kernels: C++/pre-1.0 syntax, Array1D loops, glibc-exact maths, nc_mul guards. See below. |
transpile-build-config |
Scan a source tree and write the JSON config that transpile-llm consumes. |
transpile-comments |
Strip C++ comments from the sources referenced by a config, or from a single file. |
transpile-api |
HTTP API server. Needs uv sync --group api. |
Useful transpile options beyond the basics:
| Option | Effect |
|---|---|
--path python_pivot |
Route C++ → Python → target, with Python as a shared IR (C++ sources only). |
--ir-augment |
Compile C/C++ to LLVM IR to pre-fill type holes without LLM calls (needs clang). |
--trace-types |
Run Python under sys.settrace and use the recorded runtime types to fill holes. |
--infer-with-llm, --llm-rename |
Ask the LLM for what algorithmic inference could not resolve, or to rename opaque locals (Ghidra local_10). |
-I DIR, --include-impls |
C/C++: inline local #include "X.h" headers (and their sibling .cpp) into one translation unit. |
--cbm REPO_ROOT |
C++: recover the cross-file class/method/macro surface from a codebase-memory-mcp graph. |
--provenance FILE |
Write a JSON sidecar mapping every LIR node to its originating HIR node. |
Examples:
uv run transpile examples/example.cpp --path python_pivot --target rust
uv run transpile-levels examples/example.cpp --level file --engine liftRun any command with --help for the full option list.
Pure-algorithmic transpilers can't infer types or map idioms; pure-LLM ones (TransCoder et al.) produce unverifiable output. So the work is split:
| Algorithmic | LLM |
|---|---|
| Parse, symbol resolution, dataflow, emission, verification | Type-hole filling, idiom mapping, naming, comments |
Three rules the codebase enforces:
- LLMs operate on typed holes, never free-form text. Every call declares a response shape and a validator.
- Every LLM output is verified: parse, compile, and behaviourally (tests).
- All LLM calls are cached by
hash(prompt + model + temperature), so the pipeline is reproducible.
Promoted LLM answers land in stdlib_maps/ as data, so the system bends from
LLM-heavy toward algorithmic-heavy over time.
Frontend → HIR (source-faithful) → MIR (normalised, typed) → LIR (target-shaped) → Backend → verify
The key invariant: HIR → MIR may not invent types it doesn't know. Missing
information becomes an UnknownT hole that a later pass (algorithmic inference
first, LLM fallback) must explicitly fill. The pipeline refuses constructs it
can't model rather than emit wrong code.
A second engine, the never-refuse lift (transpilers.lift), does whole-file
C++ → Python 1:1, emitting # TODO[lift] stubs for gaps (about 96% mechanical on
EnergyPlus). Run any granularity (object · file · module · folder/repo) with
transpile-levels --engine {strict|lift}: strict is the verified HIR pipeline,
lift never refuses.
transpile --fidelity {structural|idiomatic} (default structural) controls how
faithful the output must be to the source's architecture. Under structural,
--verify additionally runs the structural-fidelity verifier: the output's
module/function/struct skeleton and control-flow nesting must be isomorphic to the
source's. Idiom mapping is allowed only at the statement/expression level, for example
foreach → indexed loop, Rust's struct+impl split, or Fortran methods as free
functions. Added, dropped, merged or renamed functions and flattened control flow
fail the gate. idiomatic skips the skeleton gate, permitting parent-level rewrites.
It applies to the direct path only.
These figures were recorded in earlier runs and have not been re-run for this README; re-run the commands in Verification and reporting to reproduce them.
Verified end-to-end (compile + run + output match) on examples/algorithms/
(18 Python files): Rust, Mojo, Python, Fortran all 18/18; C 17/18
(needs dynamic-array growth for sieve.py).
Real-world corpus (examples/samples/, transpile-only → Rust): JavaScript
26/27, C++ 78/170, Python 32/57, Java 27/74, C 20/70.
The end-to-end Mojo and C++ numbers are compile-and-run results that require
those toolchains to be installed. The engine itself (parse → IR → emit, type
inference, the verifiers, and all pure-Python paths) is exercised by the pytest
suite, which runs without any target toolchain. Mojo target verification is gated
on a working mojo install; where it is absent, the matrix reports those cells as
SKIP rather than PASS, so the Mojo figures are not reproducible in a
toolchain-less CI.
End-to-end matrix: compile every output and verify it matches Python's stdout.
uv run python scripts/run_matrix.py examples/algorithms
uv run python scripts/transpile_matrix.py examples/samples/Python rust # transpile onlyFailure taxonomy: classify every verify-gate failure over a corpus into
parse · unresolved-symbol · unfilled-UnknownT-hole · type-inference-miss · target-compile-error · output-mismatch · structural-divergence · timeout
and roll it up per (source, target) pair (CSV + markdown):
uv run python scripts/failure_taxonomy.py examples/samples --targets rust,mojo --csv out.csvsrc/transpilers/verify/behavioral.py additionally classifies semantic divergences
in verified output, for example floored versus truncated integer division.
For whole-repository migrations such as EnergyPlus:
- Plan the batch:
transpile-build-config <source_dir> -o config.json(use--exclude,--min-lines/--max-lines,--tier,--sort). - Transpile:
transpile-llm config.json(add--repairfor the compile-driven repair loop,--dry-runor--statusto inspect first,--resolveto retry entries marked done but missing their.mojo).--endpoint URL --model NAMEuses a local server instead of OpenRouter;--postpassapplies the deterministic fixes to each draft;--samples N --verify-cmd CMDkeeps the first of up to N drafts that a verifier accepts.--domain-promptsadds GoogleTest or Eigen guidance when those sources are detected. See Using the local fine-tuned model. - Stateless one-shot runner:
transpile-statelessproduces a Python and a Mojo port per file in one completion. Backends:claude,lmstudio(load-balanced across endpoints) andopenrouter(retries with backoff, and model failover viaOPENROUTER_FAILOVER).--chunkcompresses or splits oversize files and stitches the result. Seestateless transpilation/0_inference.mdto serve the fine-tuned model locally. - Track progress:
scripts/migration_status.pycompares the upstream repository with its port (lines of code, file/function counts, migration-map status, test results), optionally across two git revisions; see docs/migration_status.md.tools/migraph/renders the migration as an interactive graph you can scrub through git history. - Benchmark models:
benchmarks/holdstranspilation-bench, a 40-task C++/Python → Mojo suite (benchmarks/README.md), andbenchmarks/epmojo_eval/, which scores drafts by whether they are bit-for-bit identical to the verbatim C++ (benchmarks/epmojo_eval/README.md).
The repo ships the LoRA adapter (data/sft/cpp_mojo/adapter_15b_v2, on
Qwen/Qwen2.5-Coder-1.5B-Instruct), not the base weights. To serve it, merge the adapter
into the base and convert it to GGUF for Ollama, llama.cpp or LM Studio
(stateless transpilation/0_inference.md has the recipe). Then:
uv run transpile-llm config.json --endpoint http://localhost:11434/v1 \
--model <the name you gave the model> --postpassThings worth knowing, all measured on this adapter:
- It answers with one fenced
mojoblock, whichtranspile-llmaccepts when there are no<<<FILE>>>markers (exactly one closedmojofence; several fences are refused as ambiguous). - Leave
--domain-promptsoff for it. With the EnergyPlusMojo guidance the model often copies the guidance's example function instead of translating the routine. - Raw drafts are rarely bit-exact; the post-pass is what makes them usable. It is
transpilers.repair.mojo_postpass, also available astranspile-postpass FILE, and every rule can be switched off (--no-syntax,--no-structs,--no-arrays,--no-mut,--no-libm,--no-fma):- C++ and pre-1.0 Mojo habits become Mojo 1.0 (
//comments,fn,let,alias,&&,const, quoted imports); - a field-less struct with one method is lifted to a function named after the C++ routine;
Array1Dparameters becomeListonly when every use is the canonicalfor i in range(a.l(), a.u() + 1)loop with the variable used solely asx[i]; anything else is reported, never guessed;std.mathcalls (exp,pow,log, ...) are routed to glibc,pow(x, 2)becomesnc_mul(x, x), ObjexxFCL'spow_Nandmax/minfollow the upstream spelling (pass--sourcethe C++ text);mutis added toList-like parameters the body index-assigns;- products that feed an add or subtract are wrapped in
nc_mul, including products bound to variables, because Mojo fusesa*b + cinto an FMA that the oracle build (-ffp-contract=off) does not. Anything it cannot fix (C++ braces, unknown types, no@exportentry) is listed in the report.
- C++ and pre-1.0 Mojo habits become Mojo 1.0 (
- Best-of-n when a verifier exists.
--samples N --temperature 0.8 --verify-cmd CMDdraws up to N drafts per file and keeps the first thatCMDaccepts with exit status 0 ({target}and{source}are substituted).benchmarks/epmojo_eval/verify_one.pyis such a verifier for routines extracted from EnergyPlusMojo: it accepts a draft only if it is bit-exact.
Measured with the harness in benchmarks/epmojo_eval/ (3,000 edge-case inputs per routine, bit for bit
against the verbatim C++, no guidance, temperature 0 unless stated):
| Set | Raw output | With the post-pass |
|---|---|---|
| Development, 40 routines (31 unique) | 8 exact | 39 exact |
| Held-out, 21 unique routines | 1 exact | 16 exact (19 after the struct rule, which was written after seeing that pattern) |
| The 3 routines greedy decoding fails, best-of-8 with the oracle | 2 recovered |
Read these numbers narrowly: the routines are short, stateless, numeric Real64 → Real64 functions (52
unique in total); the development set was used while writing the rules, so the held-out 16 of 21 is the fair
figure; routines with arrays, state, output parameters or callees were not measured; and the post-pass is
meant for model drafts, not for rewriting existing kernels. Best-of-n needs an oracle for the routine.
A toolchain-free data and fine-tune stack bootstraps the C++/Python → Mojo translator adapter. It runs on NVIDIA GPUs with a CUDA build of PyTorch.
- Run or retrain the adapter: RUN.md covers setup and the LLaMA-Factory
recipe.
scripts/sft/train_3b_cuda_local.shis the local two-phase 3B run for an NVIDIA GPU;scripts/sft/cloud_train.shandtools/cloud/are for a rented NVIDIA box (scripts/sft/cloud_bundle.md). - Shipped adapter:
data/sft/cpp_mojo/adapter_15b_v2(LoRA onQwen/Qwen2.5-Coder-1.5B-Instruct), plus the training pairs indata/sft/. - Algorithm dataset generator:
scripts/sft/build_algorithm_pairs.pyemits verified C/C++/Python → Mojo (plus Rust/C bonus) pairs intodata/sft/algorithms/from the seed corpus inexamples/, with a rejects channel for unmappable constructs. - Record/replay shims:
scripts/sft/record_replay.pywith bool/int normalisation so captured fixtures replay deterministically. - Psychrometric shims:
scripts/sft/psychro_shims.pyprovides a realPsyPsatFnTempimplementation (replacing the earlierpvstarstub) for EnergyPlus-flavoured ports. - God-object slicing:
scripts/sft/god_object_slice.pyemits per-module sub-state Mojo structs from a god-object slice. - Data flywheel:
just flywheelpipes verified repairs back intostdlib_maps/and the SFT corpus (docs/data_flywheel.md).
| Topic | Where |
|---|---|
| Running and retraining the Mojo model | RUN.md, docs/fine_tuning_guide.md, docs/finetune_qwen7b.md |
| Cloud/NVIDIA training bundle | tools/cloud/README.md, scripts/sft/cloud_bundle.md |
| Migration status report | docs/migration_status.md |
| Data flywheel | docs/data_flywheel.md |
| LLVM IR grounding and IR-pair datasets | docs/llvm_ir_pipeline.md, docs/ir_pair_pipeline.md |
| Multi-LLM pipeline | docs/multi_llm_pipeline.md |
| Migrating Topologic | docs/topologic_migration.md |
| Strategy and research | docs/north_star_strategy.md, docs/transpilation-methods-research.md, docs/related_projects.md |
| Benchmarks | benchmarks/README.md |
| Bit-exactness evaluation harness | benchmarks/epmojo_eval/README.md |
| Post-pass rules | src/transpilers/repair/mojo_postpass.py (module docstring) |
src/transpilers/ frontends · ir (hir/mir/lir) · passes · backends · llm · verify · repair · rag · graph · cli
scripts/ dataset builders · sft/ (fine-tune + eval) · rag/ · migration_status.py
data/ datasets · the shipped Mojo LoRA adapter · codebase RAG index
examples/ verified corpus (algorithms/ + per-language samples)
tests/ engine test suite (Mojo-gated and GPU/ML cases skip automatically)
benchmarks/ transpilation-bench (40-task C++/Python → Mojo) · epmojo_eval/ (bit-exactness harness)
tools/ migraph (migration dashboard) · cloud (portable NVIDIA training bundle)
stateless transpilation/ workspace for the one-shot runner (manifest, out/) and its review/verify scripts
deploy/ Dockerfile for the HTTP API
just --list # every recipe
just test # pytest
just lint # ruff check src tests
just fmt # ruff format src tests
just check # lint + test (what CI should run)The pytest suite needs no target toolchain; Mojo-gated and ML/torch tests skip automatically when their dependencies are absent. Core tooling: uv (Python dependencies), just (task runner), direnv (shell activation) and the Hugging Face stack (Transformers / PEFT / LLaMA-Factory) for fine-tuning.
Tracked in GitHub Issues.
Highlights: dynamic array growth on Fortran (done on C), OptionT/RefT in the type
lattice, and statement-expression sequencing in HIR (to lift postfix i++/i-- out of
expression contexts).
Copyright (c) 2026 Bartlomiej Tokarzewski. All Rights Reserved. No copying, distribution or modification without prior written permission; see LICENSE.