Rustでコンパイラ改

※Rust0.7で作成しました。

今回は以下のリファクタリングを行いました。

- copyは使わなくして、cloneを使うようにしました。
- Joinableというトレイトを作って汎用的なJoinを作りました。
- macroを使って複数のimplを１つに纏めました。

```rust
use join::Joinable;
use ast::*;

pub mod ast {

  #[deriving(Clone,ToStr)]
  pub enum E {
    ELdc(~T, int),
    EBin(~T, ~str, ~E, ~E),
    EPrint(~T, ~E),
    EBlock(~T, ~[~E]),
  }

  macro_rules! EOp(
    ($T:ident, $op:expr) => (
      pub fn $T(t: ~T, a: ~E, b: ~E) -> E {
        EBin(t, ~$op, a, b)
      }
    )
  )

  EOp!(EAdd, "add")
  EOp!(ESub, "sub")
  EOp!(EMul, "mul")
  EOp!(EDiv, "div")

  #[deriving(Clone,ToStr)]
  pub enum T {
    Ti(int),
    Tv,
    TFun(~T, ~[~T]),
  }

  #[deriving(Clone,ToStr)]
  pub enum R {
    pub RG(~T, ~str),
    pub RL(~T, ~str),
    pub RR(~T, ~str),
    pub RN(~T, ~str),
  }

  #[deriving(Clone,ToStr)]
  pub enum LL {
    LLCall(Option<~R>, ~R, ~[~R]),
    LLBin(Option<~R>, ~str, ~R, ~R),
  }

  macro_rules! impl_ToStr_for(
    ($T:ty) => (
      impl ToStr for $T {
        #[inline]
        fn to_str(&self) -> ~str {
          fmt!("%?", *self)
        }
      }
    )
  )

  macro_rules! impl_ToStr_for_Option(
    ($T:ty) => (
      impl ToStr for Option<$T> {
        fn to_str(&self) -> ~str {
          match *self {
            Some(ref r) => fmt!("Some(%?)",r),
            None => ~"None",
          }
        }
      }
    )
  )

  impl_ToStr_for!(~E)
  impl_ToStr_for!(~T)
  impl_ToStr_for!(~R)
  impl_ToStr_for!(~LL)

  impl_ToStr_for_Option!(~R)

  impl Eq for T {

    pub fn eq(&self, t:&T) -> bool {
      self.to_str() == t.to_str()
    }

    pub fn ne(&self, t:&T) -> bool {
      !(self == t)
    }

  }

  impl R {

    pub fn t(&self) -> ~T {
      match *self {
        RG(ref t, _) => t.clone(),
        RL(ref t, _) => t.clone(),
        RR(ref t, _) => t.clone(),
        RN(ref t, _) => t.clone(),
      }
    }

  }

  pub trait P {
    fn p(&self) -> ~str;
  }

  impl P for T {
    pub fn p(&self) -> ~str {
      match *self {
        Ti(ref i) => "i" + i.to_str(),
        Tv => ~"void",
        TFun(ref t, ref ls) => {
          t.p() + "(" + ls.map(|t| t.p()).join(", ") + ")*"
        }
      }
    }

  }

  impl P for R {
    pub fn p(&self) -> ~str {
      match *self {
        RG(_,ref id) => "@" + *id,
        RL(_,ref id) => "%" + *id,
        RR(_,ref id) => "%." + *id,
        RN(_,ref id) => id.clone(),
      }
    }

  }

}


fn main() {
//    println(os::getcwd().to_str());
//    println(os::getenv("PATH").unwrap());
//    os::setenv("PATH", os::getenv("PATH").unwrap());
//    println(readAll("test.rs"));
//    writeAll("tes.txt", "hogehoge");

  let ast = ~EBlock(~Tv, ~[
    ~EPrint(~Ti(32), ~ELdc(~Ti(32), 11)),
    ~EPrint(~Ti(32),
      ~EAdd(~Ti(32), ~ELdc(~Ti(32), 11), ~ELdc(~Ti(32), 22)))
  ]);
  println("ast=" + ast.to_str());

  let ll = kNormal(ast);
  println("ll=" + ll.to_str());
  
  emit("e.ll", ll);
  
  println(exec("llc e.ll -o e.s").to_str());
  println(exec("llvm-gcc -m64 e.s -o e").to_str());
  println(exec("./e").to_str());
}

mod interpreter {
  use ast::*;

  fn eval(e:&E)->int {

    match e {

      &ELdc(_, i) => i,

      &EBin(_, ~"add", ref a, ref b) => eval(*a) + eval(*b),

      &EBin(_, ref op, _, _) => fail!("operator "+*op),

      &EPrint(_, ref e) => {
        let e = eval(*e);
        println(e.to_str());
        e
      }

      &EBlock(_, ref ls) => {
        fn f(ls:&[~E],r:int)-> int {
          match ls {
            [] => r,
            [ref a, ..rest] => f(rest,eval(*a))
          }
        }
        f(*ls, 0)
      }
    }
  }
}

mod kNormal {
  use ast::*;
  use std::vec;

  fn gid(t:&T)-> ~R {
    ~RR(~(*t).clone(),~"")
  }

  static mut ls:Option<~[~LL]> = None;

  fn add(l:~LL) {
    unsafe {
      ls = Some(vec::append_one(ls.unwrap(), l));
    }
  }

  fn f(a: &E)-> ~R {

    match a {

      &EBin(ref t,ref op, ref a, ref b) => {
        let a = f(*a);
        let b = f(*b);
        let id = gid(*t);
        if (*t != a.t() || *t != b.t()) {fail!(fmt!("type mismatch %?",*t));}
        add(~LLBin(Some(id.clone()), op.clone(), a, b));
        id
      }

      &ELdc(ref t, ref i) => ~RN(t.clone(), i.to_str()),

      &EPrint(ref t, ref a) => {
        let a = f(*a);
        if (*t != a.t()) {fail!(fmt!("type mismatch t=%? ta=%?", t, a.t()))}
        add(~LLCall(None, ~RG(~TFun(~Tv, ~[t.clone()]), "print_" + t.p()), ~[a.clone()]));
        a
      }

      &EBlock(_,ref ls) => {
        fn f2(ls:&[~E],r:&R)-> ~R {
          match ls {
            [] => ~r.clone(),
            [ref e, ..rest] => f2(rest,f(*e)),
          }
        }
        f2(*ls, ~RN(~Tv,~""))
      }

    }

  }

  pub fn apply(e: &E) -> ~[~LL] {
    unsafe {
      ls = Some(~[]);
      f(e);
      ls.unwrap()
    }
  }
  
}

fn kNormal(a: &ast::E) -> ~[~ast::LL] {
  kNormal::apply(a)
}

pub mod emit {

  use ast::*;
  use asm;

  fn o(id: &Option<~R>, out: &str) {
    match id {
      &Some(ref id) =>asm::__(id.p() + " = " + out),
      &None => asm::__(out),
    }
  }

  fn emitLL(l: &LL) {
    match l {
      &LLCall(ref id, ref op, ref prms) => {
        let prms:~[~str] = prms.map(|a| a.t().p() + " " + a.p());
        o(id, fmt!("call %s %s(%s) nounwind",
          op.t().p(), op.p(), prms.join(", ")))
      }
      &LLBin(ref id, ref op, ref a, ref b) => {
        o(id, fmt!("%s %s %s, %s", *op, a.t().p(), a.p(), b.p()))
      }
    }
  }

  pub fn apply(file: &str, ls: &[~LL]) {

    asm::open(file);
    asm("@.str = private constant [4 x i8] c\"%d\\0A\\00\"");
    asm("define void @print_i32(i32 %a) nounwind ssp {");
    asm("entry:");
    asm::__("call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i32 %a) nounwind");
    asm::__("ret void");
    asm("}");
    asm("define void @print_i8(i8 %a) nounwind ssp {");
    asm("entry:");
    asm::__("call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i64 0, i64 0), i8 %a) nounwind");
    asm::__("ret void");
    asm("}");

    asm("declare i32 @printf(i8*, ...) nounwind");

    asm("define i32 @main() nounwind ssp {");
    asm("entry:");

    ls.map(|l| emitLL(*l));
    

    asm::__("ret i32 0");
    asm("}");
    asm::close();
  }

}

pub fn emit(file: &str, ls: &[~ast::LL]) {
  emit::apply(file, ls);
}

pub fn genid(s:&str) -> ~str {
  static mut id:int = 0;
  unsafe {
    id += 1;
    s + id.to_str()
  }
}

fn readAll(file:&str) -> ~str {
  use std::io;
  use std::result;
  let reader = result::get(&io::file_reader(&Path(file)));
  let mut s = ~"";
  while !reader.eof() {
    s = s + reader.read_line() + "\n";
  }
  s
} 

fn writeAll(file:&str, s:&str) {
  use std::io;
  use std::result;
  let writer = result::get(&io::buffered_file_writer(&Path(file)));
  writer.write_str(s);
}

pub mod asm {

  use std::io;
  use std::result;

  pub static mut writer: Option<@Writer> = None;

  pub fn open(file:&str) {
    unsafe {
      writer = Some(result::get(&io::buffered_file_writer(&Path(file))));
    }
  }

  pub fn println(s:&str) {
    unsafe {
      writer.unwrap().write_str(s + "\n");
    }
  }

  pub fn close() {
    unsafe {
      writer = None;
    }
  }

  pub fn __(s:&str) {
    println("  " + s);
  }

}

pub fn asm(s:&str) {
  asm::println(s);
}

fn exec(cmd:&str) -> (int, ~str, ~str) {

  use std::str;
  use std::run;

  let mut cmds:~[&str] = cmd.split_str_iter(" ").collect();
  let prog:~str = cmds.shift().to_str();
  let args:~[~str] = cmds.map(|&s| s.to_str());
  let o = run::process_output(prog,args);

  unsafe {
    (o.status,
    str::raw::from_bytes(o.output),
    str::raw::from_bytes(o.error),
    )
  }

}

pub mod join {
  pub trait Joinable<T> {
    fn join(&self, sep:&str) -> ~str;
  }

  impl<T:ToStr> Joinable<T> for ~[T] {
    fn join(&self, sep:&str) -> ~str {
      let mut s:~str = ~"";
      for (*self).iter().advance |t| {
        match s {
          ~"" => s = t.to_str(),
          _ => s = s + sep + t.to_str(),
        }
      }
      s
    }
  }

}
```