[Rust] syn: Rust ์ฝ”๋“œ ํŒŒ์„œ

syn์€ Rust ์ฝ”๋“œ ์ž์ฒด์˜ ํŒŒ์‹ฑ์„ ์œ„ํ•œ ์„œ๋“œํŒŒํ‹ฐ crate๋‹ค.

์ฝ”๋“œ๋ฅผ ๋Ÿฐํƒ€์ž„์ด๋‚˜ ์ปดํŒŒ์ผํƒ€์ž„์— ๋ถ„์„ํ•ด์„œ ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋‚˜ ์ œ๋„ˆ๋ ˆ์ด์…˜์„ ์ˆ˜ํ–‰ํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ๋–„ ์œ ์šฉํ•˜๋‹ค.
ํŠนํžˆ, procedural macro์˜ ๋ณด์กฐ๋„๊ตฌ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹๋‹ค.




์„ค์น˜

๋ฒ„์ „์€ 2๋กœ ๋ฐ•๊ณ , ์›ํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ ํ”ผ์ณ ํ”Œ๋ž˜๊ทธ์— ๋„ฃ์–ด์ค€๋‹ค.

[dependencies]
syn = { version = "2", features = ["full"] }

๊ทธ๋ฆฌ๊ณ  ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ข€ ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋Š”๋ฐ, full์€ ์ง„์งœ full์ด ์•„๋‹ˆ๋‹ค.
extra-traits ๊ฐ™์€ ๋ช‡๋ช‡ ํ”Œ๋ž˜๊ทธ๋Š” ์†์œผ๋กœ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค. ์ €๊ฑธ ๋„ฃ์–ด์•ผ Debug๊ฐ€ ๋œ๋‹ค.




๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์€ ๋ณ„๊ฒŒ ์—†๋‹ค.
๊ทธ๋ƒฅ ์ฝ”๋“œ ๋„ฃ๊ณ  ๋Œ๋ฆฌ๋ฉด AST๊ฐ€ ๋ฟ… ๋‚˜์˜ค๋Š” ๊ฒƒ์ด๋‹ค.

const SOME_CODE: &str = r#"
struct Foo {
    a: i32,
    b: String,
}

fn main() {
    println!("Hello, World!");
}
"#;

fn main() {
    let ast = syn::parse_file(SOME_CODE).unwrap();

    ast.items.iter().for_each(|item| match item {
        syn::Item::Fn(f) => {
            println!("Function: {}", f.sig.ident);
        }
        syn::Item::Struct(s) => {
            println!("Struct: {}", s.ident);

            s.fields.iter().for_each(|field| {
                println!("Field: {}", field.ident.as_ref().unwrap());
            });
        }
        _ => {}
    });
}

๊ทธ๋ ‡๋‹ค.
์ด ๊ฒฝ์šฐ์—๋Š” ์ „์ฒด ์ฝ”๋“œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŒŒ์‹ฑ์„ ํ–ˆ๋Š”๋ฐ

parse_str์„ ์“ฐ๊ณ  ์–ด๋–ค ํƒ€์ž…์œผ๋กœ ๋ฐ›์„์ง€๋ฅผ ์ง€์ •ํ•˜๋ฉด ํŠน์ • ํ‘œํ˜„์— ๋Œ€ํ•ด์„œ๋งŒ ๋ถ€๋ถ„์ ์œผ๋กœ ํŒŒ์‹ฑ์„ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
์•„๋ž˜๋Š” ๋‹จ์ผ ํ‘œํ˜„์‹์„ ํŒŒ์‹ฑํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋‹ค.

use syn::Expr;

const SOME_CODE: &str = r#"
(1 + 20).to_string()
"#;

fn main() {
    let ast: Expr = syn::parse_str(SOME_CODE).unwrap();

    match ast {
        Expr::MethodCall(method_call) => {
            println!("Method call: {:?}", method_call.method);

            match *method_call.receiver {
                Expr::Paren(paren) => match *paren.expr {
                    Expr::Binary(binary) => {
                        if let Expr::Lit(lit) = *binary.left {
                            if let syn::Lit::Int(int) = lit.lit {
                                println!("Left: {:?}", int.base10_digits());
                            }
                        }

                        if let Expr::Lit(lit) = *binary.right {
                            if let syn::Lit::Int(int) = lit.lit {
                                println!("Right: {:?}", int.base10_digits());
                            }
                        }
                    }
                    _ => {
                        panic!("Not a binary");
                    }
                },
                _ => {
                    panic!("Not a paren");
                }
            }
        }
        _ => {
            panic!("Not a method call");
        }
    }
}

์ž˜ ๋œ๋‹ค.




proc ๋งคํฌ๋กœ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฐ•๋ ฅํ•œ ์  ์ค‘ ํ•˜๋‚˜๋Š”, TokenStream์— ๋Œ€ํ•ด์„œ๋„ ์“ธ๋งŒํ•˜๊ฒŒ ์žฌ๊ฐ€๊ณต ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ค€๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

procmacro๋กœ ๋‡Œ์ ˆ์„ ํ•ด๋ณธ ์‚ฌ๋žŒ์€ ์•Œ๊ฒ ์ง€๋งŒ, TokenStream์„ ํ†ตํ•ด์„œ ์–ด๋А์ •๋„ ์ •๋ฆฌ๋œ ํ˜•ํƒœ๋กœ ์ฝ”๋“œ ํ˜•ํƒœ๋ฅผ ๋‚ด๋ ค์ฃผ๊ธด ํ•˜์ง€๋งŒ, ์ง„์งœ ํŒŒ์‹ฑ๋œ AST๊ฐ€ ์•„๋‹ˆ๋ผ lexing๋งŒ ์ฒ˜๋ฆฌ๋œ ๊ฑฐ์˜ ์ˆœ์ˆ˜ํ•œ Token ๋ญ‰์น˜๋ผ์„œ ์‘์šฉํ•˜๊ธฐ๊ฐ€ ๊นŒ๋‹ค๋กญ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด foo: Option ๊ฐ™์€๊ฒŒ TokenStream์— ๋“ค์–ด์˜ค๋ฉด ์ง„์งœ [Ident:"foo", Ident:'Option", "<", "i32", ">"] ์ด๋”ด ์‹์œผ๋กœ ๋‚˜์˜จ๋‹ค.

syn์˜ ํŒŒ์‹ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๊ฑธ ๊ฐ„๋‹จํ•˜๊ฒŒ AST ์ˆ˜์ค€๊นŒ์ง€ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด parse_macro_input ๋งคํฌ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์ด๊ฑด ๊ตฌ์กฐ์ฒด์— ๋Œ€ํ•ด์„œ ์‚ฌ์šฉํ•  procmacro๋‹ค.

use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemStruct};

#[proc_macro_attribute]
pub fn my_macro(_attribute: TokenStream, item: TokenStream) -> TokenStream {
    let copied = item.clone();

    // Parse the input tokens into a syntax tree
    let ast = parse_macro_input!(item as ItemStruct);

    println!("{:?}", ast);

    // Hand the output tokens back to the compiler
    TokenStream::from(copied)
}

๊ทธ๋ฆฌ๊ณ  ๊ตฌ์กฐ์ฒด์— ๋‹ฌ์•„์„œ ๋Œ๋ ค๋ณด๋ฉด

์ ๋‹นํžˆ ์˜ˆ์˜๊ฒŒ ํŒŒ์‹ฑํ•ด์ค„ ๊ฒƒ์ด๋‹ค.

๊ทธ๋Ÿผ ๋ง›๋ณด๊ณ  ์ฆ๊ธฐ๋ฉด ๋œ๋‹ค.