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

extern crate proc_macro;
extern crate proc_macro2;

mod boilerplate;
pub mod diagnostic;
pub mod input;
pub mod parser;

pub use diagnostic::Incomplete;
pub use input::{Input, Token};
pub use proc_macro2::{Ident, Literal, Punct};