diff --git a/README.md b/README.md index 1e5c59c..3039b9e 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,17 @@ # mdbook-check-code -A configuration-driven mdBook preprocessor that validates code blocks in multiple languages by compiling them with user-specified compilers. +A configuration-driven mdBook preprocessor that validates code blocks by compiling them with user-specified compilers. -## Overview +## Quick Start -`mdbook-check-code` is a preprocessor for [mdBook](https://github.com/rust-lang/mdBook) that automatically extracts and validates code blocks from your documentation. All language behavior is configured in `book.toml` - no languages are built-in. This helps catch errors early and ensures your documentation stays in sync with working code. - -## Features - -- **Multi-language support**: C, TypeScript, Solidity, or any language you configure -- **Language variants**: Define variants with different compilers (e.g., Parasol C for FHE) -- **Configuration-driven**: All compiler behavior specified in `book.toml` - no hardcoded defaults -- **Environment variable expansion**: Use `${VAR}` syntax in compiler paths -- **No regex**: Uses `pulldown-cmark` for clean markdown parsing -- **Selective compilation**: Supports `ignore` flag to skip specific code blocks -- **Code propagation**: Use the `propagate` flag to share code between blocks (useful for structs and helper functions) -- **mdBook integration**: Works seamlessly with mdBook's build process -- **Clear error messages**: Shows compilation errors with context -- **Extensible**: Add new languages without writing Rust code - -## Installation - -### Using Nix (Recommended) - -This project provides a Nix flake that includes the preprocessor and compilers (Sunscreen LLVM, gcc, TypeScript): +### Using Nix ```bash -# Enter development environment -cd mdbook-check-code +# Enter development environment with all compilers nix develop # Build the preprocessor nix build - -# Install to your profile -nix profile install ``` ### Using Cargo @@ -43,270 +20,79 @@ nix profile install cargo install --path . ``` -**Note**: You'll need to separately install compilers for the languages you want to check: -- Parasol C: [Sunscreen LLVM compiler](https://github.com/Sunscreen-tech/sunscreen-llvm) -- Plain C: gcc or clang -- TypeScript: Node.js and `npm install -g typescript` +Requires separate installation of compilers for enabled languages (gcc, clang, tsc, etc.). ## Usage -### Configure your mdBook - -Add the preprocessor and language configurations to your `book.toml`: +Add the preprocessor and language configurations to `book.toml`: ```toml -[book] -title = "My Documentation" -authors = ["Your Name"] - [preprocessor.check-code] -command = "mdbook-check-code" -# C configuration +# C language configuration [preprocessor.check-code.languages.c] enabled = true compiler = "gcc" flags = ["-fsyntax-only"] -# Parasol variant - uses Sunscreen LLVM for FHE compilation +# Parasol variant for FHE code [preprocessor.check-code.languages.c.variants.parasol] -compiler = "${CLANG}" # Environment variable expansion +compiler = "${CLANG}" flags = ["-target", "parasol", "-fsyntax-only"] -preamble = "#include " # Prepended to all blocks - -# TypeScript configuration -[preprocessor.check-code.languages.typescript] -enabled = true -compiler = "tsc" -flags = ["--noEmit", "--skipLibCheck"] - -# Solidity configuration -[preprocessor.check-code.languages.solidity] -enabled = true -compiler = "solc" - -[output.html] +preamble = "#include " ``` -### Write your documentation - -Use standard markdown with code blocks. The fence marker determines which language configuration is used: +Write code blocks with fence markers: ````markdown -# Parasol C Example - ```c,variant=parasol -[[clang::fhe_program]] uint8_t add( - [[clang::encrypted]] uint8_t a, - [[clang::encrypted]] uint8_t b -) { +[[clang::fhe_program]] uint8_t add(uint8_t a, uint8_t b) { return a + b; } ``` - -# Plain C Example - -```c -#include - -uint32_t multiply(uint32_t a, uint32_t b) { - return a * b; -} -``` - -# TypeScript Example - -```typescript -function greet(name: string): string { - return `Hello, ${name}!`; -} -``` ```` -### Build your book +Build the book: ```bash -# Set environment variables (if not using Nix) -export CLANG=/path/to/sunscreen-llvm/bin/clang - -# Build the book mdbook build ``` -The preprocessor will automatically validate all configured code blocks and report any errors. - -## Code Block Flags - -### `ignore` - Skip compilation - -Use this flag for code that shouldn't be compiled (e.g., pseudocode, incomplete examples): - -````markdown -```c,ignore -// This won't be compiled -incomplete_function() { -``` -```` - -### `propagate` - Share code between blocks +The preprocessor validates all code blocks during the build process and reports compilation errors. -Use this flag to make definitions available to subsequent code blocks in the same file: +### Code Block Flags -````markdown -Define a struct: - -```c,variant=parasol,propagate -typedef struct Point { - uint16_t x; - uint16_t y; -} Point; -``` - -Use the struct in later blocks: - -```c,variant=parasol -[[clang::fhe_program]] void move_point( - [[clang::encrypted]] Point *p, - uint16_t dx, - uint16_t dy -) { - p->x = p->x + dx; - p->y = p->y + dy; -} -``` -```` +- `ignore` - Skip compilation for a block +- `propagate` - Make code available to subsequent blocks in the same file ## Configuration -### Language Configuration - -Each language requires a full configuration in `book.toml`: - -**Required fields**: -- `enabled` (bool): Whether to check this language -- `compiler` (string): Compiler executable (supports `${VAR}` env var expansion) -- `flags` (array): Compiler flags - -**Optional fields**: -- `preamble` (string): Code prepended to all blocks (e.g., includes) -- `fence_markers` (array): Custom fence identifiers (e.g., `["ts", "typescript"]`) +All language behavior is configured in `book.toml`. Each language requires: +- `enabled` (bool) - Whether to check this language +- `compiler` (string) - Compiler executable (supports `${VAR}` env var expansion) +- `flags` (array) - Compiler flags -### Language Variants - -You can define variants of a language that use different compilers or flags: - -```toml -# Base language -[preprocessor.check-code.languages.c] -enabled = true -compiler = "gcc" -flags = ["-fsyntax-only"] - -# Parasol variant for FHE code -[preprocessor.check-code.languages.c.variants.parasol] -compiler = "${CLANG}" -flags = ["-target", "parasol", "-fsyntax-only"] -preamble = "#include " -``` - -Variants are referenced using the `variant=name` attribute in the fence marker: - -````markdown -```c,variant=parasol -// Your Parasol C code here -``` -```` - -### Adding New Languages - -Add any language by configuring it in `book.toml`. Example for Python with mypy: - -```toml -[preprocessor.check-code.languages.python] -enabled = true -compiler = "mypy" -flags = ["--ignore-missing-imports"] -# Optional: specify custom fence markers -fence_markers = ["python", "py"] -``` - -By default, the language name is used as the fence marker (e.g., `python`). Use -`fence_markers` to add aliases or override the default behavior. - -### Environment Variables - -Use `${VAR}` syntax in compiler paths and flags. Common variables: -- `${CLANG}`: Path to Sunscreen LLVM clang binary - -### Nix Development Environment - -When using `nix develop`, the environment is automatically configured with: -- The Sunscreen LLVM compiler -- Standard gcc compiler -- TypeScript (node + tsc) -- `CLANG` environment variable -- mdBook and other development tools - -## How It Works - -1. **Integration**: mdBook calls the preprocessor before rendering -2. **Configuration**: Preprocessor loads language configs from `book.toml` and expands env vars -3. **Extraction**: Uses `pulldown-cmark` to extract code blocks (no regex!) -4. **Language matching**: Fence markers matched via simple string equality checks -5. **Compilation**: Each code block is validated with the configured compiler and flags -6. **Error Reporting**: Compilation failures are reported with file names and error messages -7. **Success**: If all code blocks compile, mdBook continues with rendering +Optional: +- `preamble` (string) - Code prepended to all blocks +- `fence_markers` (array) - Custom fence identifiers ## Testing -Test the preprocessor on the included fixtures (includes C, Parasol C variant, TypeScript, and Solidity examples): +Test with included fixtures: ```bash -# Using Nix (recommended - includes all compilers) +# With all compilers (Nix) nix develop --command bash -c "cd tests/fixtures && mdbook build" -# Or with Cargo (requires compilers to be installed separately) +# Or with Cargo cargo build --release -cd tests/fixtures -export CLANG=/path/to/sunscreen-llvm/bin/clang -mdbook build -``` - -## Development - -```bash -# Enter development environment (includes all compilers) -nix develop - -# Build -cargo build - -# Run tests -cargo test - -# Format code -cargo fmt - -# Run clippy -cargo clippy - -# Build with Nix -nix build +cd tests/fixtures && mdbook build ``` -## Architecture - -- **No regex**: Uses `pulldown-cmark` for markdown parsing and simple string operations -- **Configuration-driven**: All language behavior from `book.toml`, no hardcoded languages -- **Modular**: Clean separation between markdown extraction, configuration, and compilation -- **Extensible**: Add languages via config without touching Rust code - ## License This project is licensed under the GNU Affero General Public License v3.0 (AGPLv3). -See the [LICENSE](LICENSE) file for details. - ## Related Projects - [mdBook](https://github.com/rust-lang/mdBook) - The book generator diff --git a/sunscreen-llvm.nix b/sunscreen-llvm.nix index 36addb7..6ce2b9a 100644 --- a/sunscreen-llvm.nix +++ b/sunscreen-llvm.nix @@ -1,7 +1,8 @@ { lib, stdenv, fetchurl, autoPatchelfHook, zlib }: let - version = "2025-09-30"; + version = "2025.09.30"; + fileVersion = builtins.replaceStrings [ "." ] [ "-" ] version; urlBase = "https://github.com/Sunscreen-tech/sunscreen-llvm/releases/download/v${version}"; @@ -11,17 +12,17 @@ in stdenv.mkDerivation rec { src = if stdenv.isDarwin then fetchurl { - url = "${urlBase}/parasol-compiler-macos-aarch64-${version}.tar.gz"; + url = "${urlBase}/parasol-compiler-macos-aarch64-${fileVersion}.tar.gz"; sha256 = "0ra93mji3j9km7ia21gsqswn49a3abwc1ml1xq643hzq4xigyqjd"; } else if stdenv.isAarch64 then fetchurl { - url = "${urlBase}/parasol-compiler-linux-aarch64-${version}.tar.gz"; + url = "${urlBase}/parasol-compiler-linux-aarch64-${fileVersion}.tar.gz"; sha256 = "197fybbjvimnyqwwn3q7s9yrljbqp57s42n9znpckmnbcbp8p373"; } else fetchurl { - url = "${urlBase}/parasol-compiler-linux-x86-64-${version}.tar.gz"; + url = "${urlBase}/parasol-compiler-linux-x86-64-${fileVersion}.tar.gz"; sha256 = "1p0418nqzs6a2smrbqiyrxj34pimm6qzj7k29l4ys226cz6kfz2r"; };