<commit_before>extern mod extra;

use exec::Prog;
use parse::parse_recursive;
use state::ParseState;
use compile::{Instruction, compile_recursive};
use error::ParseError::*;

mod parse;
mod state;
mod compile;
mod error;
mod exec;

pub struct CompiledRegexp {
  input: ~str,
  prog: Prog
}

impl CompiledRegexp {
  pub fn new(s: &str) -> Result<CompiledRegexp, ParseCode> {
    match UncompiledRegexp::new(s).compile() {
      Ok(re) => Ok(re),
      Err(e) => Err(e)
    }
  }
  fn new_with_prog(prog: Prog, s: &str) -> CompiledRegexp {
    CompiledRegexp {
      prog: prog,
      input: s.to_owned().clone()
    }
  }
}

impl CompiledRegexp {
  // the same thing as re.match() in python,
  // but can't make match a function name in rust
  fn run(&self, input: &str) {
    self.prog.run(input);
  }

  // should only find the first
  fn search(&self, input: &str) {
    let len = input.len();

    for start in range(0, len) {
      for end in range(start, len) {
        self.prog.run(input.slice(start, end));
      }
    }
  }

  // not really working how replace should
  fn replace(&self, input: &str, repstr: &str) {
	self.prog.replace(input, repstr);
  }

  // ugly, but functional?
  fn findall(&self, input: &str) {
	let mut start = 0;
	let mut buff = 0;
	let len = input.len();
	while start < len {
	  match self.prog.run(input.slice(start, len)) {
        Some(t) => {
		  if t.sp == 0 {
		    start = start + 1;
          }
          else {
            start = t.sp + buff;
          }
		  buff = start;
        }
        None => { start = start + 1; }
      }
    }
  }

  fn split(&self, input: &str) {

  }
}

// uncompiled regular expression
// not parsed until compile is called...
// compile returns a CompiledRegexp
pub struct UncompiledRegexp {
  input: ~str
}

impl UncompiledRegexp {
  pub fn new(s: &str) -> UncompiledRegexp {
    UncompiledRegexp { input: s.clone().to_owned() }
  }
}

impl UncompiledRegexp {
  // we should hide the underlying parsing algorithm
  // from the user
  fn parse(&mut self) -> Result<state::Regexp, ParseCode> {
    let mut ps = ParseState::new();
    let mut input = self.input.clone();
    match parse_recursive(&mut input, &mut ps) {
      ParseOk => {
        ps.pop()
      }
      e => {
        Err(e)
      }
    }
  }
  fn compile(&mut self) -> Result<CompiledRegexp, ParseCode> {
    let mut stack: ~[Instruction] = ~[];
    match self.parse() {
      Ok(ref re) => {
        compile_recursive(re, &mut stack);
        let prog = Prog::new(stack);
        Ok(CompiledRegexp::new_with_prog(prog, self.input))
      }
      Err(e) => {
        Err(e)
      }
    }
  }
  // for these, just call compile, and
  // run the corresponding CompiledRegex
  // functions
  fn run(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.run(input),
      Err(e) => println(e.to_str())
    }
  }
  fn search(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.search(input),
      Err(e) => println(e.to_str())
    }
  }
  fn replace(&mut self, input: &str, repstr: &str) {
    match self.compile() {
      Ok(ref mut re) => re.replace(input, repstr),
      Err(e) => println(e.to_str())
    }
  }
  fn findall(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.findall(input),
      Err(e) => println(e.to_str())
    }
  }
  fn split(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.split(input),
      Err(e) => println(e.to_str())
    }
  }
}

fn main() {
  println("--Case 0--");
  let mut re = UncompiledRegexp::new("[a-z]d|abc");
  re.run("abc");

  println("--Case 1--");
  let mut re = UncompiledRegexp::new("(?:http(s)?:\\/\\/)?(www.)?([a-zA-Z0-9_.]+).(com|org|net|edu)\\/?");
  re.run("http://ferristseng.comuASDAFASFASBVZKXJVBKZXBVKJZBXVKBZXV");
  re.run("http://reddit.com/");
  re.run("https://google.com/");
  //re.run("NOT A WEBSITE");
  re.run("http://virginia.edu");
  re.run("www.cnn.com");

  println("--Case 2--");
  let mut re = UncompiledRegexp::new("[^a-zA-Z0-9]*");
  re.run("我是曾繁睿");

  println("--Case 2 (NonGreedy)--");
  let mut re = UncompiledRegexp::new("[^a-zA-Z0-9]*?");
  re.run("我是曾繁睿");

  println("--Case 3--");
  let mut re = UncompiledRegexp::new("(a+?)*");
  re.run("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

  println("--case 3a--");
  re.findall("aaaaabaaaba");

  println("--Case 4--");
  let mut re = UncompiledRegexp::new("<((\\w|[^>])+)>");
  re.run("

");

  println("--Case 5--");
  let mut re = UncompiledRegexp::new("\\d+");
  re.run("1258102958012895danzxnvhlhl4314");

  // println("--Case 2--");
  // UncompiledRegexp::new("a|b|c").compile();

  // println("--Case 3--");
  // UncompiledRegexp::new("a|(Bcf)|dez").compile();

  // println("--Case 4--");
  // //UncompiledRegexp::new("abc*|d").parse();

  // println("--Case 5--");
  // //UncompiledRegexp::new("io(ab|c)*zz|(bcd)*").parse();

  // println("--Case 6--");
  // //UncompiledRegexp::new("あ(ab(cd|d)|e)|f").parse();

  /*
  println("--Case 7--");
  //Regexp::new("[[A-Z]0-9(fgh)]]]|[abc]").parse();
  Regexp::new("1\\d2").parse();

  println("--Case 8--");
  UncompiledRegexp::new("(abc){1,}").parse();

  println("--Case 9--");
  UncompiledRegexp::new("abc{3,4}?").parse();

  println("--Case 10--");
  UncompiledRegexp::new("a|b{3}").parse();

  println("--Case 11--");
  UncompiledRegexp::new("a{4,3}?").parse();

  println("--Case 12--");
  UncompiledRegexp::new("[C[e-h]arlemange]|bs|c").compile();

  println("--Case 13--");
  UncompiledRegexp::new("[^aA-ZA]").compile();

  println("--Case 14--");
  UncompiledRegexp::new("[^\U00000000-\U0010FFFF]").parse();

  println("--Case 15--");
  UncompiledRegexp::new("[^a-f]").parse();

  println("--Case 16--");
  UncompiledRegexp::new("a?").compile();

  println("--Case 17--");
  UncompiledRegexp::new("(ABC)+").compile();

  println("--Case 18--");
  UncompiledRegexp::new("(A|B)*").compile();
  */

  println("OK");
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn parse_alternation_ok_test() {
    assert!(UncompiledRegexp::new("a|b").parse().is_ok());
  }

  #[test]
  fn parse_concatenation_ok_test() {
    assert!(UncompiledRegexp::new("a(bc)d").parse().is_ok());
  }

  #[test]
  fn parse_char_class_ok_test() {
    assert!(UncompiledRegexp::new("[a-zABC!@#]]]").parse().is_ok());
  }

  #[test]
  fn parse_capture_ok_test() {
    assert!(UncompiledRegexp::new("(hel(ABC)ok)").parse().is_ok());
  }

  #[test]
  fn parse_capture_fail_test() {
    assert!(UncompiledRegexp::new("(hel(ABC)ok").parse().is_err());
  }
}
<commit_msg>findall<commit_after>extern mod extra;

use exec::Prog;
use parse::parse_recursive;
use state::ParseState;
use compile::{Instruction, compile_recursive};
use error::ParseError::*;

mod parse;
mod state;
mod compile;
mod error;
mod exec;

pub struct CompiledRegexp {
  input: ~str,
  prog: Prog
}

impl CompiledRegexp {
  pub fn new(s: &str) -> Result<CompiledRegexp, ParseCode> {
    match UncompiledRegexp::new(s).compile() {
      Ok(re) => Ok(re),
      Err(e) => Err(e)
    }
  }
  fn new_with_prog(prog: Prog, s: &str) -> CompiledRegexp {
    CompiledRegexp {
      prog: prog,
      input: s.to_owned().clone()
    }
  }
}

impl CompiledRegexp {
  // the same thing as re.match() in python,
  // but can't make match a function name in rust
  fn run(&self, input: &str) {
    self.prog.run(input);
  }

  // should only find the first
  fn search(&self, input: &str) {
    let len = input.len();

    for start in range(0, len) {
      for end in range(start, len) {
        self.prog.run(input.slice(start, end));
      }
    }
  }

  // not really working how replace should
  fn replace(&self, input: &str, repstr: &str) {
	self.prog.replace(input, repstr);
  }

  // ugly, but functional?
  fn findall(&self, input: &str) {
	let mut start = 0;
	let mut buff = 0;
	let len = input.len();
	while start < len {
	  match self.prog.run(input.slice(start, len)) {
        Some(t) => {
		  if t.sp == 0 {
		    start = start + 1;
          }
          else {
            start = t.sp + buff;
          }
		  buff = start;
        }
        None => { start = start + 1; }
      }
    }
  }

  fn split(&self, input: &str) {

  }
}

// uncompiled regular expression
// not parsed until compile is called...
// compile returns a CompiledRegexp
pub struct UncompiledRegexp {
  input: ~str
}

impl UncompiledRegexp {
  pub fn new(s: &str) -> UncompiledRegexp {
    UncompiledRegexp { input: s.clone().to_owned() }
  }
}

impl UncompiledRegexp {
  // we should hide the underlying parsing algorithm
  // from the user
  fn parse(&mut self) -> Result<state::Regexp, ParseCode> {
    let mut ps = ParseState::new();
    let mut input = self.input.clone();
    match parse_recursive(&mut input, &mut ps) {
      ParseOk => {
        ps.pop()
      }
      e => {
        Err(e)
      }
    }
  }
  fn compile(&mut self) -> Result<CompiledRegexp, ParseCode> {
    let mut stack: ~[Instruction] = ~[];
    match self.parse() {
      Ok(ref re) => {
        compile_recursive(re, &mut stack);
        let prog = Prog::new(stack);
        Ok(CompiledRegexp::new_with_prog(prog, self.input))
      }
      Err(e) => {
        Err(e)
      }
    }
  }
  // for these, just call compile, and
  // run the corresponding CompiledRegex
  // functions
  fn run(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.run(input),
      Err(e) => println(e.to_str())
    }
  }
  fn search(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.search(input),
      Err(e) => println(e.to_str())
    }
  }
  fn replace(&mut self, input: &str, repstr: &str) {
    match self.compile() {
      Ok(ref mut re) => re.replace(input, repstr),
      Err(e) => println(e.to_str())
    }
  }
  fn findall(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.findall(input),
      Err(e) => println(e.to_str())
    }
  }
  fn split(&mut self, input: &str) {
    match self.compile() {
      Ok(ref mut re) => re.split(input),
      Err(e) => println(e.to_str())
    }
  }
}

