MCPcopy Index your code
hub / github.com/ekzhang/crepe

github.com/ekzhang/crepe @v0.2.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.2.0 ↗ · + Follow
149 symbols 282 edges 39 files 19 documented · 13% updated 6mo agov0.2.0 · 2025-12-14★ 5215 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Crepe

github crates.io docs.rs build status

Crepe is a library that allows you to write declarative logic programs in Rust, with a Datalog-like syntax. It provides a procedural macro that generates efficient, safe code and interoperates seamlessly with Rust programs.

Features

  • Semi-naive evaluation
  • Stratified negation
  • Automatic generation of indices for relations
  • Easily call Rust functions from within Datalog rules
  • Typesafe way to initialize @input relations
  • Very fast, compiled directly with the rest of your Rust code

Example

The program below computes the transitive closure of a directed graph. Note the use of the crepe! macro.

use crepe::crepe;

crepe! {
    @input
    struct Edge(i32, i32);

    @output
    struct Reachable(i32, i32);

    Reachable(x, y) <- Edge(x, y);
    Reachable(x, z) <- Edge(x, y), Reachable(y, z);
}

fn main() {
    let mut runtime = Crepe::new();
    runtime.extend([Edge(1, 2), Edge(2, 3), Edge(3, 4), Edge(2, 5)]);

    let (reachable,) = runtime.run();
    for Reachable(x, y) in reachable {
        println!("node {} can reach node {}", x, y);
    }
}

Output:

node 1 can reach node 2
node 1 can reach node 3
node 1 can reach node 4
node 1 can reach node 5
node 2 can reach node 3
node 2 can reach node 4
node 2 can reach node 5
node 3 can reach node 4

You can do much more with Crepe. The next example shows how you can use stratified negation, Rust expression syntax, and semi-naive evaluation to find all paths in a weighted graph with length at most MAX_PATH_LEN.

use crepe::crepe;

const MAX_PATH_LEN: u32 = 20;

crepe! {
    @input
    struct Edge(i32, i32, u32);

    @output
    struct Walk(i32, i32, u32);

    @output
    struct NoWalk(i32, i32);

    struct Node(i32);

    Node(x) <- Edge(x, _, _);
    Node(x) <- Edge(_, x, _);

    Walk(x, x, 0) <- Node(x);
    Walk(x, z, len1 + len2) <-
        Edge(x, y, len1),
        Walk(y, z, len2),
        (len1 + len2 <= MAX_PATH_LEN);

    NoWalk(x, y) <- Node(x), Node(y), !Walk(x, y, _);
}

fn main() {
    let n = 256;
    let mut edges = Vec::new();
    for i in 0..n {
        for j in 0..n {
            if rand::random::<f32>() < 0.02 {
                edges.push(Edge(i, j, 5));
            }
        }
    }

    let mut runtime = Crepe::new();
    runtime.extend(edges);
    let (walk, nowalk) = runtime.run();
    println!("Walk: {}", walk.len());
    println!("NoWalk: {}", nowalk.len());
}

Output:

Walk: 89203
NoWalk: 8207

Notes

From initial testing, the generated code is very fast. Variants of transitive closure for large graphs (~106 relations) run at comparable speed to compiled Souffle, and use a fraction of the compilation time.

For benchmarks, see the benches/ directory. The benchmarks can be run using cargo bench.

This macro generates a Crepe struct in the current module, as well as structs for all of the declared relations. This means that to integrate Crepe inside a larger program, you should put it in its own module with related code. See the documentation for more information.

Acknowledgements

This project was heavily inspired by Souffle and Formulog, which both use similar models of Datalog compilation for static analysis.

Extension points exported contracts — how you extend this code

Valuable (Interface)
Custom trait [2 implementers]
tests/test_custom_trait_bounds.rs
Node (Interface)
Multiple trait definitions [1 implementers]
tests/test_showcase_all_features.rs
Node (Interface)
Custom trait for graph nodes [1 implementers]
tests/test_graph_with_node_trait.rs
Key (Interface)
(no doc) [1 implementers]
tests/test_multiple_generics_with_bounds.rs
Measurable (Interface)
(no doc) [1 implementers]
tests/test_multiple_trait_methods.rs
KeyTrait (Interface)
(no doc) [1 implementers]
tests/test_complex_multiple_bounds.rs
Label (Interface)
(no doc) [1 implementers]
tests/test_complex_multiple_generics.rs
Custom (Interface)
(no doc) [1 implementers]
tests/test_multiple_trait_bounds.rs

Core symbols most depended-on inside this repo

iter
called by 38
src/strata.rs
to_lowercase
called by 9
src/lib.rs
relation_type
called by 7
src/lib.rs
get_relation
called by 4
src/lib.rs
to_ident
called by 3
src/lib.rs
collect_generic_params
called by 3
src/lib.rs
format_generics
called by 3
src/lib.rs
generic_params_decl
called by 3
src/lib.rs

Shape

Function 70
Method 34
Class 25
Interface 13
Enum 7

Languages

Rust100%

Modules by API surface

src/lib.rs34 symbols
src/parse.rs11 symbols
tests/test_showcase_all_features.rs8 symbols
tests/test_complex_multiple_bounds.rs8 symbols
tests/test_multiple_generics_with_bounds.rs7 symbols
tests/test_multiple_trait_methods.rs6 symbols
tests/test_custom_trait_bounds.rs6 symbols
tests/test_multiple_trait_bounds.rs5 symbols
tests/test_graph_with_node_trait.rs5 symbols
tests/test_complex_trait_methods.rs5 symbols
tests/test_complex_multiple_generics.rs5 symbols
tests/test_transitive_closure.rs4 symbols

For agents

$ claude mcp add crepe \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact