Metadata-Version: 2.4
Name: barepy
Version: 0.1.0
Summary: Python as an elegant coding bridge to bare-metal microcontroller C. A hybrid optimizing transpiler with source-mapped debugging.
Author: barepy
Keywords: transpiler,compiler,microcontroller,bare-metal,embedded,firmware,arm,cortex-m,stm32,codegen,source-map
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Compilers
Classifier: Topic :: Software Development :: Embedded Systems
Classifier: Topic :: Software Development :: Code Generators
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: C
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Provides-Extra: studio
Requires-Dist: textual>=0.60; extra == "studio"

<p align="center">
  <img src="https://img.shields.io/badge/barepy-Python%20%E2%86%92%20bare--metal%20C-4b8bbe?style=for-the-badge" alt="barepy">
</p>

<h1 align="center">barepy</h1>

<p align="center">
  <strong>Python as an elegant coding bridge to bare-metal microcontroller C.</strong><br>
  A hybrid optimizing transpiler with source-mapped debugging.
</p>

<p align="center">
  <img src="https://img.shields.io/badge/version-0.1.0-blue?style=flat-square" alt="version">
  <img src="https://img.shields.io/badge/python-3.11%2B-4b8bbe?style=flat-square&logo=python&logoColor=white" alt="python">
  <img src="https://img.shields.io/badge/target-ARM%20Cortex--M%20%C2%B7%20native%20sim-lightgrey?style=flat-square" alt="targets">
  <img src="https://img.shields.io/badge/output-readable%20C-brightgreen?style=flat-square" alt="output">
  <img src="https://img.shields.io/badge/errors-source--mapped-orange?style=flat-square" alt="source-mapped">
  <img src="https://img.shields.io/badge/status-M0%20vertical%20slice-yellow?style=flat-square" alt="status">
</p>

---

Write firmware in a clean Python subset. barepy transpiles it to **readable C**,
compiles it for your board (or a native simulator), and — crucially — maps
every compiler and runtime error *back to the exact Python line and column*.

```python
import barepy as bp
from barepy.boards import STM32F4

bp.init(STM32F4, clock="168MHz")

led = bp.Pin(13, bp.OUTPUT)
btn = bp.Pin(0, bp.INPUT_PULLUP)

@bp.setup
def setup():
    led.off()

@bp.loop
def loop():
    led.toggle() if btn.pressed() else led.off()
    bp.delay(200)
```

```bash
barepy sim examples/blink.py          # transpile -> C -> compile -> simulate
```

---

## ✨ Highlights

| | |
|---|---|
| 🐍 **Python in, C out** | A static, zero-overhead subset transpiles to small, fast, *readable* C — not an on-chip interpreter. |
| 🎯 **Source-mapped everything** | Compiler, board, and runtime errors surface against **your Python** with a caret — never the generated C. |
| ⚖️ **Hybrid tiers** | Most code is zero-cost static. Genuinely dynamic code opts into a small boxed-value runtime via `@bp.dynamic`. Pay only for what you use. |
| 🔬 **Real optimizer** | Fix-point const-fold/prop, algebraic identities, branch-fold, DCE — every rewrite auditable with `optimization-table`. |
| 🛡️ **Panic-free** | Every pipeline stage runs inside a fault barrier; a 1,500-case fuzzer asserts zero escapes. Runtime faults degrade gracefully. |
| 🔥 **Hot-code analysis** | Ranks patterns dangerous on an MCU: blocking in an ISR, `while True` with no exit, div-by-zero, 32-bit overflow, busy loops. |
| 🔌 **Everything is a plugin** | Boards, backends, passes, tiers, CLI/TUI commands, lints, flashers — all extension points on a plugin fabric. |
| 🖥️ **Textual studio** | Cockpit TUI around your IDE: diagnostics (incl. C-level errors mapped to Python) · cost · optimizations · hot-code · generated C · sim · debug-C memory profiler · flash · test suite · live activity monitor. |