fn main() {
  println("--Case 0--");
  let mut re = UncompiledRegexp::new("[a-z]d|abc");
  re.run("abc");

  println("--Case 1--");
  let mut re = UncompiledRegexp::new("(?:http(s)?://)?(www.)?([a-zA-Z0-9_.]+).(com|org|net|edu)/?");
  re.run("http://ferristseng.comuASDAFASFASBVZKXJVBKZXBVKJZBXVKBZXV");
  re.run("http://reddit.com/");
  re.run("https://google.com/");
  //re.run("NOT A WEBSITE");
  re.run("http://virginia.edu");
  re.run("www.cnn.com");

  println("--Case 2--");
  let mut re = UncompiledRegexp::new("[^a-zA-Z0-9]*");
  re.run("我是曾繁睿");

  println("--Case 2 (NonGreedy)--");
  let mut re = UncompiledRegexp::new("[^a-zA-Z0-9]*?");
  re.run("我是曾繁睿");

  println("--Case 3--");
  let mut re = UncompiledRegexp::new("(a+?)*");
  re.run("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

  println("--case 3a--");
  re.findall("aaaaabaaaba");

  println("--Case 4--");
  let mut re = UncompiledRegexp::new("<([^>]+)>");
  re.run("

");

  // println("--Case 2--");
  // UncompiledRegexp::new("a|b|c").compile();

  // println("--Case 3--");
  // UncompiledRegexp::new("a|(Bcf)|dez").compile();

  // println("--Case 4--");
  // //UncompiledRegexp::new("abc*|d").parse();

  // println("--Case 5--");
  // //UncompiledRegexp::new("io(ab|c)*zz|(bcd)*").parse();

  // println("--Case 6--");
  // //UncompiledRegexp::new("あ(ab(cd|d)|e)|f").parse();

  /*
  println("--Case 7--");
  //Regexp::new("[[A-Z]0-9(fgh)]]]|[abc]").parse();
  Regexp::new("1\\d2").parse();

  println("--Case 8--");
  UncompiledRegexp::new("(abc){1,}").parse();

  println("--Case 9--");
  UncompiledRegexp::new("abc{3,4}?").parse();

  println("--Case 10--");
  UncompiledRegexp::new("a|b{3}").parse();

  println("--Case 11--");
  UncompiledRegexp::new("a{4,3}?").parse();

  println("--Case 12--");
  UncompiledRegexp::new("[C[e-h]arlemange]|bs|c").compile();

  println("--Case 13--");
  UncompiledRegexp::new("[^aA-ZA]").compile();

  println("--Case 14--");
  UncompiledRegexp::new("[^\U00000000-\U0010FFFF]").parse();

  println("--Case 15--");
  UncompiledRegexp::new("[^a-f]").parse();

  println("--Case 16--");
  UncompiledRegexp::new("a?").compile();

  println("--Case 17--");
  UncompiledRegexp::new("(ABC)+").compile();

  println("--Case 18--");
  UncompiledRegexp::new("(A|B)*").compile();
  */

  println("OK");
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn parse_alternation_ok_test() {
    assert!(UncompiledRegexp::new("a|b").parse().is_ok());
  }

  #[test]
  fn parse_concatenation_ok_test() {
    assert!(UncompiledRegexp::new("a(bc)d").parse().is_ok());
  }

  #[test]
  fn parse_char_class_ok_test() {
    assert!(UncompiledRegexp::new("[a-zABC!@#]]]").parse().is_ok());
  }

  #[test]
  fn parse_capture_ok_test() {
    assert!(UncompiledRegexp::new("(hel(ABC)ok)").parse().is_ok());
  }

  #[test]
  fn parse_capture_fail_test() {
    assert!(UncompiledRegexp::new("(hel(ABC)ok").parse().is_err());
  }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::LinkedList;
use std::str::FromStr;

pub type Value = i32;
pub type ForthResult = Result<(), Error>;
type StackResult<T> = Result<T, Error>;

type Stack = LinkedList<Value>;
type Code = LinkedList<Term>;
type Definitions = HashMap<String, Code>;

pub struct Forth {
    code: Code,
    defs: Definitions,
    stack: Stack,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

#[derive(Debug, Clone)]
enum Term {
    Number(Value),
    Word(String),
    StartDefinition,
    EndDefinition,
}

impl FromStr for Term {
    type Err = ();

    fn from_str(s: &str) -> Result<Term, ()> {
        match s {
            ":" => Ok(Term::StartDefinition),
            ";" => Ok(Term::EndDefinition),
            _ => Err(()),
        }
        .or_else(|_| Value::from_str(s).map(Term::Number))
        .or_else(|_| Ok(Term::Word(s.to_ascii_lowercase())))
    }
}

impl Forth {
    pub fn new() -> Forth {
        Forth {
            code: LinkedList::new(),
            defs: HashMap::new(),
            stack: LinkedList::new(),
        }
    }

    pub fn stack(&self) -> Vec<Value> {
        self.stack.iter().cloned().collect()
    }

    pub fn eval(&mut self, input: &str) -> ForthResult {
        let mut new_code = Forth::into_code(input);
        self.code.append(&mut new_code);
        self.run()
    }

    fn run(&mut self) -> ForthResult {
        while let Some(term) = self.code.pop_front() {
            self.step_term(term)?;
        }
        Forth::ok()
    }

    fn step_term(&mut self, term: Term) -> ForthResult {
        match term {
            Term::Number(value) => self.push(value),
            Term::Word(word) => self.step_word(&word),
            Term::StartDefinition => self.store_definition(),
            Term::EndDefinition => {
                eprintln!("`;` without preceding `:`");
                Err(Error::InvalidWord)
            }
        }
    }

    fn step_word(&mut self, word: &str) -> ForthResult {
        if let Some(def) = self.defs.get(word) {
            let mut def = def.clone();
            while let Some(term) = def.pop_front() {
                self.step_term(term)?;
            }
            Self::ok()
        } else {
            self.step_built_in(word)
        }
    }

    fn divide((a, b): (Value, Value)) -> StackResult<Value> {
        a.checked_div(b).ok_or_else(|| {
            eprintln!("Cannot divide {} by {}", a, b);
            Error::DivisionByZero
        })
    }

    fn step_built_in(&mut self, word: &str) -> ForthResult {
        match word {
            "+" => self.bin_op(|(a, b)| Ok(a + b)),
            "-" => self.bin_op(|(a, b)| Ok(a - b)),
            "*" => self.bin_op(|(a, b)| Ok(a * b)),
            "/" => self.bin_op(Self::divide),
            "dup" => self.pop().and_then(|a| self.push(a).and(self.push(a))),
            "drop" => self.pop().and(Forth::ok()),
            "swap" => self
                .pop_two()
                .and_then(|(a, b)| self.push(b).and(self.push(a))),
            "over" => self
                .pop_two()
                .and_then(|(a, b)| self.push(a).and(self.push(b)).and(self.push(a))),
            _ => {
                eprintln!("{} is undefined", word);
                Err(Error::UnknownWord)
            }
        }
    }

    fn store_definition(&mut self) -> ForthResult {
        let mut def = LinkedList::new();

        loop {
            match self.code.pop_front() {
                Some(Term::EndDefinition) => break,
                Some(term) => def.push_back(term),
                None => return Err(Error::InvalidWord),
            }
        }

        if let Some(Term::Word(name)) = def.pop_front() {
            self.store_word(name, def)
        } else {
            Err(Error::InvalidWord)
        }
    }

    fn push(&mut self, value: Value) -> ForthResult {
        self.stack.push_back(value);
        Forth::ok()
    }

    fn pop(&mut self) -> StackResult<Value> {
        self.stack.pop_back().ok_or_else(|| {
            eprintln!("Stack underflow");
            Error::StackUnderflow
        })
    }

    fn pop_two(&mut self) -> StackResult<(Value, Value)> {
        self.pop().and_then(|b| self.pop().map(|a| (a, b)))
    }

    fn bin_op<F>(&mut self, op: F) -> ForthResult
    where
        F: FnOnce((Value, Value)) -> StackResult<Value>,
    {
        self.pop_two()
            .and_then(op)
            .and_then(|value| self.push(value))
    }

    fn store_word(&mut self, name: String, code: Code) -> ForthResult {
        let mut resolved_def = Code::new();
        for t in code.iter() {
            match t {
                Term::Number(_) => resolved_def.push_back(t.clone()),
                Term::Word(s) => {
                    if let Some(cs) = self.defs.get(s) {
                        resolved_def.append(&mut cs.clone());
                    } else {
                        resolved_def.push_back(t.clone());
                    }
                }
                _ => {
                    eprintln!("Nested definition in {}", name);
                    return Err(Error::InvalidWord);
                }
            }
        }
        self.defs.insert(name, resolved_def);
        Forth::ok()
    }

    fn into_code(input: &str) -> LinkedList<Term> {
        input
            .split(|c: char| c.is_whitespace() || c.is_control())
            .map(Term::from_str)
            .filter(Result::is_ok)
            .map(Result::unwrap)
            .collect()
    }

    fn ok() -> ForthResult {
        Ok(())
    }
}
<commit_msg>forth: Finish closing #961 - update example (#1033)<commit_after>use std::collections::HashMap;
use std::collections::VecDeque;
use std::str::FromStr;

pub type Value = i32;
pub type ForthResult = Result<(), Error>;
type StackResult<T> = Result<T, Error>;

type Stack = Vec<Value>;
type Code = VecDeque<Term>;
type Definitions = HashMap<String, Code>;

pub struct Forth {
    code: Code,
    defs: Definitions,
    stack: Stack,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

#[derive(Debug, Clone)]
enum Term {
    Number(Value),
    Word(String),
    StartDefinition,
    EndDefinition,
}

impl FromStr for Term {
    type Err = ();

    fn from_str(s: &str) -> Result<Term, ()> {
        match s {
            ":" => Ok(Term::StartDefinition),
            ";" => Ok(Term::EndDefinition),
            _ => Err(()),
        }
        .or_else(|_| Value::from_str(s).map(Term::Number))
        .or_else(|_| Ok(Term::Word(s.to_ascii_lowercase())))
    }
}

impl Forth {
    pub fn new() -> Forth {
        Forth {
            code: Code::new(),
            defs: HashMap::new(),
            stack: Stack::new(),
        }
    }

    pub fn stack(&self) -> &[Value] {
        self.stack.as_slice()
    }

    pub fn eval(&mut self, input: &str) -> ForthResult {
        let mut new_code = Forth::into_code(input);
        self.code.append(&mut new_code);
        self.run()
    }

    fn run(&mut self) -> ForthResult {
        while let Some(term) = self.code.pop_front() {
            self.step_term(term)?;
        }
        Forth::ok()
    }

    fn step_term(&mut self, term: Term) -> ForthResult {
        match term {
            Term::Number(value) => self.push(value),
            Term::Word(word) => self.step_word(&word),
            Term::StartDefinition => self.store_definition(),
            Term::EndDefinition => {
                eprintln!("`;` without preceding `:`");
                Err(Error::InvalidWord)
            }
        }
    }

    fn step_word(&mut self, word: &str) -> ForthResult {
        if let Some(def) = self.defs.get(word) {
            let mut def = def.clone();
            while let Some(term) = def.pop_front() {
                self.step_term(term)?;
            }
            Self::ok()
        } else {
            self.step_built_in(word)
        }
    }

    fn divide((a, b): (Value, Value)) -> StackResult<Value> {
        a.checked_div(b).ok_or_else(|| {
            eprintln!("Cannot divide {} by {}", a, b);
            Error::DivisionByZero
        })
    }

    fn step_built_in(&mut self, word: &str) -> ForthResult {
        match word {
            "+" => self.bin_op(|(a, b)| Ok(a + b)),
            "-" => self.bin_op(|(a, b)| Ok(a - b)),
            "*" => self.bin_op(|(a, b)| Ok(a * b)),
            "/" => self.bin_op(Self::divide),
            "dup" => self.pop().and_then(|a| self.push(a).and(self.push(a))),
            "drop" => self.pop().and(Forth::ok()),
            "swap" => self
                .pop_two()
                .and_then(|(a, b)| self.push(b).and(self.push(a))),
            "over" => self
                .pop_two()
                .and_then(|(a, b)| self.push(a).and(self.push(b)).and(self.push(a))),
            _ => {
                eprintln!("{} is undefined", word);
                Err(Error::UnknownWord)
            }
        }
    }

    fn store_definition(&mut self) -> ForthResult {
        let mut def = Code::new();

        loop {
            match self.code.pop_front() {
                Some(Term::EndDefinition) => break,
                Some(term) => def.push_back(term),
                None => return Err(Error::InvalidWord),
            }
        }

        if let Some(Term::Word(name)) = def.pop_front() {
            self.store_word(name, def)
        } else {
            Err(Error::InvalidWord)
        }
    }

    fn push(&mut self, value: Value) -> ForthResult {
        self.stack.push(value);
        Forth::ok()
    }

    fn pop(&mut self) -> StackResult<Value> {
        self.stack.pop().ok_or_else(|| {
            eprintln!("Stack underflow");
            Error::StackUnderflow
        })
    }

    fn pop_two(&mut self) -> StackResult<(Value, Value)> {
        self.pop().and_then(|b| self.pop().map(|a| (a, b)))
    }

    fn bin_op<F>(&mut self, op: F) -> ForthResult
    where
        F: FnOnce((Value, Value)) -> StackResult<Value>,
    {
        self.pop_two()
            .and_then(op)
            .and_then(|value| self.push(value))
    }

    fn store_word(&mut self, name: String, code: Code) -> ForthResult {
        let mut resolved_def = Code::new();
        for t in code.iter() {
            match t {
                Term::Number(_) => resolved_def.push_back(t.clone()),
                Term::Word(s) => {
                    if let Some(cs) = self.defs.get(s) {
                        resolved_def.append(&mut cs.clone());
                    } else {
                        resolved_def.push_back(t.clone());
                    }
                }
                _ => {
                    eprintln!("Nested definition in {}", name);
                    return Err(Error::InvalidWord);
                }
            }
        }
        self.defs.insert(name, resolved_def);
        Forth::ok()
    }

    fn into_code(input: &str) -> Code {
        input
            .split(|c: char| c.is_whitespace() || c.is_control())
            .map(Term::from_str)
            .filter(Result::is_ok)
            .map(Result::unwrap)
            .collect()
    }

    fn ok() -> ForthResult {
        Ok(())
    }
}
<|endoftext|>


<commit_before><commit_msg>prefix block scoped variables<commit_after><|endoftext|>


<commit_before>use std::io;
use std::io::Cursor;
use std::io::prelude::*;
use std::borrow::Cow;
use std::net::TcpStream;
use std::string::FromUtf8Error;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};

const RECV_BUF_SIZE: usize = 8192;
const GQTP_HEADER_SIZE: usize = 24;

#[derive(Debug)]
pub enum GQTPError {
    InvalidProtocol,
    InvalidBodySize,
    StatusError(u16),
    IO(io::Error),
    EncodingError(FromUtf8Error)
}

impl From<io::Error> for GQTPError {
    fn from(err: io::Error) -> GQTPError {
        GQTPError::IO(err)
    }
}

impl From<FromUtf8Error> for GQTPError {
    fn from(err: FromUtf8Error) -> GQTPError {
        GQTPError::EncodingError(err)
    }
}

/// Request [GQTP protocol](http://groonga.org/docs/spec/gqtp.html) over TcpStream
pub struct GQTPRequest<'a> {
    addr: Cow<'a, str>,
}

impl<'a> Default for GQTPRequest<'a> {
    fn default() -> GQTPRequest<'a> {
        GQTPRequest { addr: Cow::Borrowed("127.0.0.1:10043") }
    }
}

impl<'a> GQTPRequest<'a> {
    /// Create a GQTP client.
    pub fn new() -> GQTPRequest<'a> {
        GQTPRequest::default()
    }

    /// Set host address for GQTP server.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate ruroonga_client as groonga;
    ///
    /// groonga::GQTPRequest::new().with_addr("127.0.0.1:20043");
    /// ```
    pub fn with_addr<T>(mut self, addr: T) -> GQTPRequest<'a>
        where T: Into<Cow<'a, str>>
    {
        self.addr = addr.into();
        self
    }

    /// Send request and Receive response.
    pub fn call<C>(&self, command: C) -> Result<String, GQTPError>
        where C: AsRef<str>
    {
        // send
        let mut stream = try!(TcpStream::connect(self.addr.as_ref()));
        let mut send_buf = vec![];
        try!(send_buf.write_u8(0xc7));
        try!(send_buf.write_u8(0));
        try!(send_buf.write_i16::<BigEndian>(0));
        try!(send_buf.write_u8(0));
        try!(send_buf.write_u8(0x02));   // flags
        try!(send_buf.write_u16::<BigEndian>(0));
        try!(send_buf.write_u32::<BigEndian>(command.as_ref().len() as u32));
        try!(send_buf.write_u32::<BigEndian>(0));
        try!(send_buf.write_u64::<BigEndian>(0));
        send_buf.extend_from_slice(command.as_ref().as_bytes());
        let _ = stream.write_all(send_buf.as_slice());

        // receive and check protocol header value
        let mut read_buf = vec![0; RECV_BUF_SIZE];
        let _ = stream.read(&mut read_buf);
        let mut buf = Cursor::new(read_buf);

        let protocol = try!(buf.read_u8());
        let query_type = try!(buf.read_u8());
        if protocol != 0xc7 || query_type > 5 {
            return Err(GQTPError::InvalidProtocol);
        }
        let _ = try!(buf.read_i16::<BigEndian>());
        let _ = try!(buf.read_u8());

        let flags = try!(buf.read_u8());
        if !((flags & 0x01) == 0x01 || (flags & 0x02) == 0x02) {
            return Err(GQTPError::InvalidProtocol);
        }

        let status = try!(buf.read_u16::<BigEndian>());
        if status != 0 && status != 1 {
            return Err(GQTPError::StatusError(status));
        }
        let size = try!(buf.read_i32::<BigEndian>());
        let _ = try!(buf.read_i32::<BigEndian>());    // opaque
        let _ = try!(buf.read_i64::<BigEndian>());    // cas

        // read body
        let mut msg_buf_len = if (size as usize + GQTP_HEADER_SIZE) > RECV_BUF_SIZE {
            RECV_BUF_SIZE - GQTP_HEADER_SIZE
        } else {
            size as usize
        };
        let mut msg = vec![0; msg_buf_len];
        let _ = try!(buf.read(&mut msg));
        if (size as usize + GQTP_HEADER_SIZE) > RECV_BUF_SIZE {
            loop {
                let mut read_buf = vec![0; RECV_BUF_SIZE];
                let rsize = try!(stream.read(&mut read_buf));
                msg.extend_from_slice(read_buf.as_ref());
                msg_buf_len += rsize;
                if msg_buf_len >= size as usize {
                    break;
                }
            }
        }

        Ok(try!(String::from_utf8(msg)))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn smoke_gqtp() {
        let req = GQTPRequest::new();
        assert_eq!("127.0.0.1:10043", req.addr)
    }

    #[test]
    fn smoke_gqtp_with_addr() {
        let req = GQTPRequest::new().with_addr("127.0.0.1:20043");
        assert_eq!("127.0.0.1:20043", req.addr)
    }

    #[test]
    fn smoke_gqtp_with_addr_string() {
        let req = GQTPRequest::new().with_addr("127.0.0.1:20043".to_string());
        assert_eq!("127.0.0.1:20043", req.addr)
    }
}
<commit_msg>gqtp_request: Reduce abusing type casts<commit_after>use std::io;
use std::io::Cursor;
use std::io::prelude::*;
use std::borrow::Cow;
use std::net::TcpStream;
use std::string::FromUtf8Error;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};

const RECV_BUF_SIZE: usize = 8192;
const GQTP_HEADER_SIZE: usize = 24;

#[derive(Debug)]
pub enum GQTPError {
    InvalidProtocol,
    InvalidBodySize,
    StatusError(u16),
    IO(io::Error),
    EncodingError(FromUtf8Error)
}

impl From<io::Error> for GQTPError {
    fn from(err: io::Error) -> GQTPError {
        GQTPError::IO(err)
    }
}

impl From<FromUtf8Error> for GQTPError {
    fn from(err: FromUtf8Error) -> GQTPError {
        GQTPError::EncodingError(err)
    }
}

/// Request [GQTP protocol](http://groonga.org/docs/spec/gqtp.html) over TcpStream
pub struct GQTPRequest<'a> {
    addr: Cow<'a, str>,
}

