blob: 7ebf3aec82af3e3e87673ac200f9d9d4abad1483 [file] [log] [blame] [view]
David Tolnay6eacaff2016-10-23 15:20:23 -07001Nom parser for Rust source code
2===============================
David Tolnay35161ff2016-09-03 11:33:15 -07003
David Tolnayac9953b2016-09-07 08:37:12 -07004[![Build Status](https://api.travis-ci.org/dtolnay/syn.svg?branch=master)](https://travis-ci.org/dtolnay/syn)
5[![Latest Version](https://img.shields.io/crates/v/syn.svg)](https://crates.io/crates/syn)
David Tolnay50e62202016-09-12 09:49:49 -07006[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://dtolnay.github.io/syn/syn/)
David Tolnayac9953b2016-09-07 08:37:12 -07007
David Tolnay35161ff2016-09-03 11:33:15 -07008Parse Rust structs and enums without a Syntex dependency, intended for use with
9[Macros 1.1](https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md).
10
David Tolnayf939f352016-09-11 18:00:09 -070011Designed for fast compile time.
12
David Tolnayb5a7b142016-09-13 22:46:39 -070013- Compile time for `syn` (from scratch including all dependencies): **4 seconds**
David Tolnayf939f352016-09-11 18:00:09 -070014- Compile time for the `syntex`/`quasi`/`aster` stack: **60+ seconds**
15
David Tolnayed8e23e2016-09-27 08:18:22 -070016## Usage with Macros 1.1
David Tolnay6c9f5b62016-09-13 15:19:22 -070017
David Tolnay69b538e2016-09-23 19:59:48 -070018```toml
19[dependencies]
David Tolnay3c0c8c52016-10-30 20:20:46 -070020syn = "0.10"
David Tolnayf8f43072016-10-08 14:58:05 -070021quote = "0.3"
David Tolnay69b538e2016-09-23 19:59:48 -070022
23[lib]
David Tolnay45cc8492016-10-08 20:52:03 -070024proc-macro = true
David Tolnay69b538e2016-09-23 19:59:48 -070025```
26
White-Oak82d0db72016-09-13 21:45:58 +030027```rust
David Tolnay45cc8492016-10-08 20:52:03 -070028#![feature(proc_macro, proc_macro_lib)]
White-Oak82d0db72016-09-13 21:45:58 +030029
David Tolnay45cc8492016-10-08 20:52:03 -070030extern crate proc_macro;
31use proc_macro::TokenStream;
David Tolnayb4c63262016-09-23 20:03:06 -070032
33extern crate syn;
David Tolnay6c9f5b62016-09-13 15:19:22 -070034
White-Oak82d0db72016-09-13 21:45:58 +030035#[macro_use]
36extern crate quote;
White-Oak82d0db72016-09-13 21:45:58 +030037
David Tolnay45cc8492016-10-08 20:52:03 -070038#[proc_macro_derive(MyMacro)]
David Tolnayb4c63262016-09-23 20:03:06 -070039pub fn my_macro(input: TokenStream) -> TokenStream {
White-Oak82d0db72016-09-13 21:45:58 +030040 let source = input.to_string();
41
David Tolnayb988b6d2016-10-05 00:12:37 -070042 // Parse the string representation to an AST
David Tolnayf38cdf62016-09-23 19:07:09 -070043 let ast = syn::parse_macro_input(&source).unwrap();
White-Oak82d0db72016-09-13 21:45:58 +030044
David Tolnay6c9f5b62016-09-13 15:19:22 -070045 // Build the output, possibly using quasi-quotation
46 let expanded = quote! {
47 // ...
48 };
49
David Tolnayb988b6d2016-10-05 00:12:37 -070050 // Parse back to a token stream and return it
David Tolnay6c9f5b62016-09-13 15:19:22 -070051 expanded.to_string().parse().unwrap()
White-Oak82d0db72016-09-13 21:45:58 +030052}
53```
David Tolnay6c9f5b62016-09-13 15:19:22 -070054
David Tolnayb988b6d2016-10-05 00:12:37 -070055## Complete example
56
57Suppose we have the following simple trait which returns the number of fields in
58a struct:
59
60```rust
61trait NumFields {
62 fn num_fields() -> usize;
63}
64```
65
66A complete Macros 1.1 implementation of `#[derive(NumFields)]` based on `syn`
67and [`quote`](https://github.com/dtolnay/quote) looks like this:
68
69```rust
David Tolnay45cc8492016-10-08 20:52:03 -070070#![feature(proc_macro, proc_macro_lib)]
David Tolnayb988b6d2016-10-05 00:12:37 -070071
David Tolnay45cc8492016-10-08 20:52:03 -070072extern crate proc_macro;
73use proc_macro::TokenStream;
David Tolnayb988b6d2016-10-05 00:12:37 -070074
75extern crate syn;
76
77#[macro_use]
78extern crate quote;
79
David Tolnay45cc8492016-10-08 20:52:03 -070080#[proc_macro_derive(NumFields)]
David Tolnayb988b6d2016-10-05 00:12:37 -070081pub fn num_fields(input: TokenStream) -> TokenStream {
82 let source = input.to_string();
83
84 // Parse the string representation to an AST
85 let ast = syn::parse_macro_input(&source).unwrap();
86
87 // Build the output
David Tolnayed7a5082016-10-30 20:06:29 -070088 let expanded = expand_num_fields(&ast);
David Tolnayb988b6d2016-10-05 00:12:37 -070089
David Tolnayed7a5082016-10-30 20:06:29 -070090 // Return the original input struct unmodified, and the
91 // generated impl along with it
92 quote!(#ast #expanded).to_string().parse().unwrap()
David Tolnayb988b6d2016-10-05 00:12:37 -070093}
94
David Tolnayed7a5082016-10-30 20:06:29 -070095fn expand_num_fields(ast: &syn::MacroInput) -> quote::Tokens {
David Tolnayb988b6d2016-10-05 00:12:37 -070096 let n = match ast.body {
97 syn::Body::Struct(ref data) => data.fields().len(),
98 syn::Body::Enum(_) => panic!("#[derive(NumFields)] can only be used with structs"),
99 };
100
101 // Used in the quasi-quotation below as `#name`
102 let name = &ast.ident;
103
104 // Helper is provided for handling complex generic types correctly and effortlessly
105 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
106
107 quote! {
David Tolnayb988b6d2016-10-05 00:12:37 -0700108 // The generated impl
109 impl #impl_generics ::mycrate::NumFields for #name #ty_generics #where_clause {
110 fn num_fields() -> usize {
111 #n
112 }
113 }
114 }
115}
116```
117
David Tolnay686f5042016-10-30 19:24:51 -0700118## Optional features
119
120Syn puts a lot of functionality behind optional features in order to optimize
121compile time for the most common use cases. These are the available features and
122their effect on compile time. Dependencies are included in the compile times.
123
124Features | Compile time | Functionality
125--- | --- | ---
126*(none)* | 1 sec | The data structures representing the AST of Rust structs, enums, and types.
127parsing | 4 sec | Parsing Rust source code containing structs and enums into an AST.
128printing | 2 sec | Printing an AST of structs and enums as Rust source code.
129**parsing, printing** | **4 sec** | **This is the default.** Parsing and printing of Rust structs and enums. This is typically what you want for implementing Macros 1.1 custom derives.
130full | 2 sec | The data structures representing the full AST of all possible Rust code.
131full, parsing | 7 sec | Parsing any valid Rust source code to an AST.
132full, printing | 4 sec | Turning an AST into Rust source code.
133full, parsing, printing | 8 sec | Parsing and printing any Rust syntax.
134full, parsing, printing, expand | 9 sec | Expansion of custom derives in a file of Rust code. This is typically what you want for expanding custom derives on stable Rust using a build script.
135full, parsing, printing, expand, pretty | 60 sec | Expansion of custom derives with pretty-printed output. This is what you want when iterating on or debugging a custom derive, but the pretty printing should be disabled once you get everything working.
136
David Tolnayed7a5082016-10-30 20:06:29 -0700137## Custom derives on stable Rust
138
139Syn supports a way of expanding custom derives from a build script, similar to
140what [Serde is able to do with serde_codegen](https://serde.rs/codegen-stable.html).
141The advantage of using Syn for this purpose rather than Syntex is much faster
142compile time.
143
144Continuing with the `NumFields` example from above, it can be extended to
145support stable Rust like this. One or more custom derives are added to a
146[`Registry`](https://dtolnay.github.io/syn/syn/struct.Registry.html), which is
147then able to expand those derives in a source file at a particular path and
148write the expanded output to a different path. A custom derive is represented by
149the [`CustomDerive`](https://dtolnay.github.io/syn/syn/trait.CustomDerive.html)
150trait which takes a [`MacroInput`](https://dtolnay.github.io/syn/syn/struct.MacroInput.html)
151(either a struct or an enum) and [expands it](https://dtolnay.github.io/syn/syn/struct.Expanded.html)
152into zero or more new items and maybe a modified or unmodified instance of the
153original input.
154
155```rust
156pub fn expand_file<S, D>(src: S, dst: D) -> Result<(), String>
157 where S: AsRef<Path>,
158 D: AsRef<Path>
159{
160 let mut registry = syn::Registry::new();
161 registry.add_derive("NumFields", |input| {
162 let tokens = expand_num_fields(&input);
163 let items = syn::parse_items(&tokens.to_string()).unwrap();
164 Ok(syn::Expanded {
165 new_items: items,
166 original: Some(input),
167 })
168 });
169 registry.expand_file(src, dst)
170}
171```
172
173The codegen can be invoked from a build script as follows.
174
175```rust
176extern crate your_codegen;
177
178use std::env;
179use std::path::Path;
180
181fn main() {
182 let out_dir = env::var_os("OUT_DIR").unwrap();
183
184 let src = Path::new("src/codegen_types.in.rs");
185 let dst = Path::new(&out_dir).join("codegen_types.rs");
186
187 your_codegen::expand_file(&src, &dst).unwrap();
188}
189```
190
David Tolnay35161ff2016-09-03 11:33:15 -0700191## License
192
193Licensed under either of
194
195 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
196 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
197
198at your option.
199
200### Contribution
201
202Unless you explicitly state otherwise, any contribution intentionally submitted
203for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
204be dual licensed as above, without any additional terms or conditions.