Skip to content
This repository has been archived by the owner on Jun 6, 2021. It is now read-only.

Note: Quoting and Unquoting

Alex Rønne Petersen edited this page Jun 9, 2013 · 10 revisions

The quote expression is used to turn a piece of code into an AST value of type @core::macro::Expr:

let expr = quote {
    if foo(@"...") {
        42;
        "asdf";
    } else {
        bar;
    };
};

// expr is of type @core::macro::Expr. It looks like this:

assert expr == @new Block {
    exprs = @[@new If {
        condition = @new FunctionCall {
            function = @new QualIdent {
                    idents = @[@new Ident {
                        value = "foo"
                    }
                ]
            },
            arguments = @[@new Box {
                    operand = @new StringLiteral {
                        value = "..."
                    }
                }
            ]
        },
        true_branch = @new Block {
            exprs = @[@new IntLiteral {
                    value = 42
                },
                @new StringLiteral {
                    value = "asdf"
                }
            ]
        },
        false_branch = @new Block {
                exprs = @[@new Ident {
                    value = "bar"
                }
            ]
        }
    }]
};

// Note that quoted expressions are actually stored in the data segment.

// The operand to quote doesn't have to be a block:

let expr2 = quote 42;

assert expr2 == @new IntLiteral {
    value = 42
};

Quoting happens after parsing, so invalid input to quote cannot occur (because it would have been flagged by the parser already). Of course, semantic errors can occur after quoted code is unquoted.

Note that quote can only be invoked inside a macro declaration.

A value of type @core::macro::Expr can be unquoted with unquote, effectively turning it into 'real' code:

let expr = quote 42;

// Quote a block with a result value of 42.
quote {
    unquote expr;
};

Note that unquote can only be invoked inside a quote expression.

So we could write a macro like this:

pub macro unless(cond, act) {
    quote {
        if !unquote cond {
            unquote act;
        };

        ();
    };
}

This could be invoked as:

unless!(2 + 2 != 4, {
    assert false, "well, that's not good";
});

Arguments to macros are passed literally as $core::macro::Expr AST values. So when unless is invoked, e.g. the unquote cond expression turns the 2 + 2 != 4 expression (represented as AST nodes) into actual code.

A macro's body is evaluated sequentially at compile time. The last value in a macro body is expected to be of type @core::macro::Expr and will be expanded into the macro call site. A macro's body is only semantically checked once invoked (since the inputs aren't available until then).

Within a macro's body, macro followed by a string literal can be used to query information about the environment the macro is being expanded in. In particular, we could say:

pub macro get_line() {
    let line = macro "line";
    quote unquote line;
}

We could then do:

assert get_line() == 42; // Assuming we're expanding get_line at line 42.

A list of the things that can be queried:

  • "file" (str): The absolute path to the file the macro is being expanded in.
  • "line" (u32): The line the macro is being expanded on. Starts at 1.
  • "column" (u32): The column the macro is being expanded at. Starts at 1.
  • "module" (str): The name of the module the macro is being expanded in, e.g. "foo::bar".
  • "function" (str): The name of the function the macro is being expanded in, e.g. "my_function".
Clone this wiki locally