impl<'a> Default for GQTPRequest<'a> {
    fn default() -> GQTPRequest<'a> {
        GQTPRequest { addr: Cow::Borrowed("127.0.0.1:10043") }
    }
}

impl<'a> GQTPRequest<'a> {
    /// Create a GQTP client.
    pub fn new() -> GQTPRequest<'a> {
        GQTPRequest::default()
    }

    /// Set host address for GQTP server.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate ruroonga_client as groonga;
    ///
    /// groonga::GQTPRequest::new().with_addr("127.0.0.1:20043");
    /// ```
    pub fn with_addr<T>(mut self, addr: T) -> GQTPRequest<'a>
        where T: Into<Cow<'a, str>>
    {
        self.addr = addr.into();
        self
    }

    /// Send request and Receive response.
    pub fn call<C>(&self, command: C) -> Result<String, GQTPError>
        where C: AsRef<str>
    {
        // send
        let mut stream = try!(TcpStream::connect(self.addr.as_ref()));
        let mut send_buf = vec![];
        try!(send_buf.write_u8(0xc7));
        try!(send_buf.write_u8(0));
        try!(send_buf.write_i16::<BigEndian>(0));
        try!(send_buf.write_u8(0));
        try!(send_buf.write_u8(0x02));   // flags
        try!(send_buf.write_u16::<BigEndian>(0));
        try!(send_buf.write_u32::<BigEndian>(command.as_ref().len() as u32));
        try!(send_buf.write_u32::<BigEndian>(0));
        try!(send_buf.write_u64::<BigEndian>(0));
        send_buf.extend_from_slice(command.as_ref().as_bytes());
        let _ = stream.write_all(send_buf.as_slice());

        // receive and check protocol header value
        let mut read_buf = vec![0; RECV_BUF_SIZE];
        let _ = stream.read(&mut read_buf);
        let mut buf = Cursor::new(read_buf);

        let protocol = try!(buf.read_u8());
        let query_type = try!(buf.read_u8());
        if protocol != 0xc7 || query_type > 5 {
            return Err(GQTPError::InvalidProtocol);
        }
        let _ = try!(buf.read_i16::<BigEndian>());
        let _ = try!(buf.read_u8());

        let flags = try!(buf.read_u8());
        if !((flags & 0x01) == 0x01 || (flags & 0x02) == 0x02) {
            return Err(GQTPError::InvalidProtocol);
        }

        let status = try!(buf.read_u16::<BigEndian>());
        if status != 0 && status != 1 {
            return Err(GQTPError::StatusError(status));
        }
        let size = try!(buf.read_i32::<BigEndian>()) as usize;
        let _ = try!(buf.read_i32::<BigEndian>());    // opaque
        let _ = try!(buf.read_i64::<BigEndian>());    // cas

        // read body
        let mut msg_buf_len = if (size + GQTP_HEADER_SIZE) > RECV_BUF_SIZE {
            RECV_BUF_SIZE - GQTP_HEADER_SIZE
        } else {
            size
        };
        let mut msg = vec![0; msg_buf_len];
        let _ = try!(buf.read(&mut msg));
        if (size + GQTP_HEADER_SIZE) > RECV_BUF_SIZE {
            loop {
                let mut read_buf = vec![0; RECV_BUF_SIZE];
                let rsize = try!(stream.read(&mut read_buf));
                msg.extend_from_slice(read_buf.as_ref());
                msg_buf_len += rsize;
                if msg_buf_len >= size {
                    break;
                }
            }
        }

        Ok(try!(String::from_utf8(msg)))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn smoke_gqtp() {
        let req = GQTPRequest::new();
        assert_eq!("127.0.0.1:10043", req.addr)
    }

    #[test]
    fn smoke_gqtp_with_addr() {
        let req = GQTPRequest::new().with_addr("127.0.0.1:20043");
        assert_eq!("127.0.0.1:20043", req.addr)
    }

    #[test]
    fn smoke_gqtp_with_addr_string() {
        let req = GQTPRequest::new().with_addr("127.0.0.1:20043".to_string());
        assert_eq!("127.0.0.1:20043", req.addr)
    }
}
<|endoftext|>


