Rust で Intern されたASTを扱う方法(3)

同じようなコードはマクロを使うことで繰り返し書かずに済むようにできます。
こんかいはlocal_thread_intern_arenaマクロを作って使いまわしできるようにしてみました。
PartialEq の実装をDerivingで自動的に実装できると良いと思うのですがそれはまた別の機会にやってみたいと思います。

定義の仕方は、以下のようにマクロを呼び出します:

```Rust
thread_local_intern_arena!(ARENA: Arena -> Exp);
```

こうすることでARENAという名前のローカルスレッドな変数にExp型用のArena型のアリーナが作られます。使い方は以下のようにHash,Eq,PartialEq,AddressEqを実装し、internで同じアドレスを割り当てて使います:

```Rust
#[derive(Debug,Hash,Eq)]
enum Exp {
    Int(i8),
    Add(EXP,EXP),
}
type EXP = &'static Exp;// boxのアドレスになってる。
impl AddressEq for Exp {}
impl PartialEq for Exp {
    fn eq(&self,other:&Exp) -> bool {
        self.addr_eq(other) ||
        match (self,other) {
            (Exp::Int(i1),Exp::Int(i2)) => *i1==*i2,
            (Exp::Add(e1,e2),Exp::Add(e3,e4)) => e1.addr_eq(e3) && e2.addr_eq(e4),
            (_, _) => false
        }
    }
}
thread_local_intern_arena!(ARENA: Arena -> Exp);
fn int(i:i8) -> EXP {
    intern(Exp::Int(i))
}
fn add(e1:EXP,e2:EXP) -> EXP {
    intern(Exp::Add(e1,e2))
}
fn eval(e:EXP) -> i8{
    match e {
        Exp::Int(i) => *i,
        Exp::Add(e1,e2) => eval(e1)+eval(e2)
    }
}
```

全ソースは以下のようになります:

```Rust
fn leak<A>(a:A) -> &'static A {
    Box::leak(Box::new(a))
}
trait AddressEq {
    fn addr_eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}
macro_rules! thread_local_intern_arena {
    ($(#[$attr:meta])* $vis:vis $name:ident: $t:ident -> $t2:ty) => {
        use rustc_hash::FxHashMap;
        use std::cell::RefCell;
        struct $t (FxHashMap<&'static $t2, &'static $t2>);
        impl $t {
            fn new()->$t {
                $t(FxHashMap::default())
            }
            fn intern(&mut self,e:$t2) -> &'static $t2 {
                let pe = leak(e);
                if let Some(x) = self.0.get(pe) {
                    unsafe {core::ptr::drop_in_place(pe as *const _ as *mut $t2)}
                    return *x;
                }
                self.0.insert(pe, pe);
                pe
            }
        }    
        impl Drop for $t {
            fn drop(&mut self) {
                for (e, _) in self.0.iter() {
                    unsafe {core::ptr::drop_in_place(e as *const _ as *mut $t2)}
                }
            }
        }
        fn intern(e:$t2) -> &'static $t2 {
            $name.with(|arena|{arena.borrow_mut().intern(e)})
        }
        thread_local!($(#[$attr])* $vis static $name: RefCell<$t> = RefCell::new($t::new()));

    }
}

#[derive(Debug,Hash,Eq)]
enum Exp {
    Int(i8),
    Add(EXP,EXP),
}
type EXP = &'static Exp;// boxのアドレスになってる。
impl AddressEq for Exp {}
impl PartialEq for Exp {
    fn eq(&self,other:&Exp) -> bool {
        self.addr_eq(other) ||
        match (self,other) {
            (Exp::Int(i1),Exp::Int(i2)) => *i1==*i2,
            (Exp::Add(e1,e2),Exp::Add(e3,e4)) => e1.addr_eq(e3) && e2.addr_eq(e4),
            (_, _) => false
        }
    }
}
thread_local_intern_arena!(ARENA: Arena -> Exp);
fn int(i:i8) -> EXP {
    intern(Exp::Int(i))
}
fn add(e1:EXP,e2:EXP) -> EXP {
    intern(Exp::Add(e1,e2))
}
fn eval(e:EXP) -> i8{
    match e {
        Exp::Int(i) => *i,
        Exp::Add(e1,e2) => eval(e1)+eval(e2)
    }
}
#[test]
fn testa() {
    assert_eq!(3, eval(intern(Exp::Add(intern(Exp::Int(1)),intern(Exp::Int(2))))));
    assert_eq!(3, eval(add(int(1),int(2))));
    assert_eq!(true, int(1).addr_eq(int(1)));
    assert_eq!(true, add(int(1),int(2)).addr_eq(add(int(1),int(2))));
    assert_eq!(false, int(2).addr_eq(leak(Exp::Int(2))));

    assert_eq!(true, int(1)==int(1));
    assert_eq!(true, add(int(1),int(2))==add(int(1),int(2)));
    assert_eq!(true, int(2)==leak(Exp::Int(2)));
    assert_eq!(false, add(int(1),int(2))==add(int(1),leak(Exp::Int(2))));
}
fn main() {
    println!("{:?}", eval(intern(Exp::Add(intern(Exp::Int(1)),intern(Exp::Int(2))))));
    println!("{:?}", eval(add(int(1),int(2))));
    println!("{:?}", int(1));
    println!("{:?}", int(1).addr_eq(int(1)));
    println!("{:?}", add(int(1),int(2)).addr_eq(add(int(1),int(2))));
    println!("{:?}", int(2).addr_eq(leak(Exp::Int(2))));
}
```

続き：

https://qiita.com/h_sakurai/items/615cc21aa0c3fb118bcb

https://qiita.com/h_sakurai/items/a81df43380eab03aa698

https://qiita.com/h_sakurai/items/0d9aa816a99fffa52b9b