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};