diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index c981a1f..66afaef 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -12,6 +12,7 @@
- [TokenStream](./procedural_macros/token_stream.md)
- [proc-macro2, syn, quote](./procedural_macros/proc_macro2_syn_quote.md)
- [Syntax Tree](./procedural_macros/syntax_tree.md)
+ - [Rust Syntax Tree](./procedural_macros/syntax_tree/rust_syntax_tree.md)
- [Span](./procedural_macros/span.md)
- [quote](./procedural_macros/quote.md)
- [Procedural Macro Crates](./procedural_macros/proc_macro_crates.md)
diff --git a/src/procedural_macros/syntax_tree.md b/src/procedural_macros/syntax_tree.md
index b3b7b13..36782a3 100644
--- a/src/procedural_macros/syntax_tree.md
+++ b/src/procedural_macros/syntax_tree.md
@@ -43,21 +43,22 @@ use syn::*;
fn main() {
// Parse the `pub` keyword
let input = quote::quote! {pub};
- let _token: Token![pub] = parse2(input).unwrap();
+ let t: Token![pub] = parse2(input).unwrap();
+ println!("{t:?}");
// Or use parse_quote!
- let _token: Token![pub] = parse_quote! {pub};
-
+ let t: Token![pub] = parse_quote! {pub};
+ println!("{t:?}");
// Parse the `struct` keyword
- let _token: Token![struct] = parse_quote! {struct};
-
+ let t: Token![struct] = parse_quote! {struct};
+ println!("{t:?}");
// Parse `+=`
- let _token: Token![+=] = parse_quote! {+=};
-
+ let t: Token![+=] = parse_quote! {+=};
+ println!("{t:?}");
// Parse `::`
- let _token: Token![::] = parse_quote! {::};
-
+ let t: Token![::] = parse_quote! {::};
+ println!("{t:?}");
// Error: `pub fn main() {}` is not a single token
- // let _token: Token![pub] = parse_quote! {pub fn main() {}};
+ // let t: Token![pub] = parse_quote! {pub fn main() {}};
}
```
@@ -72,7 +73,8 @@ mod kw{
}
fn main() {
- let _token: kw::div = parse_quote! {div};
+ let t: kw::div = parse_quote! {div};
+ println!("{t:?}");
}
```
@@ -82,11 +84,15 @@ fn main() {
use syn::*;
fn main() {
- let _node: ItemFn = parse_quote! {fn main() {println!("Hello, world!")}};
- let _node: ItemStruct = parse_quote! {struct MyStruct {field: i32}};
+ let node: ItemFn = parse_quote! {fn main() {println!("Hello, world!")}};
+ println!("{node:#?}");
+ let node: ItemStruct = parse_quote! {struct MyStruct {field: i32}};
+ println!("{node:#?}");
// `syn::DeriveInput` is a syntax tree node that represents any valid input to a derive macro.
- let _node: DeriveInput = parse_quote! {#[derive(Debug)] struct MyStruct {field: i32}};
+ let node: DeriveInput = parse_quote! {#[derive(Debug)] struct MyStruct {field: i32}};
+ println!("{node:#?}");
}
+
```
## Parsing a Custom Syntax Tree Node
@@ -104,9 +110,14 @@ use syn::{
parse::{ParseStream, Parser},
*,
};
+// We define custom keywords in a `kw` or `keywords` module by convention.
+mod kw {
+ syn::custom_keyword!(div);
+}
fn main() {
let input = quote! {
+ // Tip: try modifying it to an invalid div element and see the result.
"Hello World"
};
// parse::Parser::parse2(|input: ParseStream| -> Result<()> { todo!() }, input).unwrap();
@@ -118,23 +129,26 @@ fn main() {
// `<`
input.parse::()?;
// `div`
- input.parse::()?;
+ input.parse::()?;
// `>`
input.parse::]>()?;
// `"Hello World"`
- input.parse::()?;
+ let str = input.parse::()?;
// `<`
input.parse::()?;
// `/`
input.parse::()?;
// `div`
- input.parse::()?;
+ input.parse::()?;
// `>`
input.parse::]>()?;
+ println!("{str:?}");
+ println!("Done!");
Ok(())
};
parser.parse2(input).unwrap();
}
+
```
### Defining a custom syntax tree node type by implementing the `syn::parse::Parse` trait
diff --git a/src/procedural_macros/syntax_tree/rust_syntax_tree.md b/src/procedural_macros/syntax_tree/rust_syntax_tree.md
new file mode 100644
index 0000000..bf21aa0
--- /dev/null
+++ b/src/procedural_macros/syntax_tree/rust_syntax_tree.md
@@ -0,0 +1,66 @@
+# Rust Syntax Tree
+
+We've learned how to implement our own syntax tree nodes for virtually any DSL. But if we only want to work with valid Rust code using `syn`, we don't need to implement any custom nodes. The good news is that you don't even need to learn anything beyond what this tutorial has already covered about `syn` to handle concrete Rust-code-related tasks.
+
+Here is a simple example to get you started.
+
+# Ok Type Extraction
+
+Let's implement a function that parses a Rust function and extracts the inner success type (Ok type) from its Result return type and prints it to the console.
+
+> [!TIP]
+>
+> You don't need to read the following implementation. Just run it and observe the output.
+
+```rust,editable,compile_fail
+use proc_macro2::TokenStream;
+use syn::ItemFn;
+
+fn main() {
+ ok_type(syn::parse_quote! {fn foo() -> i32 {}});
+ ok_type(syn::parse_quote! {fn foo() -> Result {}});
+ ok_type(syn::parse_quote! {fn add(a:u32,b:u32) -> Result {}});
+}
+
+fn ok_type(item_fn: TokenStream) {
+ println!(r#"fn: "{}""#, item_fn.to_string());
+ let item_fn: ItemFn = syn::parse_quote! {#item_fn};
+ let syn::ReturnType::Type(_, return_type) = item_fn.sig.output else {
+ println!("Ok Type Unknown.");
+ return;
+ };
+ let syn::Type::Path(return_type) = return_type.as_ref() else {
+ println!("Ok Type Unknown.");
+ return;
+ };
+ let syn::PathArguments::AngleBracketed(generic_args) =
+ &return_type.path.segments.first().unwrap().arguments
+ else {
+ println!("Ok Type Unknown.");
+ return;
+ };
+ let syn::GenericArgument::Type(syn::Type::Path(ok_type)) = generic_args.args.first().unwrap()
+ else {
+ println!("Ok Type Unknown.");
+ return;
+ };
+ println!("Ok type is: {}", ok_type.path.get_ident().unwrap());
+}
+```
+
+The above code uses a few `syn` nodes. How do you know in advance which nodes you need to implement your own `ok_type` function? The answer is: no, you don't need to know anything.
+
+You just need to start at the very top node — `ItemFn` for this example. Print its debug output, then figure out the "happy path" that leads to your task target.
+
+For example:
+
+```rust,editable,compile_fail
+use syn::ItemFn;
+
+fn main() {
+ let item_fn: ItemFn = syn::parse_quote! {fn foo() -> Result {}};
+ println!("{item_fn:#?}");
+}
+```
+
+Then follow the "happy path" all the way to your target — `i32` in this example. The golden rule is: focus only on the "happy-path".