{"id":50533162,"url":"https://github.com/daedalus/lp2","last_synced_at":"2026-06-03T15:30:26.646Z","repository":{"id":360007625,"uuid":"1248047159","full_name":"daedalus/lp2","owner":"daedalus","description":"Bidirectional Lean4↔Python transpiler (Python 3.11+ / Lean 4)","archived":false,"fork":false,"pushed_at":"2026-05-24T16:13:03.000Z","size":83,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-24T16:20:40.894Z","etag":null,"topics":["codegen","formal-verification","lean","lean4","python","transpiler"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daedalus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-24T05:43:57.000Z","updated_at":"2026-05-24T16:13:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/daedalus/lp2","commit_stats":null,"previous_names":["daedalus/lp2"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/daedalus/lp2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2Flp2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2Flp2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2Flp2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2Flp2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daedalus","download_url":"https://codeload.github.com/daedalus/lp2/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2Flp2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33872297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["codegen","formal-verification","lean","lean4","python","transpiler"],"created_at":"2026-06-03T15:30:24.857Z","updated_at":"2026-06-03T15:30:26.640Z","avatar_url":"https://github.com/daedalus.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LP² (lp2) — Lean ↔ Python, squared\n\n**LP²** = LP² = LP × LP = **L**ean **↔ P**ython, squared for bidirectional. Translates in both directions: Python→Lean and Lean→Python.\n\n[![Python](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/master/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n\nTranslate Python functions with type hints into Lean 4 definitions, and Lean 4 definitions into Python functions.\n\n## Install\n\n```bash\npip install lp2\n```\n\n## Usage\n\n### CLI\n\n```bash\n# Python → Lean4\nlp2 py2lean my_file.py\nlp2 py2lean --stdin \u003c my_file.py\n\n# Lean4 → Python\nlp2 lean2py my_file.lean\nlp2 lean2py --stdin \u003c my_file.lean\n```\n\n### Python API\n\n```python\nfrom lp2 import py_to_lean, lean_to_py, convert_str, convert_file\n\nlean_code = py_to_lean(\"def add(x: int, y: int) -\u003e int:\\n    return x + y\\n\")\npy_code = lean_to_py(\"def add (x y : Int) : Int := x + y\\n\")\n\n# Convenience\nresult = convert_str(source, \"py2lean\")\nresult = convert_file(\"input.py\", \"py2lean\")\n```\n\n### Examples\n\nFor more comprehensive examples, see the `examples/` directory:\n- `demo_factorial_roundtrip.py` / `factorial.py`: Recursive factorial (Python → Lean → Python)\n- `demo_fibonacci_roundtrip.py` / `fibonacci.py`: Recursive Fibonacci\n- `demo_bubble_sort_roundtrip.py` / `bubble_sort.py`: Bubble sort with nested while loops\n- `demo_quicksort_roundtrip.py` / `quicksort.py`: Quicksort with for-loop + `if/elif/else`\n- `demo_fermat_roundtrip.py` / `fermat.py`: Fermat factorization with while loops\n- `demo_theorem_transpilation.py`: Transpile Lean theorems to Python as Boolean-valued functions\n- `factorial_algorithms.py` / `factorial_algorithms_test.py` / `factorial_algorithms.lean`: Multiple factorial algorithms (iterative, product tree, prime swing) with `@no_transpile` for unsupported features\n- `simple_while.py`: Simple while loop example\n\n**Python → Lean4:**\n\n```python\ndef fib(n: int) -\u003e int:\n    if n \u003c= 1:\n        return n\n    return fib(n - 1) + fib(n - 2)\n```\n\n```lean4\ndef fib (n : Int) : Int :=\n  if n ≤ 1 then n else fib (n - 1) + fib (n - 2)\n```\n\n**Lean4 → Python:**\n\n```lean4\ndef isZero (n : Int) : Bool := match n with\n  | 0 =\u003e true\n  | _ =\u003e false\n```\n\n```python\ndef isZero(n: int) -\u003e bool:\n    match n:\n        case 0:\n            return True\n        case _:\n            return False\n```\n\n## Caveats \u0026 Limitations\n\n### `no_transpile` — skipping unsupported Python features\n\nWhen the transpiler encounters a Python feature it cannot translate (e.g.,\ngenerators, comprehensions, bytearray, f-strings), it raises an error by default.\nYou can mark any function or statement with `@no_transpile` to keep the original\nPython source in the output as a Lean comment:\n\n```python\n@no_transpile\ndef sieve(n: int) -\u003e list[int]:\n    bs = bytearray(b\"\\x01\") * (n + 1)\n    bs[0:2] = b\"\\x00\\x00\"\n    return [i for i in range(2, n + 1) if bs[i]]\n```\n\nGenerates:\n\n```lean4\n-- no_transpile (@sieve)\n--   def sieve(n: int) -\u003e list[int]:\n--       bs = bytearray(b\"\\x01\") * (n + 1)\n--       ...\n```\n\nFor individual lines, use a `# no_transpile` comment:\n\n```python\nsys.set_int_max_str_digits(100000)  # no_transpile\n```\n\nBoth annotations are **parser-only** and never executed at Python runtime.\n\n### Stdlib source-following for imported functions\n\nWhen the transpiler sees `import math`, it imports the module at transpile time.\nIf it encounters `math.factorial(x)`, it attempts to retrieve the function's\nsource via `inspect.getsource` and transpile it automatically. For C-extension\nfunctions (like `math.factorial`), where no Python source exists, it falls back\nto transpiling a known Python implementation:\n\n```lean4\npartial def factorial (n : Nat) : Nat :=\n  let result := 1; let rec loop (i : Nat) (result : Nat) : Nat :=\n    if i \u003c n + 1 then loop (i + 1) (result * i) else result; loop 2 result\n```\n\nThis avoids maintaining a brittle mapping from Python stdlib names to Lean\nlibrary paths. Pure-Python functions from `functools`, `collections`, etc. can\nbe auto-transpiled in the same way. Only functions where source retrieval fails\n_and_ no fallback exists require `@no_transpile`.\n\n### Termination on `Int` recursion\n\nLean's termination checker cannot prove well-foundedness for recursion over `Int`.\nThe transpiler auto-detects self-recursive calls and emits `partial def` for such\nfunctions. This sidesteps termination checking but means Lean cannot verify the\nfunction always halts.\n\n```python\ndef factorial(n: int) -\u003e int:\n    if n \u003c= 1:\n        return 1\n    return n * factorial(n - 1)\n```\n\n```lean4\npartial def factorial (n : Int) : Int :=\n  if n ≤ 1 then 1 else n * factorial (n - 1)\n```\n\n### `+` on lists (Python concatenation vs. Lean element-wise addition)\n\nPython's `+` on lists means **concatenation**. Lean 4.30.0-rc2 defines `Add (List α)`\nas element-wise addition (`zipWith`). The transpiler emits a `local instance`\nthat overrides `Add (List Int)` to use `List.append`, so `+` on `List Int`\nconcatenates as expected. This only covers `List Int` — other list types (e.g.,\n`List Float`, `List String`) remain element-wise.\n\n### List mutation is silently dropped\n\nIn-place list mutation (`xs[i] = x`, `xs.append(x)`) is not supported. The\ntranspiler silently drops these statements. Use immutable patterns instead:\n\n```python\n# Instead of:\n#   xs[i] = x\n#   xs.append(x)\n\n# Use:\n#   xs = xs[:i] + [x] + xs[i+1:]\n#   xs = xs + [x]\n```\n\n### Round-trip is lossy\n\nTranspiling Python → Lean → Python recovers syntactically valid Python code but\nloses some information:\n\n| Lost detail | Example | Round-trip result |\n|---|---|---|\n| `//` (int division) | `x // y` | `x / y` |\n| `tuple` type | `(1, 2)` → `Prod.mk` | parsed as generic call |\n| Return type annotations | `def f() -\u003e int:` | `def f():` |\n| `for` / `while` loops | `let rec` / `match` | parser skips (not yet supported) |\n| Nested `let` chains | `let a := ...; let b := ...` | may produce unusual `def` wrappers |\n\n### No `Std` or `mathlib4` available\n\nThe transpiler targets Lean 4.30.0-rc2 without `Std` or `mathlib4`.\nFeatures like `omega`, `List.range`, `HashMap`, `Nat.factorial`, and `Nat.popCount`\nare unavailable from the standard library. The transpiler compensates with:\n\n- A built-in `partial def get` helper for list indexing (bypassing `Fin` proof\n  requirements)\n- A built-in `Nat.popCount` shim (bit-count via recursion)\n- Stdlib source-following (see above) to synthesize implementations from\n  equivalent Python code\n- `@no_transpile` as the escape hatch for anything that cannot be synthesized\n\n### Unsupported Python features\n\nThese Python constructs have no Lean equivalent or are not yet implemented:\n\n- Generators / `yield`\n- `async` / `await`\n- Decorators (except the special `@no_transpile`)\n- Exception handling (`try`/`except`/`finally`)\n- `with` statements (context managers)\n- List/dict/set comprehensions\n- `*args` / `**kwargs` (variadic functions)\n- Classes with methods (only simple data structures supported)\n- Augmented assignment on lists (`xs += [x]`)\n- F-strings, slice assignment, `dict`/`set` mutation\n\n## Development\n\n```bash\npip install -e \".[test]\"\npytest\nruff format src/ tests/\nprospector --with-tool ruff --with-tool mypy src/\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaedalus%2Flp2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaedalus%2Flp2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaedalus%2Flp2/lists"}