Skip to main content

libperl_macrogen/
c_fn_decl.rs

1//! C 関数宣言辞書
2//!
3//! C ヘッダファイルから収集した関数宣言を管理する。
4//! `RustDeclDict` と対になる構造で、将来的な比較や検証に活用できる。
5
6use std::collections::HashMap;
7
8use crate::intern::InternedStr;
9
10/// C 関数パラメータ
11#[derive(Debug, Clone)]
12pub struct CParam {
13    /// パラメータ名(匿名の場合は None)
14    pub name: Option<InternedStr>,
15    /// パラメータの型(文字列表現)
16    pub ty: String,
17}
18
19/// C 関数宣言
20#[derive(Debug, Clone)]
21pub struct CFnDecl {
22    /// 関数名
23    pub name: InternedStr,
24    /// パラメータリスト
25    pub params: Vec<CParam>,
26    /// 戻り値の型(文字列表現)
27    pub ret_ty: String,
28    /// THX 依存性(pTHX_ または pTHX がパラメータに含まれる)
29    pub is_thx: bool,
30    /// ターゲットディレクトリで宣言されたか
31    pub is_target: bool,
32    /// 宣言の場所(ファイルパス:行番号)
33    pub location: Option<String>,
34}
35
36/// C 関数宣言辞書
37#[derive(Debug, Default)]
38pub struct CFnDeclDict {
39    /// 関数名 → 関数宣言のマッピング
40    pub fns: HashMap<InternedStr, CFnDecl>,
41}
42
43impl CFnDeclDict {
44    /// 新しい辞書を作成
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// 関数宣言を追加
50    pub fn insert(&mut self, decl: CFnDecl) {
51        self.fns.insert(decl.name, decl);
52    }
53
54    /// 関数が存在するか
55    pub fn contains(&self, name: InternedStr) -> bool {
56        self.fns.contains_key(&name)
57    }
58
59    /// 関数宣言を取得
60    pub fn get(&self, name: InternedStr) -> Option<&CFnDecl> {
61        self.fns.get(&name)
62    }
63
64    /// 関数が THX 依存かどうか
65    pub fn is_thx_dependent(&self, name: InternedStr) -> bool {
66        self.fns.get(&name).is_some_and(|d| d.is_thx)
67    }
68
69    /// THX 依存関数の数
70    pub fn thx_count(&self) -> usize {
71        self.fns.values().filter(|d| d.is_thx).count()
72    }
73
74    /// 登録された関数数
75    pub fn len(&self) -> usize {
76        self.fns.len()
77    }
78
79    /// 空かどうか
80    pub fn is_empty(&self) -> bool {
81        self.fns.is_empty()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use crate::intern::StringInterner;
89
90    #[test]
91    fn test_c_fn_decl_dict_basic() {
92        let mut interner = StringInterner::new();
93        let mut dict = CFnDeclDict::new();
94
95        let name = interner.intern("Perl_foo");
96        let decl = CFnDecl {
97            name,
98            params: vec![
99                CParam { name: None, ty: "PerlInterpreter *".to_string() },
100                CParam { name: Some(interner.intern("x")), ty: "int".to_string() },
101            ],
102            ret_ty: "void".to_string(),
103            is_thx: true,
104            is_target: true,
105            location: Some("proto.h:123".to_string()),
106        };
107
108        dict.insert(decl);
109
110        assert!(dict.contains(name));
111        assert!(dict.is_thx_dependent(name));
112        assert_eq!(dict.len(), 1);
113        assert_eq!(dict.thx_count(), 1);
114
115        let retrieved = dict.get(name).unwrap();
116        assert_eq!(retrieved.params.len(), 2);
117        assert_eq!(retrieved.ret_ty, "void");
118    }
119
120    #[test]
121    fn test_c_fn_decl_dict_non_thx() {
122        let mut interner = StringInterner::new();
123        let mut dict = CFnDeclDict::new();
124
125        let name = interner.intern("strlen");
126        let decl = CFnDecl {
127            name,
128            params: vec![
129                CParam { name: Some(interner.intern("s")), ty: "const char *".to_string() },
130            ],
131            ret_ty: "size_t".to_string(),
132            is_thx: false,
133            is_target: false,
134            location: Some("string.h:100".to_string()),
135        };
136
137        dict.insert(decl);
138
139        assert!(dict.contains(name));
140        assert!(!dict.is_thx_dependent(name));
141        assert_eq!(dict.thx_count(), 0);
142    }
143}