SkillAgentSearch skills...

PythoC

PythoC is a Python DSL compiler that compiles statically-typed Python to LLVM IR, providing C-equivalent runtime capabilities with Python syntax and compile-time metaprogramming.

Install / Use

/learn @1flei/PythoC
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

PythoC: Write C in Python

PythoC is a Python DSL compiler that compiles statically-typed Python to LLVM IR, providing C-equivalent runtime capabilities with Python syntax and compile-time metaprogramming.

Design Philosophy

Core principle: C-level runtime + Python-powered compile-time

  1. C-compatible runtime: Compiled code maps directly to native machine code with C-level control and performance
    • Full access to low-level operations (pointers, manual memory, inline assembly)
    • C calling conventions for seamless interoperability
    • No runtime overhead beyond what C would have
  2. Compile-time = Python: Full Python power for metaprogramming, generics, and code generation
  3. Zero-cost abstractions: Python-level abstractions compile away completely
  4. Explicit typing: All types must be annotated (like C, unlike Python)
  5. Explicit control flow: No implicit control flow (no exceptions, no RAII, no destructors)
    • Structs are plain data (no methods, no constructors)
    • Manual resource management like C
  6. Optional safety features: Linear types and refinement types provide memory safety guarantees without introducing hidden control flow
    • Prevent memory leaks, use-after-free, null pointer dereference, array out-of-bounds
    • Completely optional - use only when needed
    • No extra runtime overhead
  7. Convenient Python-C interoperability:
    • Python can call PythoC compiled functions at runtime (via ctypes/cffi)
    • PythoC can invoke Python code at compile-time for metaprogramming

Quick Start

Installation

pip install pythoc

Hello World

from pythoc import compile, i32

@compile
def add(x: i32, y: i32) -> i32:
    return x + y

# Can compile to native code
@compile
def main() -> i32:
    return add(10, 20)

# Or call the compiled dynamic library from Python directly
result = main()

Run Tests

# Run all tests
python test/run_all_tests.py

# Run specific test suites
python test/run_integration_tests.py
python test/run_examples.py

Example: Binary Tree Benchmark

This example demonstrates PythoC's direct mapping to C - compare with test/example/base_binary_tree_test.c:

from __future__ import annotations
from pythoc import i32, ptr, compile, nullptr, seq, sizeof
from pythoc.libc.stdlib import malloc, free
from pythoc.libc.stdio import printf

# C: typedef struct tn { struct tn* left; struct tn* right; } treeNode;
@compile
class TreeNode:
    left: ptr[TreeNode]
    right: ptr[TreeNode]

# C: treeNode* NewTreeNode(treeNode* left, treeNode* right)
@compile
def NewTreeNode(left: ptr[TreeNode], right: ptr[TreeNode]) -> ptr[TreeNode]:
    new: ptr[TreeNode] = ptr[TreeNode](malloc(sizeof(TreeNode)))
    new.left = left
    new.right = right
    return new

# C: long ItemCheck(treeNode* tree)
@compile
def ItemCheck(tree: ptr[TreeNode]) -> i32:
    if tree.left == nullptr:
        return 1
    else:
        return 1 + ItemCheck(tree.left) + ItemCheck(tree.right)

# C: treeNode* BottomUpTree(unsigned depth)
@compile
def BottomUpTree(depth: i32) -> ptr[TreeNode]:
    if depth > 0:
        return NewTreeNode(BottomUpTree(depth - 1), BottomUpTree(depth - 1))
    else:
        return NewTreeNode(nullptr, nullptr)

# C: void DeleteTree(treeNode* tree)
@compile
def DeleteTree(tree: ptr[TreeNode]):
    if tree.left != nullptr:
        DeleteTree(tree.left)
        DeleteTree(tree.right)
    free(tree)

C Parity: Full C Capabilities

Supported Features

PythoC provides complete C runtime capabilities:

Primitive types:

  • Integers: i8, i16, i32, i64, u8, u16, u32, u64
  • Floats: f16, f32, f64, bf16, f128
  • Boolean: bool

Composite types:

  • Pointers: ptr[T]
  • Arrays: array[T, N] or array[T, N, M, ...] for multi-dimensional
  • Structs: @compile class, @struct class, struct[x: i32, y: i32] (named) or struct[i32, i32] (unnamed)
  • Unions: @union class or union[T1, T2, ...]
  • Enums: @enum class
  • Function pointers: func[[arg_types], return_type]

Control flow:

  • if/else, while, for loops
  • break, continue, return
  • Pattern matching: match/case (enhanced switch)
  • Scoped goto and labels for low-level control (similar to labeled continue/break, limited but safer)

Operations:

  • Arithmetic: +, -, *, /, %, //
  • Comparison: ==, !=, <, >, <=, >=
  • Logical: and, or, not
  • Bitwise: &, |, ^, ~, <<, >>
  • Pointer arithmetic and dereferencing

C Standard Library:

from pythoc.libc.stdio import printf, scanf
from pythoc.libc.stdlib import malloc, free, atoi
from pythoc.libc.string import memcpy, strlen
from pythoc.libc.math import sin, cos, pow

Not Yet Supported

