Go to file
2016-10-30 00:03:19 -07:00
src Allow negative literals in range pattern 2016-10-29 23:40:00 -07:00
tests Catch panics from syntex in test 2016-10-30 00:03:19 -07:00
.gitignore item::parse 2016-09-03 11:33:15 -07:00
.travis.yml Integrate with syntex to print expanded code 2016-10-23 22:18:45 -07:00
Cargo.toml Give up on comparing strings macro tokens in round trip test 2016-10-25 10:24:54 -07:00
LICENSE-APACHE item::parse 2016-09-03 11:33:15 -07:00
LICENSE-MIT item::parse 2016-09-03 11:33:15 -07:00
README.md Adjust headline of readme 2016-10-23 15:20:23 -07:00

Nom parser for Rust source code

Build Status Latest Version Rust Documentation

Parse Rust structs and enums without a Syntex dependency, intended for use with Macros 1.1.

Designed for fast compile time.

  • Compile time for syn (from scratch including all dependencies): 4 seconds
  • Compile time for the syntex/quasi/aster stack: 60+ seconds

Usage with Macros 1.1

[dependencies]
syn = "0.9"
quote = "0.3"

[lib]
proc-macro = true
#![feature(proc_macro, proc_macro_lib)]

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate syn;

#[macro_use]
extern crate quote;

#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    // Parse the string representation to an AST
    let ast = syn::parse_macro_input(&source).unwrap();

    // Build the output, possibly using quasi-quotation
    let expanded = quote! {
        // ...
    };

    // Parse back to a token stream and return it
    expanded.to_string().parse().unwrap()
}

Complete example

Suppose we have the following simple trait which returns the number of fields in a struct:

trait NumFields {
    fn num_fields() -> usize;
}

A complete Macros 1.1 implementation of #[derive(NumFields)] based on syn and quote looks like this:

#![feature(proc_macro, proc_macro_lib)]

extern crate proc_macro;
use proc_macro::TokenStream;

extern crate syn;

#[macro_use]
extern crate quote;

#[proc_macro_derive(NumFields)]
pub fn num_fields(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    // Parse the string representation to an AST
    let ast = syn::parse_macro_input(&source).unwrap();

    // Build the output
    let expanded = expand_num_fields(ast);

    // Parse back to a token stream and return it
    expanded.to_string().parse().unwrap()
}

fn expand_num_fields(ast: syn::MacroInput) -> quote::Tokens {
    let n = match ast.body {
        syn::Body::Struct(ref data) => data.fields().len(),
        syn::Body::Enum(_) => panic!("#[derive(NumFields)] can only be used with structs"),
    };

    // Used in the quasi-quotation below as `#name`
    let name = &ast.ident;

    // Helper is provided for handling complex generic types correctly and effortlessly
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    quote! {
        // Preserve the input struct unmodified
        #ast

        // The generated impl
        impl #impl_generics ::mycrate::NumFields for #name #ty_generics #where_clause {
            fn num_fields() -> usize {
                #n
            }
        }
    }
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.