blob: 4c4364d580d71c0f2d7568ed034e082846accb02 [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 Tolnay62463ba2016-11-24 13:06:01 -080051 expanded.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 Tolnay7c509bb2016-11-19 13:00:12 -080090 // Return the generated impl as a TokenStream
David Tolnay62463ba2016-11-24 13:06:01 -080091 expanded.parse().unwrap()
David Tolnayb988b6d2016-10-05 00:12:37 -070092}
93
David Tolnayed7a5082016-10-30 20:06:29 -070094fn expand_num_fields(ast: &syn::MacroInput) -> quote::Tokens {
David Tolnayb988b6d2016-10-05 00:12:37 -070095 let n = match ast.body {
96 syn::Body::Struct(ref data) => data.fields().len(),
97 syn::Body::Enum(_) => panic!("#[derive(NumFields)] can only be used with structs"),
98 };
99
100 // Used in the quasi-quotation below as `#name`
101 let name = &ast.ident;
102
103 // Helper is provided for handling complex generic types correctly and effortlessly
104 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
105
106 quote! {
David Tolnayb988b6d2016-10-05 00:12:37 -0700107 // The generated impl
108 impl #impl_generics ::mycrate::NumFields for #name #ty_generics #where_clause {
109 fn num_fields() -> usize {
110 #n
111 }
112 }
113 }
114}
115```
116
David Tolnay686f5042016-10-30 19:24:51 -0700117## Optional features
118
119Syn puts a lot of functionality behind optional features in order to optimize
120compile time for the most common use cases. These are the available features and
121their effect on compile time. Dependencies are included in the compile times.
122
123Features | Compile time | Functionality
124--- | --- | ---
125*(none)* | 1 sec | The data structures representing the AST of Rust structs, enums, and types.
126parsing | 4 sec | Parsing Rust source code containing structs and enums into an AST.
127printing | 2 sec | Printing an AST of structs and enums as Rust source code.
128**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.
129full | 2 sec | The data structures representing the full AST of all possible Rust code.
130full, parsing | 7 sec | Parsing any valid Rust source code to an AST.
131full, printing | 4 sec | Turning an AST into Rust source code.
132full, parsing, printing | 8 sec | Parsing and printing any Rust syntax.
133full, 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.
134full, 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.
135
David Tolnayed7a5082016-10-30 20:06:29 -0700136## Custom derives on stable Rust
137
138Syn supports a way of expanding custom derives from a build script, similar to
139what [Serde is able to do with serde_codegen](https://serde.rs/codegen-stable.html).
140The advantage of using Syn for this purpose rather than Syntex is much faster
141compile time.
142
143Continuing with the `NumFields` example from above, it can be extended to
144support stable Rust like this. One or more custom derives are added to a
145[`Registry`](https://dtolnay.github.io/syn/syn/struct.Registry.html), which is
146then able to expand those derives in a source file at a particular path and
147write the expanded output to a different path. A custom derive is represented by
148the [`CustomDerive`](https://dtolnay.github.io/syn/syn/trait.CustomDerive.html)
149trait which takes a [`MacroInput`](https://dtolnay.github.io/syn/syn/struct.MacroInput.html)
150(either a struct or an enum) and [expands it](https://dtolnay.github.io/syn/syn/struct.Expanded.html)
151into zero or more new items and maybe a modified or unmodified instance of the
152original input.
153
154```rust
155pub fn expand_file<S, D>(src: S, dst: D) -> Result<(), String>
156 where S: AsRef<Path>,
157 D: AsRef<Path>
158{
159 let mut registry = syn::Registry::new();
160 registry.add_derive("NumFields", |input| {
161 let tokens = expand_num_fields(&input);
162 let items = syn::parse_items(&tokens.to_string()).unwrap();
163 Ok(syn::Expanded {
164 new_items: items,
165 original: Some(input),
166 })
167 });
168 registry.expand_file(src, dst)
169}
170```
171
172The codegen can be invoked from a build script as follows.
173
174```rust
175extern crate your_codegen;
176
177use std::env;
178use std::path::Path;
179
180fn main() {
181 let out_dir = env::var_os("OUT_DIR").unwrap();
182
183 let src = Path::new("src/codegen_types.in.rs");
184 let dst = Path::new(&out_dir).join("codegen_types.rs");
185
186 your_codegen::expand_file(&src, &dst).unwrap();
187}
188```
189
David Tolnay35161ff2016-09-03 11:33:15 -0700190## License
191
192Licensed under either of
193
194 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
195 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
196
197at your option.
198
199### Contribution
200
201Unless you explicitly state otherwise, any contribution intentionally submitted
202for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
203be dual licensed as above, without any additional terms or conditions.