Crate cps

source ·
Expand description

Macro execution order is tricky. For example, the output of the following code goes against our intuition of how functions should work:

macro_rules! expand_to_larch {
    () => { larch };
}

macro_rules! recognize_tree {
    (larch) => { println!("#1, the Larch.") };
    (redwood) => { println!("#2, the Mighty Redwood.") };
    (fir) => { println!("#3, the Fir.") };
    (chestnut) => { println!("#4, the Horse Chestnut.") };
    (pine) => { println!("#5, the Scots Pine.") };
    ($($other:tt)*) => { println!("I don't know; some kind of birch maybe?") };
}

macro_rules! name_a_larch {
    () => {
        recognize_tree!(expand_to_larch!())
    };
}

fn main() {
    name_a_larch!(); // Prints "I don't know; some kind of birch maybe?"
}

[The Little Book of Rust Macros][tlborm] (where the above example comes from) outlines callbacks - a macro pattern that allows macro execution order to be specified:


macro_rules! call_with_larch {
    ($callback:ident) => { $callback!(larch) };
}

macro_rules! name_a_larch {
    () => {
        call_with_larch!(recognize_tree)
    };
}

fn main() {
    name_a_larch!(); // Correctly prints "#1, the Larch." but is pretty hard to read
}

This syntax, while powerful, soon becomes confusing.

This macro allows far more readable macros to be written:


#[cps]
macro_rules! expand_to_larch {
    () => { larch };
}

#[cps]
macro_rules! recognize_tree {
    (larch) => { println!("#1, the Larch.") };
    // ...
    ($($other:tt)*) => { println!("I don't know; some kind of birch maybe?") };
}

#[cps]
macro_rules! name_a_larch {
    () =>
    let $tree:tt = expand_to_larch!() in
    {
        recognize_tree!($tree)
    };
}

fn main() {
    name_a_larch!(); // Prints "#1, the Larch."
}

Macros-by-example are hard, difficult to maintain, and you should always consider writing a proc-macro instead. This library aims to make the macros that you do write more maintainable. Please recurse responsibly.

Usage Notes

CPS converts iteration into recursion. Therefore when using this library you may reach the recursion limit (128 at the time of writing). You can raise this using #![recursion_limit = "1024"] but your build times may suffer.

Any macro let expression must have a macro on the right-hand side that was marked as #[cps]. The following example will not work:

#[cps]
macro_rules! foo {
    () => { BaseCase };

    (bar) =>
    let $x:tt = foo!() in
    let $y:tt = stringify!($x) in // Issue: stringify is not a cps macro
    {
        $y
    };
}

Instead, use the cps variants of builtin macros:

#[cps]
macro_rules! foo {
    () => { BaseCase };

    (bar) =>
    let $x:tt = foo!() in
    let $y:tt = cps::stringify!($x) in // cps::stringify is a cps version of `stringify`
    {
        $y
    };
}

Macros

  • Performs the same task as the builtin macro of the same name, but this version can also be used as a let binding in a CPS macro
  • Performs the same task as the builtin macro of the same name, but this version can also be used as a let binding in a CPS macro
  • Performs the same task as the builtin macro of the same name, but this version can also be used as a let binding in a CPS macro

Attribute Macros

  • Manipulates a macro_rules! definition to add extended syntax to help in creating readable macros.