combine_proc_macro/lib.rs
1//! A library that allows [proc_macro] function-like macros to be parsed using
2//! the [combine] parser combinator crate.
3//!
4//! [proc_macro]: https://doc.rust-lang.org/stable/proc_macro/index.html
5//! [combine]: https://docs.rs/crate/combine
6//!
7//! ## Motivation
8//! When writing a `#[proc_macro_derive]` the input is Rust source code which is
9//! well supported by the `syn` crate. However, when writing a `#[proc_macro]`
10//! macro, it is common to want to define a custom domain specific language.
11//!
12//! This crate allows you to write a parser for your DSL using the `combine`
13//! parser combinator library. It also preserves the source _span_ information
14//! in the parsed result such that `rustc` can provide correct source locations
15//! for identifiers and literals that are re-used in the output.
16//!
17//! ## Implementing a parser
18//! This is a basic example using base `combine` parsers.
19//!
20//! ```rust
21//! # extern crate proc_macro;
22//! use combine::{ParseError, Parser, Stream};
23//! use combine_proc_macro::{Token, Literal};
24//! use combine_proc_macro::parser::{delim, keyword, literal, punct};
25//!
26//! /// Parses expressions like `{ hello "world"! }`.
27//! fn hello_grammar<I>() -> impl Parser<Input = I, Output = Literal>
28//! where
29//! I: Stream<Item = Token>,
30//! I::Error: ParseError<I::Item, I::Range, I::Position>,
31//! {
32//! ( delim('{')
33//! , keyword("hello")
34//! , literal()
35//! , punct('!')
36//! , delim('}')
37//! ).map(|(_, _, greeting, _, _)| greeting)
38//! }
39//! ```
40//!
41//! Using the `parser!` macro can help remove boilerplate.
42//!
43//! ```rust
44//! # extern crate proc_macro;
45//! use combine::Parser;
46//! use combine_proc_macro::Literal;
47//! use combine_proc_macro::parser;
48//! use combine_proc_macro::parser::{delim, keyword, literal, punct};
49//!
50//! parser!(fn hello_grammar() -> Literal {
51//! ( delim('{')
52//! , keyword("hello")
53//! , literal()
54//! , punct('!')
55//! , delim('}')
56//! ).map(|(_, _, greeting, _, _)| greeting)
57//! });
58//! ```
59//!
60//! ## Implementing a macro
61//! A proc macro must be defined at the crate root within the `lib.rs` file.
62//!
63//! ```rust,ignore
64//! extern crate proc_macro;
65//!
66//! use combine::parser::Parser;
67//! use combine_proc_macro::{Input, Incomplete};
68//! use proc_macro::TokenStream;
69//!
70//! #[proc_macro]
71//! pub fn hello_macro(input: TokenStream) -> TokenStream {
72//! let input = Input::from(input).with_lookahead(1);
73//! let result = hello_grammar().easy_parse(input);
74//! let (ast, trailing) = match result {
75//! Ok(ok) => ok,
76//! Err(err) => panic!("error parsing in `hello_macro` macro: {:#?}", err),
77//! };
78//! if let Some(diagnostic) = Incomplete::from_stream(trailing) {
79//! panic!("unexpected tokens at end of input:\n\n{}", diagnostic);
80//! }
81//!
82//! impl_hello_macro(&ast) // generate rust output; e.g. using the `quote` crate
83//! }
84//!
85//! # use combine::{ParseError, Stream};
86//! # use combine_proc_macro::Token;
87//! # use combine_proc_macro::parser::literal;
88//! # use proc_macro::Literal;
89//! #
90//! # fn hello_grammar<I>() -> impl Parser<Input = I, Output = Literal>
91//! # where
92//! # I: Stream<Item = Token>,
93//! # I::Error: ParseError<I::Item, I::Range, I::Position> { literal() }
94//! #
95//! # fn impl_hello_macro(ast: &Literal) -> TokenStream { unimplemented!() }
96//! ```
97
98extern crate proc_macro;
99extern crate proc_macro2;
100
101mod boilerplate;
102pub mod diagnostic;
103pub mod input;
104pub mod parser;
105
106pub use diagnostic::Incomplete;
107pub use input::{Input, Token};
108pub use proc_macro2::{Ident, Literal, Punct};