Skip to main content

libperl_macrogen/
intern.rs

1use std::collections::HashMap;
2
3/// インターン済み文字列の識別子
4///
5/// Ord は intern 順 (= 入力の出現順) による決定的な全順序。HashSet/HashMap
6/// 由来の非決定順序を排除するためのソートキーに使う (推論の確定順が揺れると
7/// 型伝播キャッシュ経由で生成コードまで run ごとに揺れる)。
8#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
9pub struct InternedStr(u32);
10
11impl InternedStr {
12    /// 内部IDを取得(デバッグ用)
13    pub fn as_u32(self) -> u32 {
14        self.0
15    }
16}
17
18/// 文字列インターナー
19#[derive(Clone, Debug, Default)]
20pub struct StringInterner {
21    strings: Vec<String>,
22    map: HashMap<String, InternedStr>,
23}
24
25impl StringInterner {
26    /// 新しいインターナーを作成
27    pub fn new() -> Self {
28        Self {
29            strings: Vec::new(),
30            map: HashMap::new(),
31        }
32    }
33
34    /// 文字列をインターンし、IDを返す
35    pub fn intern(&mut self, s: &str) -> InternedStr {
36        if let Some(&id) = self.map.get(s) {
37            return id;
38        }
39        let id = InternedStr(self.strings.len() as u32);
40        self.strings.push(s.to_owned());
41        self.map.insert(s.to_owned(), id);
42        id
43    }
44
45    /// IDから文字列を取得
46    pub fn get(&self, id: InternedStr) -> &str {
47        &self.strings[id.0 as usize]
48    }
49
50    /// 文字列がインターン済みか検索(新規登録しない)
51    pub fn lookup(&self, s: &str) -> Option<InternedStr> {
52        self.map.get(s).copied()
53    }
54
55    /// インターン済み文字列の数を返す
56    pub fn len(&self) -> usize {
57        self.strings.len()
58    }
59
60    /// インターナーが空かどうか
61    pub fn is_empty(&self) -> bool {
62        self.strings.is_empty()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_intern_new_string() {
72        let mut interner = StringInterner::new();
73        let id1 = interner.intern("hello");
74        let id2 = interner.intern("world");
75
76        assert_ne!(id1, id2);
77        assert_eq!(interner.get(id1), "hello");
78        assert_eq!(interner.get(id2), "world");
79    }
80
81    #[test]
82    fn test_intern_same_string() {
83        let mut interner = StringInterner::new();
84        let id1 = interner.intern("hello");
85        let id2 = interner.intern("hello");
86
87        assert_eq!(id1, id2);
88        assert_eq!(interner.len(), 1);
89    }
90
91    #[test]
92    fn test_intern_empty_string() {
93        let mut interner = StringInterner::new();
94        let id = interner.intern("");
95        assert_eq!(interner.get(id), "");
96    }
97}