<commit_before>use std::char;
use std::collections::BTreeSet;
use std::iter::FromIterator;

use fst::Map;

mod names;

const BYTES: &'static [u8] = include_bytes!("name_fst.bin");

fn query_fst(word: &str) -> Vec<char> {
    lazy_static! {
        static ref FST: Map = Map::from_bytes(BYTES.to_owned()).unwrap();
    }

    let mut chars: Vec<char> = vec!();
    if let Some(cp) = FST.get(word) {
        if cp & (0xff<<32) != 0 {
            let index: usize = (cp as u32) as usize;
            for ch in names::AMBIGUOUS_CHARS[index].chars() {
                chars.push(ch);
            }
        } else {
            char::from_u32(cp as u32).map(|ch| chars.push(ch));
        }
    }
    chars
}

pub fn lookup_by_query(query: &str) -> Vec<char> {
    let query = query.to_lowercase();
    // try the original query first:
    let original_results = query_fst(query.as_str());
    if !original_results.is_empty() {
        return original_results;
    }

    let mut candidates = BTreeSet::new();
    if query.contains(|c: char| c.is_whitespace()) {
        // Split multiple-word queries, AND them together:
        let mut words = query.split_whitespace();
        if let Some(word) = words.next() {
            for ch in query_fst(word) {
                candidates.insert(ch);
            }
        }
        for word in words {
            let mut merge_candidates = BTreeSet::new();
            for ch in query_fst(word) {
                merge_candidates.insert(ch);
            }
            candidates = BTreeSet::from_iter(candidates.intersection(&merge_candidates).cloned());
            if candidates.is_empty() {
                return vec!();
            }
        }
    }
    candidates.iter().cloned().collect()
}
<commit_msg>Don't clone an iterator, into_iter() it.<commit_after>use std::char;
use std::collections::BTreeSet;
use std::iter::FromIterator;