Features deliberately excluded or pending:

  • Fall-through switch: Use match/case with explicit branches
  • Global variable initialization: Workaround with init functions and effect system
  • Variable-length arrays (VLA): Use fixed-size arrays or malloc
  • Flexible array members: Use separate size tracking

PythoC Language Core

Beyond C parity, PythoC adds modern type system features (all optional, minimal support):

Algebraic Data Types (ADT) and Pattern Matching

Provide Rust-like enums with payload for type-safe tagged unions:

from pythoc import enum, compile, i32

@enum
class Result:
    Ok: i32
    Err: i32

@compile
def handle_result(r: Result) -> i32:
    match r:
        case (Result.Ok, value):
            return value
        case (Result.Err, code):
            return -code

Linear Types (Optional)

Prevent use-after-free and resource leaks without RAII or destructors:

from pythoc import compile, linear, consume, void, ptr, i8, i32, struct
from pythoc.libc.stdlib import malloc, free

# Allocator returns resource + linear proof
@compile
def lmalloc(size: i32) -> struct[ptr[i8], linear]:
    return malloc(size), linear()

# Only the paired deallocator can consume the proof
@compile
def lfree(ptr: ptr[i8], prf: linear) -> void:
    free(ptr)
    consume(prf)  # Proof consumed - resource released

@compile
def safe_usage() -> void:
    mem, prf = lmalloc(100)
    # ... use mem ...
    lfree(mem, prf)  # Must call lfree to consume prf
    # Compile error if prf not consumed!

Motivation: Proof-carrying code pattern. The linear proof proves resource was allocated and must be deallocated. Compiler enforces pairing of alloc/free at compile-time.

Refinement Types (Optional)

Check once, use safely everywhere without runtime overhead:

from pythoc import compile, i32, bool, refined, refine, array, ptr, nullptr

# Non-null pointer - check once, use safely everywhere
@compile
def is_nonnull(p: ptr[i32]) -> bool:
    return p != nullptr

NonNull = refined[is_nonnull]

@compile
def process_data(p: ptr[i32]) -> i32:
    for ptr_checked in refine(p, is_nonnull):
        # Type system knows ptr_checked is non-null
        return access_ptr(ptr_checked)
    else:
        return -1  # Handle null case

@compile
def access_ptr(p: refined[is_nonnull]) -> i32:
    return p[0]

# Array bounds - check once, access many arrays safely
@compile
def is_valid_index(idx: i32) -> bool:
    return 0 <= idx < 10

@compile
def process_arrays(i: i32, arr1: array[i32, 10], arr2: array[i32, 10]) -> i32:
    for idx in refine(i, is_valid_index):
        # Type system remembers idx is valid
        a = arr1[idx]  # Safe: no bounds check
        b = arr2[idx]  # Safe: no bounds check
        c = arr1[idx]  # Safe: reuse safely
        return a + b + c
    else:
        return -1

Motivation: Check once, encode in type system, use safely everywhere. Typical examples: non-null pointers, valid array indices, non-zero divisors. The type system remembers the property, eliminating redundant runtime checks. Zero overhead for subsequent uses.

Effect System

Compile-time dependency injection with zero runtime overhead. Manage the global state and can be overridden at compile-time.

# rng_lib.py
from pythoc import compile, effect, u64, void
from types import SimpleNamespace

@compile
def rng_next() -> u64:
    return u64(42)

@compile
def rng_seed(s: u64) -> void:
    pass

RNG = SimpleNamespace(next=rng_next, seed=rng_seed)

effect.default(rng=RNG)

@compile
def use_rng() -> u64:
    return effect.rng.next()

# Another file can override the default RNG implementation
from pythoc import *
from types import SimpleNamespace

@compile
def mock_rng_next() -> u64:
    return u64(43)

@compile
def mock_rng_seed(s: u64) -> void:
    pass

MockRNG = SimpleNamespace(next=mock_rng_next, seed=mock_rng_seed)

with effect(rng=MockRNG, suffix="mock"):
    from rng_lib import use_rng

print(use_rng())    # 43

Defer

Explicit scope-exit actions, lowered to control flow.

from pythoc import compile, defer, linear, consume, void

@compile
def consumer(t: linear) -> void:
    consume(t)

@compile
def ok_defer(n: i32) -> void:
    for i in seq(n):
        t = linear()
        defer(consumer, t)
        yield i
        # consumer(t) executes at scope exit and consumes t no what user-code continues or breaks

Cimport

Import C modules directly into PythoC. cimport is a pure PythoC implementation to parse and import C modules. It is under a heavy re-design and development currently.

Python as Preprocessor

Use Python's full power at compile-time for metaprogramming:

Generic Types via Python Functions

from pythoc import compile, struct, i32, f64

# Python function generates specialized types and functions
def make_point(T):
    @struct(suffix=T)  # suffix creates unique type name
    class Point:
        x: T
        y: T
    
    @compile(suffix=T)  # suffix creates unique function name
    def add_points(p1: Point, p2: Point) -> Point:
        result: Point = Point()
        result.x = p1.x + p2.x
        result.y = p1.y + 
View on GitHub
GitHub Stars221
CategoryDevelopment
Updated3d ago
Forks10

Languages

Python

Security Score

100/100

Audited on Mar 24, 2026

No findings