Skip to main content

libperl_macrogen/
rust_decl.rs

1//! Rust宣言パーサー
2//!
3//! bindgenが生成したRustコードから宣言を抽出する。
4//! syn crateを使用して正確にパースする。
5
6use std::collections::{HashMap, HashSet};
7use std::fs;
8use std::io;
9use std::path::Path;
10
11use syn::{Item, Type, FnArg, Pat, ReturnType, Fields, Visibility};
12use quote::ToTokens;
13
14use crate::unified_type::UnifiedType;
15
16/// Rust定数
17#[derive(Debug, Clone)]
18pub struct RustConst {
19    pub name: String,
20    pub ty: String,
21    pub uty: UnifiedType,
22}
23
24/// Rust関数パラメータ
25#[derive(Debug, Clone)]
26pub struct RustParam {
27    pub name: String,
28    pub ty: String,
29    pub uty: UnifiedType,
30}
31
32/// Rust関数
33#[derive(Debug, Clone)]
34pub struct RustFn {
35    pub name: String,
36    pub params: Vec<RustParam>,
37    pub ret_ty: Option<String>,
38    pub uret_ty: Option<UnifiedType>,
39}
40
41/// Rust構造体フィールド
42#[derive(Debug, Clone)]
43pub struct RustField {
44    pub name: String,
45    pub ty: String,
46    pub uty: UnifiedType,
47}
48
49/// Rust構造体
50#[derive(Debug, Clone)]
51pub struct RustStruct {
52    pub name: String,
53    pub fields: Vec<RustField>,
54}
55
56/// Rust型エイリアス
57#[derive(Debug, Clone)]
58pub struct RustTypeAlias {
59    pub name: String,
60    pub ty: String,
61    pub uty: UnifiedType,
62}
63
64/// Rust宣言辞書
65#[derive(Debug, Default)]
66pub struct RustDeclDict {
67    pub consts: HashMap<String, RustConst>,
68    pub fns: HashMap<String, RustFn>,
69    pub structs: HashMap<String, RustStruct>,
70    pub types: HashMap<String, RustTypeAlias>,
71    pub enums: HashSet<String>,
72    /// 全 extern static 変数名の集合
73    pub statics: HashSet<String>,
74    /// 配列型の extern static 変数名の集合
75    pub static_arrays: HashSet<String>,
76    /// static 変数の型マップ (名前 → Rust 型文字列)
77    /// 例: "PL_c9_utf8_dfa_tab" → "[U8; 0usize]"
78    pub static_types: HashMap<String, String>,
79    /// ビットフィールドのメソッド名集合(構造体名 → メソッド名セット)
80    pub bitfield_methods: HashMap<String, HashSet<String>>,
81    /// ビットフィールドアクセサ getter の戻り値型
82    /// (構造体名, メソッド名) → 戻り値型文字列
83    /// 例: ("op", "op_type") → "U16"
84    pub bitfield_method_types: HashMap<(String, String), String>,
85}
86
87impl RustDeclDict {
88    /// 新しい辞書を作成
89    pub fn new() -> Self {
90        Self::default()
91    }
92
93    /// ファイルからパース
94    pub fn parse_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
95        let content = fs::read_to_string(path)?;
96        Ok(Self::parse(&content))
97    }
98
99    /// 全 struct / typedef / enum 名を StringInterner に intern する。
100    ///
101    /// bindgen が生成する `pmop__bindgen_ty_2` のような名前は通常 C ヘッダ側の
102    /// パースでは登場しないため interner に未登録のまま。後段の型推論で
103    /// `from_apidoc_string("pmop__bindgen_ty_2", interner)` のように
104    /// `interner.lookup()` を経由する経路は immutable な interner しか見ないので、
105    /// パース直後に明示的に intern しておく必要がある。
106    pub fn intern_names(&self, interner: &mut crate::intern::StringInterner) {
107        for name in self.structs.keys() {
108            interner.intern(name);
109        }
110        for name in self.types.keys() {
111            interner.intern(name);
112        }
113        for name in &self.enums {
114            interner.intern(name);
115        }
116    }
117
118    /// 文字列からパース
119    pub fn parse(content: &str) -> Self {
120        let mut dict = Self::new();
121
122        // synでパース
123        let file = match syn::parse_file(content) {
124            Ok(f) => f,
125            Err(e) => {
126                eprintln!("Warning: Failed to parse Rust file: {}", e);
127                return dict;
128            }
129        };
130
131        // 各アイテムを処理
132        for item in file.items {
133            dict.process_item(&item);
134        }
135
136        dict
137    }
138
139    /// アイテムを処理
140    fn process_item(&mut self, item: &Item) {
141        match item {
142            Item::Const(item_const) => {
143                if Self::is_pub(&item_const.vis) {
144                    let name = item_const.ident.to_string();
145                    let uty = UnifiedType::from_syn_type(&item_const.ty);
146                    let ty = Self::type_to_string(&item_const.ty);
147                    self.consts.insert(name.clone(), RustConst { name, ty, uty });
148                }
149            }
150            Item::Type(item_type) => {
151                if Self::is_pub(&item_type.vis) {
152                    let name = item_type.ident.to_string();
153                    let uty = UnifiedType::from_syn_type(&item_type.ty);
154                    let ty = Self::type_to_string(&item_type.ty);
155                    self.types.insert(name.clone(), RustTypeAlias { name, ty, uty });
156                }
157            }
158            Item::Struct(item_struct) => {
159                if Self::is_pub(&item_struct.vis) {
160                    let name = item_struct.ident.to_string();
161                    let fields = Self::extract_fields(&item_struct.fields);
162                    self.structs.insert(name.clone(), RustStruct { name, fields });
163                }
164            }
165            Item::Union(item_union) => {
166                if Self::is_pub(&item_union.vis) {
167                    let name = item_union.ident.to_string();
168                    let fields = Self::extract_fields(&Fields::Named(item_union.fields.clone()));
169                    self.structs.insert(name.clone(), RustStruct { name, fields });
170                }
171            }
172            Item::Fn(item_fn) => {
173                if Self::is_pub(&item_fn.vis) {
174                    if let Some(rust_fn) = Self::extract_fn(&item_fn.sig) {
175                        self.fns.insert(rust_fn.name.clone(), rust_fn);
176                    }
177                }
178            }
179            Item::ForeignMod(foreign_mod) => {
180                // extern "C" { ... } ブロック内の関数と static 変数
181                for foreign_item in &foreign_mod.items {
182                    match foreign_item {
183                        syn::ForeignItem::Fn(fn_item) => {
184                            if Self::is_pub(&fn_item.vis) {
185                                if let Some(rust_fn) = Self::extract_fn(&fn_item.sig) {
186                                    self.fns.insert(rust_fn.name.clone(), rust_fn);
187                                }
188                            }
189                        }
190                        syn::ForeignItem::Static(static_item) => {
191                            let name = static_item.ident.to_string();
192                            let ty_str = Self::type_to_string(&static_item.ty);
193                            self.statics.insert(name.clone());
194                            if ty_str.starts_with("[") {
195                                self.static_arrays.insert(name.clone());
196                            }
197                            self.static_types.insert(name, ty_str);
198                        }
199                        _ => {}
200                    }
201                }
202            }
203            Item::Enum(item_enum) => {
204                if Self::is_pub(&item_enum.vis) {
205                    self.enums.insert(item_enum.ident.to_string());
206                }
207            }
208            Item::Impl(item_impl) => {
209                // ビットフィールドのゲッターメソッドを収集
210                let struct_name = Self::type_to_string(&item_impl.self_ty);
211                for impl_item in &item_impl.items {
212                    if let syn::ImplItem::Fn(method) = impl_item {
213                        let body_str = method.block.to_token_stream().to_string();
214                        if Self::has_self_receiver(&method.sig)
215                            && body_str.contains("_bitfield_")
216                            && method.sig.inputs.len() == 1  // getter: &self のみ
217                        {
218                            let method_name = method.sig.ident.to_string();
219                            // 戻り値型を捕獲(型推論で利用)。戻り値なし getter は対象外。
220                            if let syn::ReturnType::Type(_, ty) = &method.sig.output {
221                                let ret_ty = Self::type_to_string(ty);
222                                self.bitfield_method_types
223                                    .insert((struct_name.clone(), method_name.clone()), ret_ty);
224                            }
225                            self.bitfield_methods
226                                .entry(struct_name.clone())
227                                .or_default()
228                                .insert(method_name);
229                        }
230                    }
231                }
232            }
233            _ => {}
234        }
235    }
236
237    /// 可視性がpubかどうか
238    fn is_pub(vis: &Visibility) -> bool {
239        matches!(vis, Visibility::Public(_))
240    }
241
242    /// 関数シグネチャが &self レシーバを持つかどうか
243    fn has_self_receiver(sig: &syn::Signature) -> bool {
244        sig.inputs.first().is_some_and(|arg| matches!(arg, FnArg::Receiver(_)))
245    }
246
247    /// 型を文字列に変換
248    fn type_to_string(ty: &Type) -> String {
249        ty.to_token_stream().to_string()
250    }
251
252    /// 構造体フィールドを抽出
253    fn extract_fields(fields: &Fields) -> Vec<RustField> {
254        let mut result = Vec::new();
255
256        match fields {
257            Fields::Named(named) => {
258                for field in &named.named {
259                    if Self::is_pub(&field.vis) {
260                        if let Some(ident) = &field.ident {
261                            let uty = UnifiedType::from_syn_type(&field.ty);
262                            let ty = Self::type_to_string(&field.ty);
263                            result.push(RustField {
264                                name: ident.to_string(),
265                                ty,
266                                uty,
267                            });
268                        }
269                    }
270                }
271            }
272            Fields::Unnamed(unnamed) => {
273                for (i, field) in unnamed.unnamed.iter().enumerate() {
274                    if Self::is_pub(&field.vis) {
275                        let uty = UnifiedType::from_syn_type(&field.ty);
276                        let ty = Self::type_to_string(&field.ty);
277                        result.push(RustField {
278                            name: format!("{}", i),
279                            ty,
280                            uty,
281                        });
282                    }
283                }
284            }
285            Fields::Unit => {}
286        }
287
288        result
289    }
290
291    /// 関数シグネチャを抽出
292    fn extract_fn(sig: &syn::Signature) -> Option<RustFn> {
293        let name = sig.ident.to_string();
294
295        let mut params = Vec::new();
296        for arg in &sig.inputs {
297            match arg {
298                FnArg::Receiver(_) => {
299                    // self, &self, &mut self はスキップ
300                }
301                FnArg::Typed(pat_type) => {
302                    let param_name = match pat_type.pat.as_ref() {
303                        Pat::Ident(pat_ident) => pat_ident.ident.to_string(),
304                        _ => "_".to_string(),
305                    };
306                    let uty = UnifiedType::from_syn_type(&pat_type.ty);
307                    let param_ty = Self::type_to_string(&pat_type.ty);
308                    params.push(RustParam {
309                        name: param_name,
310                        ty: param_ty,
311                        uty,
312                    });
313                }
314            }
315        }
316
317        let (ret_ty, uret_ty) = match &sig.output {
318            ReturnType::Default => (None, None),
319            ReturnType::Type(_, ty) => (
320                Some(Self::type_to_string(ty)),
321                Some(UnifiedType::from_syn_type(ty)),
322            ),
323        };
324
325        Some(RustFn {
326            name,
327            params,
328            ret_ty,
329            uret_ty,
330        })
331    }
332
333    /// 統計情報を取得
334    pub fn stats(&self) -> RustDeclStats {
335        RustDeclStats {
336            const_count: self.consts.len(),
337            fn_count: self.fns.len(),
338            struct_count: self.structs.len(),
339            type_count: self.types.len(),
340        }
341    }
342
343    /// THX依存関数の名前を取得
344    ///
345    /// 第一引数が *mut PerlInterpreter を含む関数を返す
346    pub fn thx_functions(&self) -> std::collections::HashSet<String> {
347        self.fns.iter()
348            .filter(|(_, f)| {
349                f.params.first()
350                    .map(|p| p.ty.contains("PerlInterpreter"))
351                    .unwrap_or(false)
352            })
353            .map(|(name, _)| name.clone())
354            .collect()
355    }
356
357    /// 辞書をダンプ
358    pub fn dump(&self) -> String {
359        let mut result = String::new();
360
361        result.push_str("=== Constants ===\n");
362        let mut consts: Vec<_> = self.consts.values().collect();
363        consts.sort_by_key(|c| &c.name);
364        for c in consts {
365            result.push_str(&format!("  {}: {}\n", c.name, c.ty));
366        }
367
368        result.push_str("\n=== Type Aliases ===\n");
369        let mut types: Vec<_> = self.types.values().collect();
370        types.sort_by_key(|t| &t.name);
371        for t in types {
372            result.push_str(&format!("  {} = {}\n", t.name, t.ty));
373        }
374
375        result.push_str("\n=== Functions ===\n");
376        let mut fns: Vec<_> = self.fns.values().collect();
377        fns.sort_by_key(|f| &f.name);
378        for f in fns {
379            let params: Vec<_> = f.params.iter().map(|p| format!("{}: {}", p.name, p.ty)).collect();
380            let ret = f.ret_ty.as_deref().unwrap_or("()");
381            result.push_str(&format!("  fn {}({}) -> {}\n", f.name, params.join(", "), ret));
382        }
383
384        result.push_str("\n=== Structs ===\n");
385        let mut structs: Vec<_> = self.structs.values().collect();
386        structs.sort_by_key(|s| &s.name);
387        for s in structs {
388            result.push_str(&format!("  struct {} {{\n", s.name));
389            for f in &s.fields {
390                result.push_str(&format!("    {}: {},\n", f.name, f.ty));
391            }
392            result.push_str("  }\n");
393        }
394
395        result
396    }
397
398    /// 名前で定数を検索
399    pub fn lookup_const(&self, name: &str) -> Option<&RustConst> {
400        self.consts.get(name)
401    }
402
403    /// 名前で関数を検索
404    pub fn lookup_fn(&self, name: &str) -> Option<&RustFn> {
405        self.fns.get(name)
406    }
407
408    /// 名前で構造体を検索
409    pub fn lookup_struct(&self, name: &str) -> Option<&RustStruct> {
410        self.structs.get(name)
411    }
412
413    /// 名前で型エイリアスを検索
414    pub fn lookup_type(&self, name: &str) -> Option<&RustTypeAlias> {
415        self.types.get(name)
416    }
417}
418
419/// 統計情報
420#[derive(Debug)]
421pub struct RustDeclStats {
422    pub const_count: usize,
423    pub fn_count: usize,
424    pub struct_count: usize,
425    pub type_count: usize,
426}
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431
432    #[test]
433    fn test_parse_const() {
434        let dict = RustDeclDict::parse("pub const FOO: u32 = 42;");
435        assert_eq!(dict.consts.len(), 1);
436        let c = dict.consts.get("FOO").unwrap();
437        assert_eq!(c.name, "FOO");
438        assert_eq!(c.ty, "u32");
439    }
440
441    #[test]
442    fn test_parse_const_array() {
443        let dict = RustDeclDict::parse("pub const MSG: &[u8; 10] = b\"(unknown)\\0\";");
444        let c = dict.consts.get("MSG").unwrap();
445        assert_eq!(c.name, "MSG");
446        assert_eq!(c.ty, "& [u8 ; 10]");
447    }
448
449    #[test]
450    fn test_parse_type_alias() {
451        let dict = RustDeclDict::parse("pub type Size = ::std::os::raw::c_ulong;");
452        let t = dict.types.get("Size").unwrap();
453        assert_eq!(t.name, "Size");
454        assert_eq!(t.ty, ":: std :: os :: raw :: c_ulong");
455    }
456
457    #[test]
458    fn test_parse_fn() {
459        let dict = RustDeclDict::parse(r#"
460            extern "C" {
461                pub fn foo(x: i32, y: *mut u8) -> bool;
462            }
463        "#);
464        let f = dict.fns.get("foo").unwrap();
465        assert_eq!(f.name, "foo");
466        assert_eq!(f.params.len(), 2);
467        assert_eq!(f.params[0].name, "x");
468        assert_eq!(f.params[0].ty, "i32");
469        assert_eq!(f.params[1].name, "y");
470        assert_eq!(f.params[1].ty, "* mut u8");
471        assert_eq!(f.ret_ty, Some("bool".to_string()));
472    }
473
474    #[test]
475    fn test_parse_fn_no_return() {
476        let dict = RustDeclDict::parse(r#"
477            extern "C" {
478                pub fn bar(x: i32);
479            }
480        "#);
481        let f = dict.fns.get("bar").unwrap();
482        assert_eq!(f.name, "bar");
483        assert_eq!(f.ret_ty, None);
484    }
485
486    #[test]
487    fn test_parse_struct() {
488        let content = r#"
489pub struct Point {
490    pub x: i32,
491    pub y: i32,
492}
493"#;
494        let dict = RustDeclDict::parse(content);
495        let s = dict.structs.get("Point").unwrap();
496        assert_eq!(s.name, "Point");
497        assert_eq!(s.fields.len(), 2);
498        assert_eq!(s.fields[0].name, "x");
499        assert_eq!(s.fields[0].ty, "i32");
500    }
501
502    #[test]
503    fn test_parse_struct_with_option() {
504        let content = r#"
505pub struct Test {
506    pub callback: ::std::option::Option<
507        unsafe extern "C" fn(x: i32) -> i32,
508    >,
509}
510"#;
511        let dict = RustDeclDict::parse(content);
512        let s = dict.structs.get("Test").unwrap();
513        assert_eq!(s.name, "Test");
514        assert_eq!(s.fields.len(), 1);
515        assert_eq!(s.fields[0].name, "callback");
516        // synのto_token_stream()は型を正しくパースする
517        assert!(s.fields[0].ty.contains("Option"));
518        assert!(s.fields[0].ty.contains("fn"));
519        assert!(s.fields[0].ty.ends_with(">"), "Type should end with >");
520    }
521
522    #[test]
523    fn test_parse_struct_with_generics() {
524        let content = r#"
525pub struct Wrapper<T> {
526    pub value: T,
527}
528"#;
529        let dict = RustDeclDict::parse(content);
530        let s = dict.structs.get("Wrapper").unwrap();
531        assert_eq!(s.name, "Wrapper");
532    }
533}