Steven Fackler / sfackler
This stuff:
let count = try!(r.read(buf));
let foo = bytes!("hello");
include_file!("some_file.rs");
println!("Hello, {}", "world");
#[deriving(Show, Eq)]
struct Foo {
i: int
}
C's preprocessor operates on a lexical level.
Think find and replace - like sed
.
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define struct union
#define DO_A_THING(t) do { \
do_blah(t); \
do_foo(t); \
} while(0)
“An abstract syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language.”
pub enum Expr_ {
ExprCall(@Expr, ~[@Expr]),
ExprMatch(@Expr, ~[Arm]),
ExprForLoop(@Pat, @Expr, P<Block>, Option<Ident>),
...
}
pub enum Item_ {
ItemFn(P<FnDecl>, Purity, AbiSet, Generics, P<Block>),
ItemTrait(Generics, ~[TraitDef], ~[TraitMethod]),
...
}
...
Macros can do things that normal code simply can't.
Return from the invoking function
macro_rules! try(
($e:expr) => (
match $e {
Ok(e) => e,
Err(e) => return Err(e)
}
)
)
fn read_foo(r: &mut Reader) -> Result<Foo, IoError> {
let bar = try!(r.read_be_i32());
let baz = try!(r.read_bytes(10));
Ok(Foo { bar: bar, baz: baz })
}
(Almost) freeform syntax
let map = make_map!(Start with collections::HashMap::new() and add
"foo" => 1,
"bar" => 2,
"baz" => 3);
Do work at compile time instead of run time!
test.rs:2:35: 2:37 error: argument never used
test.rs:2 println!("hello {}", "world", 10);
^~
Syntax extensions don't have to be defined in the compiler anymore! (#11151)
They can be implemented in libraries that the compiler can load into itself.
It's simple for macro_rules!
.
#[macro_export]
macro_rules! my_macro(() => (1))
It's a bit more complicated for procedural macros.
Libraries define a macro_registrar
function to register procedural macros with the
compiler.
#[macro_registrar]
pub fn registrar(cb: |ast::Name, ext::base::SyntaxExtension|) {
...
}
pub enum SyntaxExtension {
// e.g. #[deriving(...)]
ItemDecorator(...),
// e.g. try!(...)
NormalTT(...),
// e.g. macro_rules! foo(...)
IdentTT(...),
}
Client code annotates the crate import to tell the compiler to load syntax extensions from it.
#[feature(phase)];
#[phase(syntax)]
extern crate my_macros;
fn main() {
let _x = my_macro!();
}
Let's write a syntax extension that creates sorted arrays of strings:
#[feature(phase)];
#[phase(syntax)]
extern crate sort;
fn main() {
assert_eq!(sort!("z", "", "hello", "a"),
["", "a", "hello", "z"]);
}
impl
block via AST
nodes (or don't).
External syntax extensions are feature gated for a reason! The API can and will change.