use fst::Map;

mod names;

const BYTES: &'static [u8] = include_bytes!("name_fst.bin");

fn query_fst(word: &str) -> Vec<char> {
    lazy_static! {
        static ref FST: Map = Map::from_bytes(BYTES.to_owned()).unwrap();
    }

    let mut chars: Vec<char> = vec!();
    if let Some(cp) = FST.get(word) {
        if cp & (0xff<<32) != 0 {
            let index: usize = (cp as u32) as usize;
            for ch in names::AMBIGUOUS_CHARS[index].chars() {
                chars.push(ch);
            }
        } else {
            char::from_u32(cp as u32).map(|ch| chars.push(ch));
        }
    }
    chars
}

pub fn lookup_by_query(query: &str) -> Vec<char> {
    let query = query.to_lowercase();
    // try the original query first:
    let original_results = query_fst(query.as_str());
    if !original_results.is_empty() {
        return original_results;
    }

    let mut candidates = BTreeSet::new();
    if query.contains(|c: char| c.is_whitespace()) {
        // Split multiple-word queries, AND them together:
        let mut words = query.split_whitespace();
        if let Some(word) = words.next() {
            for ch in query_fst(word) {
                candidates.insert(ch);
            }
        }
        for word in words {
            let mut merge_candidates = BTreeSet::new();
            for ch in query_fst(word) {
                merge_candidates.insert(ch);
            }
            candidates = BTreeSet::from_iter(candidates.intersection(&merge_candidates).cloned());
            if candidates.is_empty() {
                return vec!();
            }
        }
    }
    candidates.into_iter().collect()
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// A pass that checks to make sure private fields and methods aren't used
// outside their scopes.

use /*mod*/ syntax::ast;
use /*mod*/ syntax::visit;
use syntax::ast_map;
use syntax::ast::{def_variant, expr_field, expr_method_call, expr_struct};
use syntax::ast::{expr_unary, ident, item_class, item_enum, item_impl};
use syntax::ast::{item_trait, local_crate, node_id, pat_struct, private};
use syntax::ast::{provided, required};
use syntax::ast_map::{node_item, node_method};
use syntax::ast_util::{Private, Public, has_legacy_export_attr, is_local};
use syntax::ast_util::{visibility_to_privacy};
use ty::{ty_class, ty_enum};
use typeck::{method_map, method_origin, method_param, method_self};
use typeck::{method_static, method_trait};

use core::util::ignore;
use dvec::DVec;

fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
    let privileged_items = @DVec();
    let legacy_exports = has_legacy_export_attr(crate.node.attrs);

    // Adds structs that are privileged to this scope.
    let add_privileged_items = |items: &[@ast::item]| {
        let mut count = 0;
        for items.each |item| {
            match item.node {
                item_class(*) | item_trait(*) | item_impl(*)
                | item_enum(*) => {
                    privileged_items.push(item.id);
                    count += 1;
                }
                _ => {}
            }
        }
        count
    };

    // Checks that an enum variant is in scope
    let check_variant = |span, enum_id| {
        let variant_info = ty::enum_variants(tcx, enum_id)[0];
        let parental_privacy = if is_local(enum_id) {
            let parent_vis = ast_map::node_item_query(tcx.items, enum_id.node,
                                   |it| { it.vis },
                                   ~"unbound enum parent when checking \
                                    dereference of enum type");
            visibility_to_privacy(parent_vis, legacy_exports)
        }
        else {
            // WRONG
            Public
        };
        debug!("parental_privacy = %?", parental_privacy);
        debug!("vis = %?, priv = %?, legacy_exports = %?",
               variant_info.vis,
               visibility_to_privacy(variant_info.vis, legacy_exports),
               legacy_exports);
        // inherited => privacy of the enum item
        if visibility_to_privacy(variant_info.vis,
                                 parental_privacy == Public) == Private {
            tcx.sess.span_err(span,
                ~"can only dereference enums \
                  with a single, public variant");
        }
    };

    // Checks that a private field is in scope.
    let check_field = |span, id, ident| {
        let fields = ty::lookup_class_fields(tcx, id);
        for fields.each |field| {
            if field.ident != ident { loop; }
            if field.vis == private {
                tcx.sess.span_err(span, fmt!("field `%s` is private",
                                             *tcx.sess.parse_sess.interner
                                                 .get(ident)));
            }
            break;
        }
    };

    // Checks that a private method is in scope.
    let check_method = |span, origin: &method_origin| {
        match *origin {
            method_static(method_id) => {
                if method_id.crate == local_crate {
                    match tcx.items.find(method_id.node) {
                        Some(node_method(method, impl_id, _)) => {
                            if method.vis == private &&
                                    (impl_id.crate != local_crate ||
                                     !privileged_items
                                     .contains(&(impl_id.node))) {
                                tcx.sess.span_err(span,
                                                  fmt!("method `%s` is \
                                                        private",
                                                       *tcx.sess
                                                           .parse_sess
                                                           .interner
                                                           .get(method
                                                                .ident)));
                            }
                        }
                        Some(_) => {
                            tcx.sess.span_bug(span, ~"method wasn't \
                                                      actually a method?!");
                        }
                        None => {
                            tcx.sess.span_bug(span, ~"method not found in \
                                                      AST map?!");
                        }
                    }
                } else {
                    // XXX: External crates.
                }
            }
            method_param({trait_id: trait_id, method_num: method_num, _}) |
            method_trait(trait_id, method_num, _) |
            method_self(trait_id, method_num) => {
                if trait_id.crate == local_crate {
                    match tcx.items.find(trait_id.node) {
                        Some(node_item(item, _)) => {
                            match item.node {
                                item_trait(_, _, ref methods) => {
                                    if method_num >= (*methods).len() {
                                        tcx.sess.span_bug(span, ~"method \
                                                                  number \
                                                                  out of \
                                                                  range?!");
                                    }
                                    match (*methods)[method_num] {
                                        provided(method)
                                             if method.vis == private &&
                                             !privileged_items
                                             .contains(&(trait_id.node)) => {
                                            tcx.sess.span_err(span,
                                                              fmt!("method
                                                                    `%s` \
                                                                    is \
                                                                    private",
                                                                   *tcx
                                                                   .sess
                                                                   .parse_sess
                                                                   .interner
                                                                   .get
                                                                   (method
                                                                    .ident)));
                                        }
                                        provided(_) | required(_) => {
                                            // Required methods can't be
                                            // private.
                                        }
                                    }
                                }
                                _ => {
                                    tcx.sess.span_bug(span, ~"trait wasn't \
                                                              actually a \
                                                              trait?!");
                                }
                            }
                        }
                        Some(_) => {
                            tcx.sess.span_bug(span, ~"trait wasn't an \
                                                      item?!");
                        }
                        None => {
                            tcx.sess.span_bug(span, ~"trait item wasn't \
                                                      found in the AST \
                                                      map?!");
                        }
                    }
                } else {
                    // XXX: External crates.
                }
            }
        }
    };

    let visitor = visit::mk_vt(@{
        visit_mod: |the_module, span, node_id, method_map, visitor| {
            let n_added = add_privileged_items(the_module.items);

            visit::visit_mod(the_module, span, node_id, method_map, visitor);

            // FIXME #4054: n_added gets corrupted without this log statement
            debug!("%i", n_added);

            for n_added.times {
                ignore(privileged_items.pop());
            }
        },
        visit_expr: |expr, method_map: &method_map, visitor| {
            match expr.node {
                expr_field(base, ident, _) => {
                    match ty::get(ty::expr_ty(tcx, base)).sty {
                        ty_class(id, _)
                        if id.crate != local_crate ||
                           !privileged_items.contains(&(id.node)) => {
                            match method_map.find(expr.id) {
                                None => {
                                    debug!("(privacy checking) checking \
                                            field access");
                                    check_field(expr.span, id, ident);
                                }
                                Some(ref entry) => {
                                    debug!("(privacy checking) checking \
                                            impl method");
                                    check_method(expr.span, &(*entry).origin);
                                }
                            }
                        }
                        _ => {}
                    }
                }
                expr_method_call(base, _, _, _, _) => {
                    match ty::get(ty::expr_ty(tcx, base)).sty {
                        ty_class(id, _)
                        if id.crate != local_crate ||
                           !privileged_items.contains(&(id.node)) => {
                            match method_map.find(expr.id) {
                                None => {
                                    tcx.sess.span_bug(expr.span,
                                                      ~"method call not in \
                                                        method map");
                                }
                                Some(ref entry) => {
                                    debug!("(privacy checking) checking \
                                            impl method");
                                    check_method(expr.span, &(*entry).origin);
                                }
                            }
                        }
                        _ => {}
                    }
                }
                expr_struct(_, ref fields, _) => {
                    match ty::get(ty::expr_ty(tcx, expr)).sty {
                        ty_class(id, _) => {
                            if id.crate != local_crate ||
                                    !privileged_items.contains(&(id.node)) {
                                for (*fields).each |field| {
                                        debug!("(privacy checking) checking \
                                                field in struct literal");
                                    check_field(expr.span, id,
                                                field.node.ident);
                                }
                            }
                        }
                        ty_enum(id, _) => {
                            if id.crate != local_crate ||
                                    !privileged_items.contains(&(id.node)) {
                                match tcx.def_map.get(expr.id) {
                                    def_variant(_, variant_id) => {
                                        for (*fields).each |field| {
                                                debug!("(privacy checking) \
                                                        checking field in \
                                                        struct variant \
                                                        literal");
                                            check_field(expr.span, variant_id,
                                                        field.node.ident);
                                        }
                                    }
                                    _ => {
                                        tcx.sess.span_bug(expr.span,
                                                          ~"resolve didn't \
                                                            map enum struct \
                                                            constructor to a \
                                                            variant def");
                                    }
                                }
                            }
                        }
                        _ => {
                            tcx.sess.span_bug(expr.span, ~"struct expr \
                                                           didn't have \
                                                           struct type?!");
                        }
                    }
                }
                expr_unary(ast::deref, operand) => {
                    // In *e, we need to check that if e's type is an
                    // enum type t, then t's first variant is public or
                    // privileged. (We can assume it has only one variant
                    // since typeck already happened.)
                    match ty::get(ty::expr_ty(tcx, operand)).sty {
                        ty_enum(id, _) => {
                            if id.crate != local_crate ||
                                !privileged_items.contains(&(id.node)) {
                                check_variant(expr.span, id);
                            }
                        }
                        _ => { /* No check needed */ }
                    }
                }
                _ => {}
            }

            visit::visit_expr(expr, method_map, visitor);
        },
        visit_pat: |pattern, method_map, visitor| {
            match pattern.node {
                pat_struct(_, fields, _) => {
                    match ty::get(ty::pat_ty(tcx, pattern)).sty {
                        ty_class(id, _) => {
                            if id.crate != local_crate ||
                                    !privileged_items.contains(&(id.node)) {
                                for fields.each |field| {
                                        debug!("(privacy checking) checking \
                                                struct pattern");
                                    check_field(pattern.span, id,
                                                field.ident);
                                }
                            }
                        }
                        ty_enum(enum_id, _) => {
                            if enum_id.crate != local_crate ||
                                    !privileged_items.contains(
                                        &enum_id.node) {
                                match tcx.def_map.find(pattern.id) {
                                    Some(def_variant(_, variant_id)) => {
                                        for fields.each |field| {
                                            debug!("(privacy checking) \
                                                    checking field in \
                                                    struct variant pattern");
                                            check_field(pattern.span,
                                                        variant_id,
                                                        field.ident);
                                        }
                                    }
                                    _ => {
                                        tcx.sess.span_bug(pattern.span,
                                                          ~"resolve didn't \
                                                            map enum struct \
                                                            pattern to a \
                                                            variant def");
                                    }
                                }
                            }
                        }
                        _ => {
                            tcx.sess.span_bug(pattern.span,
                                              ~"struct pattern didn't have \
                                                struct type?!");
                        }
                    }
                }
                _ => {}
            }

            visit::visit_pat(pattern, method_map, visitor);
        },
        .. *visit::default_visitor()
    });
    visit::visit_crate(*crate, method_map, visitor);
}

