Sign in

    moonbindgen

    Generate MoonBit native C FFI declarations from C headers with Clang.

    ffi
    bindings
    clang
    code-generation
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    last month
    Downloads
    27

    #MoonBindgen

    MoonBindgen is a pure MoonBit Native command-line generator for conservative C FFI declarations. It delegates C parsing to Clang 23, converts the JSON AST into a typed declaration model, lowers only verified direct-ABI types, and writes deterministic MoonBit declarations together with an auditable report.

    The project deliberately does not pretend that syntax implies ownership. It rejects callbacks, by-value structs, output pointers, returned strings, variadic functions, and pointers whose lifetime is unclear. A configured call-scoped const char * input is the only string shortcut: it becomes Bytes with #borrow.

    #Why this project

    C interoperability is an ecosystem multiplier: one trustworthy generator can reduce the repetitive and error-prone first step of binding databases, compression libraries, parsers, and operating-system APIs. The hard part is not printing extern "C"; it is preserving declaration provenance, resolving typedefs without importing unrelated functions, making unsafe gaps visible, and producing output that reviewers can reproduce.

    klaseca/moonbit-bindgen is an important existing reference. It is a TypeScript framework with configurable source processing and stub generation. MoonBindgen takes a different technical route: the generator itself is MoonBit, Clang's AST is the source of declaration facts, unsupported ABI cases are rejected conservatively, every decision appears in a versioned report, and Native compile/link/call fixtures plus SQLite exercise the result. The rationale is therefore auditability and implementation diversity, not merely whether another project has been published to Mooncakes.

    #Architecture

    C header + config-v1 │ ▼ Clang 23 JSON AST ──► provenance-aware declaration model │ typedef/enum/opaque-type environment │ ▼ conservative ABI lowering │ ┌─────────────┴─────────────┐ ▼ ▼ bindings.mbt moonbindgen-report-v2

    The main header controls which declarations are emitted. Included headers may supply typedef and enum facts, but their functions are not emitted. Clang nodes that omit a repeated filename inherit the most recent explicit provenance; malformed or contradictory declaration structures stop generation instead of being guessed.

    #Requirements and installation

    • MoonBit 0.1.20260920 (914d7da) with Moonc/Core 0.10.14+7d59c7ec9
    • Clang 23.x; CI and local evidence use LLVM 23.1.1
    • Windows x64 is the currently verified environment, not a cross-platform promise

    The checked release toolchain is recorded in toolchain.json. CI rejects a different compiler or core version instead of silently accepting formatter or diagnostic drift.

    To consume the generator as a library:

    moon add shop1111/moonbindgen@0.1.0

    Add the root package to moon.pkg:

    import { "shop1111/moonbindgen" @bindgen, }

    The public entry points are generate, generate_with_config, generate_with_metadata, and parse_config. They accept Clang AST JSON; invoking Clang remains the responsibility of the CLI or the embedding application.

    let config_text =
    #|{"schema":"moonbindgen-config-v1","unsupported_policy":"report"}
    let config = @bindgen.parse_config(config_text)
    let result = @bindgen.generate_with_config(clang_ast_json, "library.h", config)
    println(result.bindings)

    Windows users can also download moonbindgen-v0.1.0-windows-x86_64.zip from the GitHub Release, extract moonbindgen.exe, and keep Clang 23 available separately. The executable is not a general C/C++ compiler bundle.

    #CLI quick start

    moon run -q cmd/main -- generate fixtures/basic.h --out _build/basic --clang 'C:\Program Files\LLVM\bin\clang.exe' moon run -q cmd/main -- generate fixtures/basic.h --out _build/basic --clang 'C:\Program Files\LLVM\bin\clang.exe' --check

    Arguments after -- go directly to Clang, for example -- -Ivendor/include -DFEATURE=1. --check compares both generated files and writes nothing; drift exits with code 6. Run --help for the complete interface. Exit codes distinguish usage/configuration (2), Clang (3), generation policy (4), output I/O (5), and drift (6).

    #Configuration v1

    --config <json> accepts only the versioned schema below. Function filters are exact names. Renames are normalized as MoonBit identifiers and collision-checked. Type overrides are restricted to Bool, Byte, Int, UInt, Int64, UInt64, Float, and Double; they cannot inject source text.

    { "schema": "moonbindgen-config-v1", "include_functions": ["library_open"], "exclude_functions": [], "renames": { "library_open": "open_library" }, "type_overrides": { "library_size_t": "UInt64" }, "utf8_inputs": ["library_open.path"], "unsupported_policy": "error" }

    unsupported_policy is report by default. With error, any unsupported declaration rejects the whole generation before official output files are replaced.

    #Supported ABI surface

    C declarationMoonBit outputPolicy
    char, signed/unsigned fixed mappingsByte, Int, UInt, Int64, UInt64Direct value ABI
    float, doubleFloat, DoubleDirect value ABI
    typedef chains and 32-bit enumsCanonical scalar / IntResolved from Clang facts
    pointer to an opaque struct typedef#external typeRaw pointer; caller owns lifetime rules
    configured input const char *Bytes plus #borrowValid only for the duration of the call
    returned strings, output pointers, callbacks, variadics, by-value structsnoneReported and skipped, or rejected in strict mode

    MoonBindgen emits a raw FFI layer. It does not generate a general C shim, callback trampolines, resource-safe wrappers, C++, or broad platform guarantees in version 0.1.x.

    #Auditable report v2

    report.json uses moonbindgen-report-v2. It records the Clang version and target, referenced include files, declaration kind and source line, original C signature, emitted MoonBit declaration, status, stable reason code, and summary counts. Existing v2 fields and reason codes will not be repurposed within 0.1.x; compatible fields may be added.

    { "schema": "moonbindgen-report-v2", "clang": { "version": "clang version 23.1.1 ...", "target": "x86_64-pc-windows-msvc" }, "summary": { "functions_generated": 4, "functions_skipped": 0, "unsupported": 0 }, "declarations": [{ "c_name": "abi_add", "status": "generated", "reason_code": "generated" }] }

    #Verification evidence

    ./scripts/verify.ps1

    The gate runs strict Native check/build/test, coverage analysis, two-run byte determinism, report schema assertions, include provenance, invalid-header rejection, artifact drift checks, a generated C fixture that compiles/links/calls scalars and a borrowed UTF-8 input, and the SQLite query below.

    examples/sqlite vendors the official SQLite 3.53.4 amalgamation. On Clang 23.1.1, MoonBindgen generates 128 functions and reports 170 unsupported functions. A small handwritten shim demonstrates the explicitly out-of-scope output-pointer boundary for open/prepare; generated declarations then execute SELECT 42 through sqlite3_step, sqlite3_column_int, sqlite3_finalize, and sqlite3_close.

    moon run -q examples/sqlite # SELECT 42 => 42

    Exact upstream provenance, checksums, licensing, and the one comment-only local edit are recorded in THIRD_PARTY.md.

    #Release hygiene

    .moonignore keeps fixtures, examples, scripts, CI configuration, tests, generated release assets, the local competition charter, and submission/ out of the Mooncakes package. The SQLite amalgamation is marked vendored for GitHub language statistics. Run the full verification gate, moon info, moon doc, and moon package --list before publishing. scripts/package-release.ps1 builds the stripped Windows CLI, creates deterministic release archives, and writes SHA256SUMS.txt; Gitlink mirroring is outside the 0.1.0 release.

    ConfigError

    pub(all) suberror ConfigError {
    InvalidConfig(String)
    } derive(Eq,
    Debug
    )

    An error raised when generation configuration is malformed or unsupported.

    ConfigError::equal

    #deprecated("`ConfigError::equal` is deprecated, use `Eq::equal` instead.")
    fn ConfigError::equal(ConfigError, ConfigError) -> Bool

    ConfigError::not_equal

    #deprecated("`ConfigError::not_equal` is deprecated, use `Eq::not_equal` instead.")
    fn ConfigError::not_equal(x : ConfigError, y : ConfigError) -> Bool

    ConfigError::to_repr

    #deprecated("`ConfigError::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn ConfigError::to_repr(ConfigError) ->
    Repr

    GenerateConfig

    pub struct GenerateConfig {
    include_functions : Map[String, Bool]
    exclude_functions : Map[String, Bool]
    renames : Map[String, String]
    type_overrides : Map[String, String]
    utf8_inputs : Map[String, Bool]
    unsupported_policy : UnsupportedPolicy
    }

    Versioned generation settings parsed from moonbindgen-config-v1 JSON.

    Use GenerateConfig::default for report-only generation, or parse_config when loading a user-supplied configuration file.

    GenerateConfig::default

    Returns the default configuration with no filters or overrides and with unsupported declarations reported instead of rejected.

    GenerateResult

    pub struct GenerateResult {
    bindings : String
    report : String
    generated : Int
    skipped : Int
    }

    Generated MoonBit declarations, their audit report, and function counts.

    ToolMetadata

    pub struct ToolMetadata {
    clang_version : String
    clang_target : String
    }

    Clang identity recorded in moonbindgen-report-v2 output.

    ToolMetadata::new

    fn ToolMetadata::new(clang_version : String, clang_target : String) -> ToolMetadata

    Creates metadata from the first Clang version line and target triple.

    ToolMetadata::unknown

    fn ToolMetadata::unknown() -> ToolMetadata

    Creates placeholder metadata for callers that already have AST JSON but do not know which Clang executable produced it.

    UnsupportedPolicy

    pub(all) enum UnsupportedPolicy {
    Report
    Error
    } derive(Eq,
    Debug
    )

    Controls how generation handles declarations outside MoonBindgen's conservative ABI surface.

    UnsupportedPolicy::equal

    #deprecated("`UnsupportedPolicy::equal` is deprecated, use `Eq::equal` instead.")
    fn UnsupportedPolicy::equal(UnsupportedPolicy, UnsupportedPolicy) -> Bool

    UnsupportedPolicy::not_equal

    #deprecated("`UnsupportedPolicy::not_equal` is deprecated, use `Eq::not_equal` instead.")
    fn UnsupportedPolicy::not_equal(x : UnsupportedPolicy, y : UnsupportedPolicy) -> Bool

    UnsupportedPolicy::to_repr

    #deprecated("`UnsupportedPolicy::to_repr` is deprecated, use `@moonbitlang/core/debug.Debug::to_repr` instead.")
    fn UnsupportedPolicy::to_repr(UnsupportedPolicy) ->
    Repr

    generate

    fn generate(ast_text : String, header : String) -> GenerateResult raise

    Generates bindings with the default configuration and unknown Clang metadata.

    Raises when ast_text is not a recognized Clang translation-unit AST or when a contradictory declaration structure cannot be interpreted safely.

    generate_with_config

    fn generate_with_config(ast_text : String, header : String, config : GenerateConfig) -> GenerateResult raise

    Generates bindings with explicit configuration and unknown Clang metadata.

    header identifies the main header whose functions are eligible for emission; included headers may still contribute typedef and enum facts.

    generate_with_metadata

    fn generate_with_metadata(ast_text : String, header : String, config : GenerateConfig, metadata : ToolMetadata) -> GenerateResult raise

    Generates deterministic bindings and a moonbindgen-report-v2 audit report with explicit configuration and Clang metadata.

    Raises for malformed AST JSON, unsupported declarations under strict policy, name collisions that cannot be represented safely, or contradictory Clang declaration structures.

    parse_config

    fn parse_config(text : String) -> GenerateConfig raise ConfigError

    Parses and validates a moonbindgen-config-v1 JSON document.

    Unknown fields, unsafe type overrides, and schema mismatches raise ConfigError::InvalidConfig.