## Why it exists

A transpiler, not an on-chip interpreter — the output is native C, so it is
fast and small. It is **hybrid**: most code is a static, zero-overhead subset;
genuinely dynamic code (config parsing, protocol work) can opt into a small
boxed-value runtime with `@bp.dynamic`. You pay only for what you use, and the
build report tells you the cost.

## Install

```bash
pip install barepy                 # core (from PyPI)
pip install 'barepy[studio]'       # + Textual TUI

# from source
pip install -e .
pip install -e '.[studio]'
```

Requires Python **3.11+** and a C compiler (`cc` for the native sim,
`arm-none-eabi-gcc` for ARM builds).

## Commands

| command | does |
|---|---|
| `barepy transpile f.py [--show] [--no-opt]` | emit C + `.map.json`, no compile |
| `barepy build f.py [--target sim\|arm]` | transpile + optimize + compile |
| `barepy sim f.py [--ticks N]` | build native + run the simulator |
| `barepy debug f.py` | build; render any error with a caret on Python source |
| `barepy optimization-table f.py` | table of every optimization applied |
| `barepy hot-code f.py [--strict]` | ranked report of fragile / dangerous code |
| `barepy board list` / `board show <name\|file>` | inspect boards |
| `barepy plugins list` | show discovered plugins + extension points |
| `barepy studio [f.py]` | launch the Textual TUI |

## The compiler

The pipeline is a real optimizing compiler, not a translator:

* **Type inference (M1)** — flow-forward inference over the static tier assigns a
  concrete C type to every local (`int32_t x`, `bool`, …) and reports type
  errors against Python spans.
* **Optimizer (M2)** — fix-point passes: constant folding, constant
  propagation, algebraic identities (`x*1`, `x+0`, `x*0`), branch folding
  (`if True`), and dead-code elimination. `barepy optimization-table` shows
  each rewrite:

  ```
  line  pass         kind      before        -> after
    14  const_fold   arith     (2 + 12)      -> 14
    15  algebraic    x*1       (x * 1)       -> x
    17  branch_fold  if-true   if <true>     -> <then branch>
    19  const_fold   arith     (14 + 100)    -> 114
  ```

* **SSA layer (M2+): inlining · monomorphization · copy-prop** — a whole-program
  pass inlines single-return `@bp.static` helpers at their call sites (binding
  parameters to the actual argument trees), **monomorphizes** un-annotated
  helpers per call-site type signature, and propagates single-definition
  (trivially SSA) copies into their uses. A fully-inlined helper emits no C
  function at all — it costs nothing:

  ```
  line  pass          kind        before      -> after
    24  monomorphize  specialize  dbl(...)    -> dbl<int>
    24  inline        expand      dbl(...)    -> ((21 * 2) + 1)
    25  monomorphize  specialize  dbl(...)    -> dbl<bool>
    26  copy_prop     propagate   a           -> ((21 * 2) + 1)
    26  const_fold    arith       (42 + 1)    -> 43
  ```

* **Hybrid dynamic tier (M4)** — `@bp.dynamic` functions transpile to C over a
  boxed-value runtime (`Obj*`, bump arena). dict / list / `for` / unpack /
  `int()` / `.split()` / `.get()` all work. The static caller unboxes scalar
  results and resets the arena at the boundary — you pay only for what you use.
  Generated dynamic C is **portable C11** — container literals are hoisted into
  ordinary statements, so nothing depends on GCC/Clang statement-expressions.

* **Escape analysis → region inference → GC (M4+)** — escape analysis classifies
  every dynamic allocation into a region (`arena` for non-escaping, `gc` for
  escaping/cyclic), surfaced in `optimization-table`. Opt into the
  cycle-collecting heap with `bp.init(..., gc="mark-sweep")`: allocations move
  onto a precise mark-sweep collector, generated code roots its live locals, and
  `bp.gc_collect()` reclaims unreachable objects — **including reference cycles**
  the bump arena never could:

  ```python
  @bp.dynamic
  def churn(n: int) -> int:
      i = 0
      while i < n:
          d = {}
          d[b"self"] = d      # a reference cycle
          bp.gc_collect()     # reclaimed each iteration; live memory stays flat
          i = i + 1
      return bp.gc_live()     # bounded, no matter how large n is
  ```

