[−][src]Crate combine_proc_macro
A library that allows proc_macro function-like macros to be parsed using the combine parser combinator crate.
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.
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.
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.
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 }
Re-exports
pub use diagnostic::Incomplete; |
pub use input::Input; |
pub use input::Token; |
Modules
diagnostic | Utilities to generate diagnostic error messages. |
input | Wrappers and transforms to around |
parser | A collection of parsers for |
Macros
parser | A macro to remove the generics boilerplate when defining parsers. |
Structs
Ident | A word of Rust code, which may be a keyword or legal variable name. |
Literal | A literal string ( |
Punct | An |