<commit_msg>Remove previous x86 data corruption workaround. #4054<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// A pass that checks to make sure private fields and methods aren't used
// outside their scopes.

use /*mod*/ syntax::ast;
use /*mod*/ syntax::visit;
use syntax::ast_map;
use syntax::ast::{def_variant, expr_field, expr_method_call, expr_struct};
use syntax::ast::{expr_unary, ident, item_class, item_enum, item_impl};
use syntax::ast::{item_trait, local_crate, node_id, pat_struct, private};
use syntax::ast::{provided, required};
use syntax::ast_map::{node_item, node_method};
use syntax::ast_util::{Private, Public, has_legacy_export_attr, is_local};
use syntax::ast_util::{visibility_to_privacy};
use ty::{ty_class, ty_enum};
use typeck::{method_map, method_origin, method_param, method_self};
use typeck::{method_static, method_trait};

use core::util::ignore;
use dvec::DVec;

fn check_crate(tcx: ty::ctxt, method_map: &method_map, crate: @ast::crate) {
    let privileged_items = @DVec();
    let legacy_exports = has_legacy_export_attr(crate.node.attrs);

    // Adds structs that are privileged to this scope.
    let add_privileged_items = |items: &[@ast::item]| {
        let mut count = 0;
        for items.each |item| {
            match item.node {
                item_class(*) | item_trait(*) | item_impl(*)
                | item_enum(*) => {
                    privileged_items.push(item.id);
                    count += 1;
                }
                _ => {}
            }
        }
        count
    };

    // Checks that an enum variant is in scope
    let check_variant = |span, enum_id| {
        let variant_info = ty::enum_variants(tcx, enum_id)[0];
        let parental_privacy = if is_local(enum_id) {
            let parent_vis = ast_map::node_item_query(tcx.items, enum_id.node,
                                   |it| { it.vis },
                                   ~"unbound enum parent when checking \
                                    dereference of enum type");
            visibility_to_privacy(parent_vis, legacy_exports)
        }
        else {
            // WRONG
            Public
        };
        debug!("parental_privacy = %?", parental_privacy);
        debug!("vis = %?, priv = %?, legacy_exports = %?",
               variant_info.vis,
               visibility_to_privacy(variant_info.vis, legacy_exports),
               legacy_exports);
        // inherited => privacy of the enum item
        if visibility_to_privacy(variant_info.vis,
                                 parental_privacy == Public) == Private {
            tcx.sess.span_err(span,
                ~"can only dereference enums \
                  with a single, public variant");
        }
    };

    // Checks that a private field is in scope.
    let check_field = |span, id, ident| {
        let fields = ty::lookup_class_fields(tcx, id);
        for fields.each |field| {
            if field.ident != ident { loop; }
            if field.vis == private {
                tcx.sess.span_err(span, fmt!("field `%s` is private",
                                             *tcx.sess.parse_sess.interner
                                                 .get(ident)));
            }
            break;
        }
    };

    // Checks that a private method is in scope.
    let check_method = |span, origin: &method_origin| {
        match *origin {
            method_static(method_id) => {
                if method_id.crate == local_crate {
                    match tcx.items.find(method_id.node) {
                        Some(node_method(method, impl_id, _)) => {
                            if method.vis == private &&
                                    (impl_id.crate != local_crate ||
                                     !privileged_items
                                     .contains(&(impl_id.node))) {
                                tcx.sess.span_err(span,
                                                  fmt!("method `%s` is \
                                                        private",
                                                       *tcx.sess
                                                           .parse_sess
                                                           .interner
                                                           .get(method
                                                                .ident)));
                            }
                        }
                        Some(_) => {
                            tcx.sess.span_bug(span, ~"method wasn't \
                                                      actually a method?!");
                        }
                        None => {
                            tcx.sess.span_bug(span, ~"method not found in \
                                                      AST map?!");
                        }
                    }
                } else {
                    // XXX: External crates.
                }
            }
            method_param({trait_id: trait_id, method_num: method_num, _}) |
            method_trait(trait_id, method_num, _) |
            method_self(trait_id, method_num) => {
                if trait_id.crate == local_crate {
                    match tcx.items.find(trait_id.node) {
                        Some(node_item(item, _)) => {
                            match item.node {
                                item_trait(_, _, ref methods) => {
                                    if method_num >= (*methods).len() {
                                        tcx.sess.span_bug(span, ~"method \
                                                                  number \
                                                                  out of \
                                                                  range?!");
                                    }
                                    match (*methods)[method_num] {
                                        provided(method)
                                             if method.vis == private &&
                                             !privileged_items
                                             .contains(&(trait_id.node)) => {
                                            tcx.sess.span_err(span,
                                                              fmt!("method
                                                                    `%s` \
                                                                    is \
                                                                    private",
                                                                   *tcx
                                                                   .sess
                                                                   .parse_sess
                                                                   .interner
                                                                   .get
                                                                   (method
                                                                    .ident)));
                                        }
                                        provided(_) | required(_) => {
                                            // Required methods can't be
                                            // private.
                                        }
                                    }
                                }
                                _ => {
                                    tcx.sess.span_bug(span, ~"trait wasn't \
                                                              actually a \
                                                              trait?!");
                                }
                            }
                        }
                        Some(_) => {
                            tcx.sess.span_bug(span, ~"trait wasn't an \
                                                      item?!");
                        }
                        None => {
                            tcx.sess.span_bug(span, ~"trait item wasn't \
                                                      found in the AST \
                                                      map?!");
                        }
                    }
                } else {
                    // XXX: External crates.
                }
            }
        }
    };

    let visitor = visit::mk_vt(@{
        visit_mod: |the_module, span, node_id, method_map, visitor| {
            let n_added = add_privileged_items(the_module.items);

            visit::visit_mod(the_module, span, node_id, method_map, visitor);

            for n_added.times {
                ignore(privileged_items.pop());
            }
        },
        visit_expr: |expr, method_map: &method_map, visitor| {
            match expr.node {
                expr_field(base, ident, _) => {
                    match ty::get(ty::expr_ty(tcx, base)).sty {
                        ty_class(id, _)
                        if id.crate != local_crate ||
                           !privileged_items.contains(&(id.node)) => {
                            match method_map.find(expr.id) {
                                None => {
                                    debug!("(privacy checking) checking \
                                            field access");
                                    check_field(expr.span, id, ident);
                                }
                                Some(ref entry) => {
                                    debug!("(privacy checking) checking \
                                            impl method");
                                    check_method(expr.span, &(*entry).origin);
                                }
                            }
                        }
                        _ => {}
                    }
                }
                expr_method_call(base, _, _, _, _) => {
                    match ty::get(ty::expr_ty(tcx, base)).sty {
                        ty_class(id, _)
                        if id.crate != local_crate ||
                           !privileged_items.contains(&(id.node)) => {
                            match method_map.find(expr.id) {
                                None => {
                                    tcx.sess.span_bug(expr.span,
                                                      ~"method call not in \
                                                        method map");
                                }
                                Some(ref entry) => {
                                    debug!("(privacy checking) checking \
                                            impl method");
                                    check_method(expr.span, &(*entry).origin);
                                }
                            }
                        }
                        _ => {}
                    }
                }
                expr_struct(_, ref fields, _) => {
                    match ty::get(ty::expr_ty(tcx, expr)).sty {
                        ty_class(id, _) => {
                            if id.crate != local_crate ||
                                    !privileged_items.contains(&(id.node)) {
                                for (*fields).each |field| {
                                        debug!("(privacy checking) checking \
                                                field in struct literal");
                                    check_field(expr.span, id,
                                                field.node.ident);
                                }
                            }
                        }
                        ty_enum(id, _) => {
                            if id.crate != local_crate ||
                                    !privileged_items.contains(&(id.node)) {
                                match tcx.def_map.get(expr.id) {
                                    def_variant(_, variant_id) => {
                                        for (*fields).each |field| {
                                                debug!("(privacy checking) \
                                                        checking field in \
                                                        struct variant \
                                                        literal");
                                            check_field(expr.span, variant_id,
                                                        field.node.ident);
                                        }
                                    }
                                    _ => {
                                        tcx.sess.span_bug(expr.span,
                                                          ~"resolve didn't \
                                                            map enum struct \
                                                            constructor to a \
                                                            variant def");
                                    }
                                }
                            }
                        }
                        _ => {
                            tcx.sess.span_bug(expr.span, ~"struct expr \
                                                           didn't have \
                                                           struct type?!");
                        }
                    }
                }
                expr_unary(ast::deref, operand) => {
                    // In *e, we need to check that if e's type is an
                    // enum type t, then t's first variant is public or
                    // privileged. (We can assume it has only one variant
                    // since typeck already happened.)
                    match ty::get(ty::expr_ty(tcx, operand)).sty {
                        ty_enum(id, _) => {
                            if id.crate != local_crate ||
                                !privileged_items.contains(&(id.node)) {
                                check_variant(expr.span, id);
                            }
                        }
                        _ => { /* No check needed */ }
                    }
                }
                _ => {}
            }

            visit::visit_expr(expr, method_map, visitor);
        },
        visit_pat: |pattern, method_map, visitor| {
            match pattern.node {
                pat_struct(_, fields, _) => {
                    match ty::get(ty::pat_ty(tcx, pattern)).sty {
                        ty_class(id, _) => {
                            if id.crate != local_crate ||
                                    !privileged_items.contains(&(id.node)) {
                                for fields.each |field| {
                                        debug!("(privacy checking) checking \
                                                struct pattern");
                                    check_field(pattern.span, id,
                                                field.ident);
                                }
                            }
                        }
                        ty_enum(enum_id, _) => {
                            if enum_id.crate != local_crate ||
                                    !privileged_items.contains(
                                        &enum_id.node) {
                                match tcx.def_map.find(pattern.id) {
                                    Some(def_variant(_, variant_id)) => {
                                        for fields.each |field| {
                                            debug!("(privacy checking) \
                                                    checking field in \
                                                    struct variant pattern");
                                            check_field(pattern.span,
                                                        variant_id,
                                                        field.ident);
                                        }
                                    }
                                    _ => {
                                        tcx.sess.span_bug(pattern.span,
                                                          ~"resolve didn't \
                                                            map enum struct \
                                                            pattern to a \
                                                            variant def");
                                    }
                                }
                            }
                        }
                        _ => {
                            tcx.sess.span_bug(pattern.span,
                                              ~"struct pattern didn't have \
                                                struct type?!");
                        }
                    }
                }
                _ => {}
            }

            visit::visit_pat(pattern, method_map, visitor);
        },
        .. *visit::default_visitor()
    });
    visit::visit_crate(*crate, method_map, visitor);
}

<|endoftext|>