## Hot-code analysis

`barepy hot-code` ranks patterns that are dangerous on a microcontroller:
blocking or allocating in an ISR, `while True` with no exit, division by zero,
32-bit overflow, `ValueError`-prone unpacking, busy loops, and more.

```
🟥 CRIT  scary.py:22  [isr-blocking] in on_button()
        blocking bp.delay() inside ISR 'on_button' stalls the whole system
        fix: ISRs must return fast; set a flag instead
```

## Fault tolerance (aerospace-grade)

The transpiler is **panic-free**: every pipeline stage runs inside a fault
barrier, so any internal bug becomes a contained ICE diagnostic, never a crash.
A 1,500-case fuzzer of malformed / adversarial / binary input asserts zero
escapes (`tests/test_robustness.py`). At **runtime**, dynamic-tier faults
(bad `int()`, unpack mismatch, KeyError) print a source-mapped error and the
program degrades gracefully instead of hard-faulting.

## Source-mapped errors

barepy emits `#line` directives into the generated C and keeps a span sidecar,
so a mistake surfaces against *your* Python, not the generated C:

```
$ barepy debug examples/bad_blink.py

examples/bad_blink.py:18:5  [static]
        btn.toggle()
        ^^^^^^^^^^^^
error: 'btn.toggle()' needs an OUTPUT pin, but 'btn' is INPUT_PULLUP
  hint: declare btn = bp.Pin(0, bp.OUTPUT)
```

Board-level constraints map back too:

```
$ barepy build examples/blink.py --board examples/custom_board.board.toml

examples/blink.py:4:1  [board]
    bp.init(STM32F4, clock="168MHz")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: clock 168MHz exceeds MyTinyBoard max 48MHz
```

## Define your own board

Three ways, all producing the same `Board` object.

**1. Data file** (`*.board.toml`) — shareable, no code:

```toml
name = "MyTinyBoard"
[mcu]
core = "cortex-m0"
flash = { base = 0x08000000, size = "64KB" }
ram   = { base = 0x20000000, size = "8KB" }
[clock]
max_hz = 48_000_000
[peripherals]
GPIOA = 0x48000000
[[pins]]
id = 13
port = "GPIOA"
bit = 5
```

```bash
barepy build blink.py --board my.board.toml
```

**2. Python class** — full control (see `barepy/boards/__init__.py`).

**3. Plugin package** — ship a board (and flasher, backend, passes…) on PyPI.

## Hardware bring-up (real clock / RCC / SysTick)

The ARM target no longer assumes a hand-configured clock. Each build emits a
`barepy_board.h` derived from the Board definition, and the runtime runs the
real bring-up sequence:

* **PLL / RCC** — from the board clock tree (`HSE`, PLL `m/n/p`) barepy computes
  the core frequency (e.g. STM32F4: `8 MHz × 336 / (8 × 2) = 168 MHz`), sets the
  flash wait states, programs the PLL, and switches `SYSCLK` over.
* **GPIO clocks** — only the ports your pins actually use are enabled, via an
  `RCC AHB1ENR` mask computed at build time.
* **SysTick timebase** — `bp.delay(ms)` waits on a real millisecond SysTick
  counter (`WFI` between ticks), not a tuned busy-loop.

```c
// generated barepy_board.h  (STM32F4 @ clock="168MHz")
#define BP_CORE_HZ        168000000u
#define BP_RCC_BASE       0x40023800u
#define BP_FLASH_WS       5u
#define BP_GPIO_ENR_MASK  0x00000005u   // GPIOA + GPIOC (the pins in use)
#define BP_HAS_PLL        1
#define BP_PLL_M 8u  #define BP_PLL_N 336u  #define BP_PLL_P 2u
```

