pest_typed_tree/
lib.rs

1extern crate proc_macro;
2
3mod generate;
4
5/// Generates helper structs for type-safe AST handling.
6/// # Structs
7/// For each rule `pest-typed-tree` will generate struct.
8/// E.g. `foo_rule` -> `FooRule`, and so on.
9/// This struct is wrapper for pest [`Pair`](pest::iterators::Pair) for which
10/// as_rule() == foo_rule holds.
11/// You can create this struct using new() constructor, and get underlying 
12/// pair or text.
13/// # Typed getters
14/// `pest-typed-tree` will generate getters that return child nodes in AST.
15/// Consider following pest grammar:
16/// ```pest
17/// foo = ...
18/// bar = ...
19/// baz = {foo ~ bar?}
20/// ```
21/// Following APIs will be generated:
22/// ```rust,ignore
23/// impl Baz {
24///     fn get_foo(&self) -> Foo;
25///     fn get_bar(&self) -> Option<Bar>;
26/// }
27/// ```
28/// # Converting to enum
29/// If rule is just choice of several unique rules, `pest-typed-tree` will 
30/// generate `to_enum` function that returns enum with actual AST child.
31/// For following grammar:
32/// ```pest
33/// foo = ...
34/// bar = ...
35/// baz = ...
36/// quux = {foo | bar | baz}
37/// ```
38/// Following API will be available
39/// ```rust,ignore
40/// enum QuuxChildren {
41///     Foo(Foo),
42///     Bar(Bar),
43///     Baz(Baz),
44/// }
45/// impl Quux {
46///     fn to_enum(&self) -> QuuxChildren;
47/// }
48/// ```
49/// See test `complex.rs` for typed getters and enum conversion usage examples.
50
51#[proc_macro_derive(TypedTree, attributes(grammar))]
52pub fn derive_typed_tree(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
53    let input = syn::parse_macro_input!(stream as syn::DeriveInput);
54    if input.attrs.len() != 1 {
55        panic!(r#"derive input must contain exactly once #[grammar = "path/to/pest/grammar""#)
56    }
57    let attr = input.attrs.into_iter().next().unwrap();
58    let mut gram_path = attr.tokens.to_string();
59    // FIXME: dirty hack
60    gram_path = gram_path.trim_start_matches('=').trim().replace('"', "");
61    // following is copy-pasted from pest_generator
62    let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
63    let gram_path = std::path::Path::new(&root).join("src/").join(&gram_path);
64
65    let grammar = std::fs::read_to_string(&gram_path).unwrap_or_else(|err| {
66        panic!(
67            "failed to read parser grammar file {}: {}",
68            gram_path.display(),
69            err
70        )
71    });
72    generate::generate(grammar)
73}