## Plugins — everything is an extension point

A plugin is a package exposing a `Plugin` through the `barepy.plugins`
entry-point group. It can register boards, backends, MIR passes, runtime tiers,
CLI/TUI commands, lints, and flashers. See `examples/plugins/barepy_tinyavr/`.

```python
from barepy.ext import Plugin, hookimpl

class plugin(Plugin):
    @hookimpl
    def register_boards(self, reg):  reg.add(MyBoard)
    @hookimpl
    def register_flasher(self, reg): reg.add("avr", avrdude_flash)
```

## Architecture

```
Python source
  -> frontend      tokenize/AST -> IR (+ spans)   barepy/frontend.py
  -> analysis      effect/tier/pin checks         barepy/frontend.py:analyze
  -> board         resolve + validate             barepy/hal.py
  -> codegen       IR -> C + #line + source map   barepy/codegen.py
  -> driver        write artifacts, compile        barepy/driver.py
  -> runtime       T0 static core (native | arm)  barepy/runtime/
```

The IR carries a `Span` on every node — the **source-map spine** that lets any
failure at any layer point back to Python. Later milestones lower the IR
through SSA/MIR (optimizer) and LIR (boxing, coroutines, ISR lowering) without
changing this pipeline shape.

## Known limitations

barepy `0.1.0` is the **M0 vertical slice** — honest about its edges:

* **Inlining is limited to single-return `@bp.static` helpers.** General
  multi-statement static functions with parameters are inlined only in that
  expression form; a full MIR call-lowering is future work.
* **The GC is a stop-the-world mark-sweep with a fixed object pool.** It handles
  cycles correctly, but there is no generational / incremental collection yet,
  and collection runs only at safe points (never mid-expression).
* **PLL / RCC bring-up targets the STM32F4 register layout.** Other families
  compile against the same generated `barepy_board.h`, but non-F4 clock trees
  may need a board-specific recipe (ship one via a plugin backend).
* **Supported Python is a subset.** Classes, generators, exceptions, and
  arbitrary imports are out of scope by design — see the frontend for the exact
  grammar.

## Status & roadmap

- [x] frontend → IR with spans (for, bool ops, bytes, dict/list, subscript, unpack)
- [x] static tier → C, `#line` + `.map.json`
- [x] effect/pin/board diagnostics with caret rendering
- [x] native simulator + STM32 ARM runtime
- [x] board system: Python class, TOML, validation
- [x] plugin fabric (boards/backends/passes/tiers/commands/lints/flashers)
- [x] Textual studio (diagnostics · C · cost · optimizations · hot-code · sim ·
  debug-C memory profiler · flash · test suite · live activity monitor)
- [x] **M1** type inference (flow-forward, C-type assignment, type errors)
- [x] **M2** optimizer (const-fold/prop, algebraic, branch-fold, DCE) + `optimization-table`
- [x] **M4** hybrid dynamic tier (boxed `Obj*` runtime, bump arena, boundary marshalling)
- [x] hot-code risk analysis (`hot-code`)
- [x] aerospace-grade fault barrier (panic-free, fuzz-verified) + mapped runtime faults
- [x] **M2+** SSA layer: inlining, monomorphization, copy-propagation
- [x] **M4+** escape analysis → region inference → cycle-collecting mark-sweep GC
- [x] portable dynamic codegen (C11, no statement-expressions)
- [x] real ARM clock/RCC bring-up + SysTick `bp.delay` (generated `barepy_board.h`)
- [ ] **M2++** full MIR call-lowering (general static functions), CSE/GVN
- [ ] **M4++** generational / incremental GC
- [ ] **M5** async state machines · **M6** DMA ownership · **M8** LLVM backend (RISC-V/ESP)

`M0` is the working vertical slice this repo ships. Each later milestone is
independently useful.
