<commit_before>#![feature(test)]

#![allow(dead_code)]

extern crate test;

extern crate polydraw;

use std::cmp::{min, max};
use std::i64;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::RGB;
use polydraw::geom::point::Point;
use polydraw::geom::triangle::Triangle;
use polydraw::geom::clip::{h_split_edge, v_split_edge, hv_split, Ring};

const DIV_PER_PIXEL: i64 = 1000;
const HALF_DIV_PER_PIXEL: i64 = DIV_PER_PIXEL / 2;
const DOUBLE_PIXEL_AREA: i64 = DIV_PER_PIXEL * DIV_PER_PIXEL * 2;

#[inline]
fn to_px(v: i64) -> i64 {
   v / DIV_PER_PIXEL
}

#[inline]
fn from_px(v: i64) -> i64 {
   v as i64 * DIV_PER_PIXEL
}

struct TriangleRenderer {
   tr: Triangle,
   colors: Vec<RGB>,
   up: Ring<Point>,
   right: Ring<Point>,
   left: Ring<Point>,
}

impl TriangleRenderer {
   fn new() -> Self {
      let tr = Triangle::new(
         Point::new(from_px(100), from_px(450)),
         Point::new(from_px(600), from_px(600)),
         Point::new(from_px(350), from_px(100)),
      );

      let mut colors = Vec::with_capacity(256);
      for i in 0..256_usize {
         colors.push(
            RGB::new(
               (i * 127 / 255) as u8, (i * 223 / 255) as u8, i as u8
            )
         );
      }

      TriangleRenderer {
         tr: tr,
         colors: colors,
         up: Ring::new(131072),
         right: Ring::new(524288),
         left: Ring::new(524288),
      }
   }
}

impl Renderer for TriangleRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      self.tr.a.x += DIV_PER_PIXEL;
      if self.tr.a.x >= from_px(frame.width as i64) {
         self.tr.a.x = 0;
      }

      if self.tr.clockwise() {
         self.tr.change_orientation();
      }

      let a = self.tr.a;
      let b = self.tr.b;
      let c = self.tr.c;

      let min_y = max(to_px(min3(a.y, b.y, c.y)), 0);
      let max_y = min(to_px(max3(a.y, b.y, c.y)), frame.height as i64 - 1);

      self.up.push(a); self.up.push(b); self.up.push(c);

      for y in min_y..max_y+1 {
         let y_world = from_px(y);
         let y_split = y_world + DIV_PER_PIXEL;

         hv_split(h_split_edge, y_split, &mut self.right, &mut self.up);

         let (min_x, max_x) = min_max_x(&self.right);

         for x in min_x..max_x {
            let x_world = from_px(x);
            let x_split = x_world + DIV_PER_PIXEL;

            hv_split(v_split_edge, x_split, &mut self.left, &mut self.right);

            plot_poly_pixel(frame, x, y, &self.left, &self.colors);

            self.left.consume();
         }

         plot_poly_pixel(frame, max_x, y, &self.right, &self.colors);

         self.right.consume();
      }
   }
}

#[inline]
fn print_points(name: &str, points: &Ring<Point>) {
   print!("{}: ", name);

   print!("s {} e {} ", points.start(), points.end());

   for p in points[..].iter() {
      print!("({:?}, {:?}) ", p.x, p.y);
   }

   println!("");
}

#[inline]
fn plot_poly_pixel(frame: &mut Frame, x: i64, y: i64, points: &Ring<Point>, colors: &Vec<RGB>) {
   let area = double_area(points);

   assert!(area >= 0);

   frame.put_pixel(x as i32, y as i32, &colors[(255 * area / DOUBLE_PIXEL_AREA) as usize]);
}

#[inline]
fn min_max_x(points: &Ring<Point>) -> (i64, i64) {
   let mut min_x = i64::MAX;
   let mut max_x = i64::MIN;
   for p in points[..].iter() {
      if p.x > max_x {
         max_x = p.x;
      }

      if p.x < min_x {
         min_x = p.x;
      }
   }

   (to_px(min_x), to_px(max_x))
}

#[inline]
pub fn min3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   min(min(v1, v2), v3)
}

#[inline]
pub fn max3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   max(max(v1, v2), v3)
}

#[inline]
pub fn double_area(points: &Ring<Point>) -> i64 {
   if points.len() <= 2 {
      return 0;
   }

   let mut p1 = points.last().unwrap();

   let mut area = 0;

   for p2 in points[..].iter() {
      area += p1.x * p2.y - p1.y * p2.x;

      p1 = p2;
   }

   area
}

fn main() {
   let mut renderer = TriangleRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Triangle")
      .run();
}
<commit_msg>Optimizing the triangle rasterization by detecting solid color horizontal spans<commit_after>#![feature(test)]

#![allow(dead_code)]

extern crate test;

extern crate polydraw;

use std::cmp::{min, max};
use std::i64;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::RGB;
use polydraw::geom::point::Point;
use polydraw::geom::triangle::Triangle;
use polydraw::geom::clip::{h_split_edge, v_split_edge, hv_split, Ring};

const DIV_PER_PIXEL: i64 = 1000;
const HALF_DIV_PER_PIXEL: i64 = DIV_PER_PIXEL / 2;
const DOUBLE_PIXEL_AREA: i64 = DIV_PER_PIXEL * DIV_PER_PIXEL * 2;

#[inline]
fn to_px(v: i64) -> i64 {
   v / DIV_PER_PIXEL
}

#[inline]
fn from_px(v: i64) -> i64 {
   v as i64 * DIV_PER_PIXEL
}

struct TriangleRenderer {
   tr: Triangle,
   colors: Vec<RGB>,
   up: Ring<Point>,
   right: Ring<Point>,
   left: Ring<Point>,
}

impl TriangleRenderer {
   fn new() -> Self {
      let tr = Triangle::new(
         Point::new(from_px(100), from_px(450)),
         Point::new(from_px(600), from_px(600)),
         Point::new(from_px(350), from_px(100)),
      );

      let mut colors = Vec::with_capacity(256);
      for i in 0..256_usize {
         colors.push(
            RGB::new(
               (i * 127 / 255) as u8, (i * 223 / 255) as u8, i as u8
            )
         );
      }

      TriangleRenderer {
         tr: tr,
         colors: colors,
         up: Ring::new(131072),
         right: Ring::new(524288),
         left: Ring::new(524288),
      }
   }

   fn solid_follow(&mut self, x_split: i64, y_world: i64, y_split: i64) -> Option<i64> {
      let right_start = self.right.start();
      let right_end = self.right.end();

      if right_end - right_start > 3 {
         let top_left = Point::new(x_split, y_split);
         for top_left_i in right_start..right_end {
            if self.right[top_left_i] == top_left {

               let bottom_left = Point::new(x_split, y_world);
               let bottom_left_i = self.right.next_index(top_left_i);
               if self.right[bottom_left_i] == bottom_left {

                  let bottom_right_i = self.right.next_index(bottom_left_i);
                  if self.right[bottom_right_i].y == y_world {

                     let top_right_i = self.right.prev_index(top_left_i);
                     if self.right[top_right_i].y == y_split {
                        let delta_min = min(
                           self.right[top_right_i].x,
                           self.right[bottom_right_i].x
                        ) - x_split;

                        if delta_min >= DIV_PER_PIXEL {
                           return Some(to_px(delta_min));
                        }
                     }
                  }
               }
            }
         }
      }

      None
   }
}

impl Renderer for TriangleRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      self.tr.a.x += DIV_PER_PIXEL;
      if self.tr.a.x >= from_px(frame.width as i64) {
         self.tr.a.x = 0;
      }

      if self.tr.clockwise() {
         self.tr.change_orientation();
      }

      let a = self.tr.a;
      let b = self.tr.b;
      let c = self.tr.c;

      let min_y = max(to_px(min3(a.y, b.y, c.y)), 0);
      let max_y = min(to_px(max3(a.y, b.y, c.y)), frame.height as i64 - 1);

      self.up.push(a); self.up.push(b); self.up.push(c);

      for y in min_y..max_y+1 {
         let y_world = from_px(y);
         let y_split = y_world + DIV_PER_PIXEL;

         hv_split(h_split_edge, y_split, &mut self.right, &mut self.up);

         let (min_x, max_x) = min_max_x(&self.right);

         let mut x = min_x;

         while x < max_x {
            let x_world = from_px(x);
            let x_split = x_world + DIV_PER_PIXEL;

            hv_split(v_split_edge, x_split, &mut self.left, &mut self.right);

            plot_poly_pixel(frame, x, y, &self.left, &self.colors);

            self.left.consume();

            x += 1;

            match self.solid_follow(x_split, y_world, y_split) {
               Some(delta) => {
                  let x_split_right = x_split + from_px(delta);

                  hv_split(v_split_edge, x_split_right, &mut self.left, &mut self.right);

                  for solid_x in x..x + delta {
                     frame.put_pixel(solid_x as i32, y as i32, &self.colors[255]);
                  }

                  self.left.consume();

                  x += delta;
               },
               None => {}
            }
         }

         plot_poly_pixel(frame, max_x, y, &self.right, &self.colors);

         self.right.consume();
      }
   }
}

#[inline]
fn print_points(name: &str, points: &Ring<Point>) {
   print!("{}: ", name);

   print!("s {} e {} ", points.start(), points.end());

   for p in points[..].iter() {
      print!("({:?}, {:?}) ", p.x, p.y);
   }

   println!("");
}

#[inline]
fn plot_poly_pixel(frame: &mut Frame, x: i64, y: i64, points: &Ring<Point>, colors: &Vec<RGB>) {
   let area = double_area(points);

   assert!(area >= 0);

   frame.put_pixel(x as i32, y as i32, &colors[(255 * area / DOUBLE_PIXEL_AREA) as usize]);
}

#[inline]
fn min_max_x(points: &Ring<Point>) -> (i64, i64) {
   let mut min_x = i64::MAX;
   let mut max_x = i64::MIN;
   for p in points[..].iter() {
      if p.x > max_x {
         max_x = p.x;
      }

      if p.x < min_x {
         min_x = p.x;
      }
   }

   (to_px(min_x), to_px(max_x))
}

#[inline]
pub fn min3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   min(min(v1, v2), v3)
}

#[inline]
pub fn max3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   max(max(v1, v2), v3)
}

#[inline]
pub fn double_area(points: &Ring<Point>) -> i64 {
   if points.len() <= 2 {
      return 0;
   }

   let mut p1 = points.last().unwrap();

   let mut area = 0;

   for p2 in points[..].iter() {
      area += p1.x * p2.y - p1.y * p2.x;

      p1 = p2;
   }

   area
}

fn main() {
   let mut renderer = TriangleRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Triangle")
      .run();
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::vec::Vec;

// Convert the word to a map from charaters to the number of times that character appears.
fn construct_letter_map(word: &str) -> HashMap<char, u8> {
    let mut map = HashMap::new();
    for letter in word.chars() {
        *map.entry(letter).or_insert(0) += 1
    }
    map
}

// Compare if `left` is a subset of `right`.
fn letter_map_less_than(left: &HashMap<char, u8>, right: &HashMap<char, u8>) -> bool {
    for (letter, l_count) in left {
        match right.get(letter) {
            Some(r_count) => if l_count > r_count {
                return false;
            },
            _ => return false,
        }
    }
    true
}

fn main() {
    let f = match File::open("dictionary.txt") {
        Ok(f) => f,
        Err(why) => panic!("Failed to open links.txt: {}", Error::description(&why)),
    };

    // Construct a vector of three letter words that are a subset of beermouth.
    let source_word = construct_letter_map("beermouth");
    let words: Vec<String> = BufReader::new(f)
        .lines()
        .map(|s| s.unwrap())
        .filter(|s| s.len() == 3)
        .filter(|s| letter_map_less_than(&construct_letter_map(s), &source_word))
        .map(|s| s)
        .collect();

    for word in words {
        println!("{}", word);
    }
}
<commit_msg>First working version<commit_after>/*
  This code finds a solution to the NPR puzzle presented on Oct 21, 2018.

  A (paraphrased) statement of the problem:
    Given the letters in the word 'beermouth', contruct a three by three matrix of words where all
    the verticals, horizontals, and diagonals form three letter words.
*/

use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::vec::Vec;

// Gets the nth char from `word`.
fn get_char(word: &str, index: usize) -> char {
    word.chars().nth(index).unwrap()
}

// Convert the word to a map from charaters to the number of times that character appears.
fn construct_letter_map(word: &str) -> HashMap<char, u8> {
    let mut map = HashMap::new();
    for letter in word.chars() {
        *map.entry(letter).or_insert(0) += 1
    }
    map
}

// Compare if `left` is a subset of `right`.
fn letter_map_less_than(left: &HashMap<char, u8>, right: &HashMap<char, u8>) -> bool {
    for (letter, l_count) in left {
        match right.get(letter) {
            Some(r_count) => if l_count > r_count {
                return false;
            },
            _ => return false,
        }
    }
    true
}

// The middle letter has to be the middle letter of at least four words. Construct lists of
// possible sets of words.
fn shared_middle_letters(words: &[String]) -> Vec<Vec<&String>> {
    let mut map = HashMap::new();
    for word in words {
        map.entry(get_char(word, 1))
            .or_insert(Vec::new())
            .push(word)
    }
    let mut out = Vec::new();
    for (_, val) in map.drain() {
        if val.len() >= 4 {
            out.push(val);
        }
    }
    out
}

// Checks that the first and last letters of `word` are still in `right`.
fn two_letter_less_than(word: &str, right: &HashMap<char, u8>) -> bool {
    for letter in [get_char(word, 0), get_char(word, 2)].iter() {
        match right.get(letter) {
            Some(count) => if *count <= 0 {
                return false;
            },
            _ => return false,
        }
    }
    true
}

// Checks if the word is a subset of the letters in `letter_map`. If so, it decrements the counts
// in `letter_map` by the first and third letters of the word. Ignores the middle letter since that
// is shared.
// Returns whether or not the word was a subset.
fn clear_two_letters(word: &str, letter_map: &mut HashMap<char, u8>) -> bool {
    if !two_letter_less_than(word, letter_map) {
        return false;
    }
    *letter_map.get_mut(&get_char(word, 0)).unwrap() -= 1;
    *letter_map.get_mut(&get_char(word, 2)).unwrap() -= 1;
    true
}

// If the word is a candidate, puts it in the slot provided by `index`. Otherwise, do nothing.
// Returns whether or not the word was inserted.
fn append_word_if<'a>(
    word: &'a String,
    letter_map: &mut HashMap<char, u8>,
    words: &mut Vec<&'a String>,
) -> bool {
    if !clear_two_letters(word, letter_map) {
        return false;
    }
    words.push(word);
    true
}

fn four_word_product<'a>(
    words: &[&'a String],
    base_map: &HashMap<char, u8>,
) -> Vec<Vec<&'a String>> {
    let mut out = Vec::new();
    let middle_letter = get_char(words[0], 1);
    for ii in 0..words.len() {
        let mut map = base_map.clone();
        let mut four_words = Vec::new();
        *map.get_mut(&middle_letter).unwrap() -= 1;
        if !append_word_if(words[ii], &mut map, &mut four_words) {
            continue;
        }
        assert_eq!(four_words.len(), 1);
        for jj in ii + 1..words.len() {
            // Truncate here so that we only get one word from this pass.
            four_words.truncate(1);
            // Create a new map per iteration.
            let mut map = map.clone();
            if !append_word_if(words[jj], &mut map, &mut four_words) {
                continue;
            }
            assert_eq!(four_words.len(), 2);
            for kk in jj + 1..words.len() {
                four_words.truncate(2);
                let mut map = map.clone();
                if !append_word_if(words[kk], &mut map, &mut four_words) {
                    continue;
                }
                assert_eq!(four_words.len(), 3);
                for ll in kk + 1..words.len() {
                    four_words.truncate(3);
                    let mut map = map.clone();
                    if append_word_if(words[ll], &mut map, &mut four_words) {
                        assert_eq!(four_words.len(), 4);
                        out.push(four_words.clone());
                    }
                }
            }
        }
    }
    out
}

// Find all of the combinations of four words that share a middle letter and the combined letters
// of all four words are still a subset of 'base_map'.
fn four_word_candidates<'a>(
    shared_middle: &[Vec<&'a String>],
    base_map: &HashMap<char, u8>,
) -> Vec<Vec<&'a String>> {
    let mut out = Vec::new();
    for words in shared_middle {
        out.append(&mut four_word_product(words, base_map));
    }
    out
}

// For four indices between zero and 4, return the missing index.
fn missing_index(ii: usize, jj: usize, kk: usize) -> usize {
    assert!(ii < 4);
    assert!(jj < 4);
    assert!(kk < 4);
    for ll in 0..4 {
        if ll != ii && ll != jj && ll != kk {
            return ll;
        }
    }
    unreachable!()
}

// Takes the candidates and finds words that make a proper word in the top row. The words are
// returned such that the first word is the left to right diagonal (1, 5, 9 on a phone), the second
// word is the middle vertical, the third is the right to left diagonal (3, 5, 7) and the remaining
// word is last.
fn top_row_candidates<'a>(
    four_cands: &Vec<Vec<&'a String>>,
    possible_words: &HashSet<&String>,
) -> Vec<Vec<&'a String>> {
    let mut out = Vec::new();
    for cand in four_cands {
        assert!(cand.len() == 4);
        for ii in 0..cand.len() {
            let first = cand[ii];
            for jj in 0..cand.len() {
                if jj == ii {
                    continue;
                }
                let second = cand[jj];
                for kk in 0..cand.len() {
                    if kk == jj || kk == ii {
                        continue;
                    }
                    let third = cand[kk];
                    let word: String = [
                        first.chars().next().unwrap(),
                        second.chars().next().unwrap(),
                        third.chars().next().unwrap(),
                    ]
                        .iter()
                        .collect();

                    if possible_words.contains(&word) {
                        let mut top_cand = vec![first, second, third];
                        top_cand.push(cand[missing_index(ii, jj, kk)]);
                        out.push(top_cand);
                    }
                }
            }
        }
    }
    out
}

// Filter out sets of four where the bottom generated from the top three words isn't a word.
fn filter_by_bottom<'a>(
    top_cands: Vec<Vec<&'a String>>,
    possible_words: &HashSet<&String>,
) -> Vec<Vec<&'a String>> {
    let mut out = Vec::new();
    for cand in top_cands {
        assert!(cand.len() == 4);
        let first = get_char(cand[0], 2);
        let second = get_char(cand[1], 2);
        let third = get_char(cand[2], 2);
        let word: String = [first, second, third].iter().collect();
        if possible_words.contains(&word) {
            out.push(cand);
            continue;
        }
    }
    out
}

fn filter_by_rest<'a>(
    cands: Vec<Vec<&'a String>>,
    possible_words: &HashSet<&String>,
) -> Vec<Vec<&'a String>> {
    let mut out = Vec::new();
    for cand in cands {
        // The left vertical word is made up of the first letter of the first top word (left to
        // right diagnol), the first letter of the remaining word, and the last letter of the
        // third top word (right to left diagonal).
        let left_v_word: String = [
            get_char(cand[0], 0),
            get_char(cand[3], 0),
            get_char(cand[2], 2),
        ]
            .iter()
            .collect();
        if !possible_words.contains(&left_v_word) {
            continue;
        }
        // The right vertical word is from the first letter of the third top word, the third letter
        // of the remaining word and the third letter of the first top word.
        let right_v_word: String = [
            get_char(cand[2], 0),
            get_char(cand[3], 2),
            get_char(cand[0], 2),
        ]
            .iter()
            .collect();
        if !possible_words.contains(&right_v_word) {
            continue;
        }

        out.push(cand);
    }
    out
}

// Returns the 3x3 grid of letters in row major form.
fn presentation_format(cand: &[&String]) -> Vec<Vec<char>> {
    let mut out = Vec::new();
    // Fill in the vectors with x's just as a placeholder.
    for _ in 0..3 {
        out.push(vec!['x', 'x', 'x']);
    }
    // The first word is the left to right diagonal.
    out[0][0] = get_char(cand[0], 0);
    out[1][1] = get_char(cand[0], 1);
    out[2][2] = get_char(cand[0], 2);
    // The second word is the middle vertical.
    out[0][1] = get_char(cand[1], 0);
    out[2][1] = get_char(cand[1], 2);
    // The third word is the left diagonal.
    out[0][2] = get_char(cand[2], 0);
    out[2][0] = get_char(cand[2], 2);
    // The last word is the middle horizontal.
    out[1][0] = get_char(cand[3], 0);
    out[1][2] = get_char(cand[3], 2);
    out
}

fn main() {
    let f = match File::open("dictionary.txt") {
        Ok(f) => f,
        Err(why) => panic!("Failed to open links.txt: {}", Error::description(&why)),
    };

    // Construct a vector of three letter words that are a subset of beermouth.
    let source_word = construct_letter_map("beermouth");
    let words: Vec<String> = BufReader::new(f)
        .lines()
        .map(|s| s.unwrap())
        .filter(|s| s.len() == 3)
        .filter(|s| letter_map_less_than(&construct_letter_map(s), &source_word))
        .map(|s| s)
        .collect();

    // Find sets of words that share a middle letter.
    let middle_candidates = shared_middle_letters(&words);
    // Find sets of four words that share a middle letter and could make up the solution.
    let four_cands = four_word_candidates(&middle_candidates, &source_word);

    // Find possible top rows.
    let word_set = {
        let mut word_set = HashSet::with_capacity(words.len());
        for word in words.iter() {
            word_set.insert(word);
        }
        word_set
    };
    let top_cands = top_row_candidates(&four_cands, &word_set);
    let bottom_cands = filter_by_bottom(top_cands, &word_set);
    let last_cands = filter_by_rest(bottom_cands, &word_set);

    for last_cand in last_cands {
        println!("Last candidate words:");
        for word in last_cand.iter() {
            print!("{} ", word);
        }
        println!("\nIn grid format:");
        let present = presentation_format(&last_cand);
        for line in present {
            for ch in line {
                print!(" {} ", ch);
            }
            print!("\n");
        }
        print!("\n");
    }
}

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

    #[test]
    fn test_letter_map() {
        let mut expected = HashMap::new();
        expected.insert('b', 1);
        expected.insert('e', 2);
        expected.insert('r', 1);
        expected.insert('m', 1);
        expected.insert('o', 1);
        expected.insert('u', 1);
        expected.insert('t', 1);
        expected.insert('h', 1);
        assert_eq!(expected, construct_letter_map("beermouth"));
    }

    #[test]
    fn test_map_less_than() {
        let source_word = construct_letter_map("beermouth");
        assert!(letter_map_less_than(
            &construct_letter_map("bee"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("bum"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("but"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("out"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("boo"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("oom"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("ouf"),
            &source_word
        ));
    }

    #[test]
    fn test_shared_middle() {
        let mut words = Vec::new();
        words.push("bee".to_string());
        words.push("bet".to_string());
        words.push("met".to_string());
        words.push("bum".to_string());
        words.push("but".to_string());
        words.push("out".to_string());
        words.push("ouf".to_string());
        words.push("bot".to_string());
        words.push("hot".to_string());
        words.push("mot".to_string());
        words.push("tom".to_string());
        words.push("rot".to_string());
        let mut expected = Vec::new();
        {
            let mut group = Vec::new();
            for ii in 3..7 {
                group.push(words[ii]);
            }
            group.sort();
            expected.push(group);
        }
        {
            let mut group = Vec::new();
            for ii in 7..12 {
                group.push(words[ii]);
            }
            group.sort();
            expected.push(group);
        }
        let mut input_words = words.clone();
        input_words.sort();
        assert_eq!(expected, shared_middle_letters(&input_words));
    }
}
<|endoftext|>


<commit_before>extern crate ctest;

use std::env;

fn main() {
    let mut cfg = ctest::TestGenerator::new();

    if let Ok(out) = env::var("DEP_CURL_INCLUDE") {
        cfg.include(&out);
    }

    if env::var("TARGET").unwrap().contains("msvc") {
        cfg.flag("/wd4574"); // did you mean to use '#if INCL_WINSOCK_API_TYPEDEFS'
    }

    cfg.header("curl/curl.h");
    cfg.define("CURL_STATICLIB", None);
    cfg.field_name(|s, field| {
        if s == "curl_fileinfo" {
            field.replace("strings_", "strings.")
        } else if s == "CURLMsg" && field == "data" {
            "data.whatever".to_string()
        } else {
            field.to_string()
        }
    });
    cfg.type_name(|s, is_struct| {
        match s {
            "CURL" |
            "CURLM" |
            "CURLSH" |
            "curl_version_info_data" => s.to_string(),
            "curl_khtype" |
            "curl_khstat" |
            "curl_khmatch" => format!("enum {}", s),
            s if is_struct => format!("struct {}", s),
            "sockaddr" => format!("struct sockaddr"),
            s => s.to_string(),
        }
    });
    // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string());
    cfg.skip_type(|n| n == "__enum_ty");
    cfg.skip_signededness(|s| {
        s.ends_with("callback") || s.ends_with("function")
    });

    cfg.skip_const(|s| {
        // Ubuntu Xenial 16.04 ships with version 7.47.0 of curl, so explicitly
        // skip constants introduced after that.
        // CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE was introduced in 7.49.0
        s == "CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE" ||

        // OSX doesn't have this yet
        s == "CURLSSLOPT_NO_REVOKE" ||

        // Disable HTTP/2 checking if feature not enabled
        (!cfg!(feature = "http2") && s.starts_with("CURL_HTTP_VERSION_2")) ||

        // A lot of curl versions doesn't support unix sockets
        s == "CURLOPT_UNIX_SOCKET_PATH" || s == "CURL_VERSION_UNIX_SOCKETS"
    });

    cfg.generate("../curl-sys/lib.rs", "all.rs");
}
<commit_msg>systest: do not check function pointers on msvc<commit_after>extern crate ctest;

use std::env;

fn main() {
    let mut cfg = ctest::TestGenerator::new();

    if let Ok(out) = env::var("DEP_CURL_INCLUDE") {
        cfg.include(&out);
    }

    if env::var("TARGET").unwrap().contains("msvc") {
        cfg.flag("/wd4574"); // did you mean to use '#if INCL_WINSOCK_API_TYPEDEFS'
    }

    cfg.header("curl/curl.h");
    cfg.define("CURL_STATICLIB", None);
    cfg.field_name(|s, field| {
        if s == "curl_fileinfo" {
            field.replace("strings_", "strings.")
        } else if s == "CURLMsg" && field == "data" {
            "data.whatever".to_string()
        } else {
            field.to_string()
        }
    });
    cfg.type_name(|s, is_struct| {
        match s {
            "CURL" |
            "CURLM" |
            "CURLSH" |
            "curl_version_info_data" => s.to_string(),
            "curl_khtype" |
            "curl_khstat" |
            "curl_khmatch" => format!("enum {}", s),
            s if is_struct => format!("struct {}", s),
            "sockaddr" => format!("struct sockaddr"),
            s => s.to_string(),
        }
    });
    // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string());
    cfg.skip_type(|n| n == "__enum_ty");
    cfg.skip_signededness(|s| {
        s.ends_with("callback") || s.ends_with("function")
    });

    cfg.skip_const(|s| {
        // Ubuntu Xenial 16.04 ships with version 7.47.0 of curl, so explicitly
        // skip constants introduced after that.
        // CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE was introduced in 7.49.0
        s == "CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE" ||

        // OSX doesn't have this yet
        s == "CURLSSLOPT_NO_REVOKE" ||

        // Disable HTTP/2 checking if feature not enabled
        (!cfg!(feature = "http2") && s.starts_with("CURL_HTTP_VERSION_2")) ||

        // A lot of curl versions doesn't support unix sockets
        s == "CURLOPT_UNIX_SOCKET_PATH" || s == "CURL_VERSION_UNIX_SOCKETS"
    });

    if cfg!(target_env = "msvc") {
        cfg.skip_fn_ptrcheck(|s| s.starts_with("curl_"));
    }

    cfg.generate("../curl-sys/lib.rs", "all.rs");
}
<|endoftext|>


<commit_before>use std::sync::Mutex;
use stopwatch;
use time;

use common::protocol;
use common::surroundings_loader;
use common::surroundings_loader::LoadType;

use audio_thread;
use chunk;
use client;
use lod;
use server_update::apply_server_update;
use terrain;
use view;

const MAX_OUTSTANDING_TERRAIN_REQUESTS: u32 = 1;

pub fn update_thread<RecvServer, UpdateView0, UpdateView1, UpdateAudio, UpdateServer, EnqueueTerrainUpdate>(
  quit                   : &Mutex<bool>,
  client                 : &client::T,
  recv_server            : &mut RecvServer,
  update_view0           : &mut UpdateView0,
  update_view1           : &mut UpdateView1,
  update_audio           : &mut UpdateAudio,
  update_server          : &mut UpdateServer,
  enqueue_terrain_update : &mut EnqueueTerrainUpdate,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView0          : FnMut(view::update::T),
  UpdateView1          : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainUpdate : FnMut(terrain::Load),
{
  'update_loop: loop {
    let should_quit = *quit.lock().unwrap();
    if should_quit {
      break 'update_loop
    } else {
      stopwatch::time("update_iteration", || {
        stopwatch::time("process_server_updates", || {
          process_server_updates(client, recv_server, update_view0, update_audio, update_server, enqueue_terrain_update);
        });

        stopwatch::time("update_surroundings", || {
          update_surroundings(client, update_view1, update_server);
        });

        stopwatch::time("process_voxel_updates", || {
          process_voxel_updates(client, update_view1);
        });
      })
    }
  }
}

#[inline(never)]
fn update_surroundings<UpdateView, UpdateServer>(
  client        : &client::T,
  update_view   : &mut UpdateView,
  update_server : &mut UpdateServer,
) where
  UpdateView   : FnMut(view::update::T),
  UpdateServer : FnMut(protocol::ClientToServer),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  let load_position = {
    let load_position = *client.load_position.lock().unwrap();
    load_position.unwrap_or_else(|| *client.player_position.lock().unwrap())
  };
  let load_position = chunk::position::of_world_position(&load_position);
  let mut surroundings_loader = client.surroundings_loader.lock().unwrap();
  let mut updates = surroundings_loader.updates(load_position.as_pnt()) ;
  loop {
    if *client.pending_terrain_requests.lock().unwrap() >= MAX_OUTSTANDING_TERRAIN_REQUESTS {
      trace!("update loop breaking");
      break;
    }

    let chunk_position;
    let load_type;
    match updates.next() {
      None => break,
      Some((b, l)) => {
        chunk_position = chunk::position::of_pnt(&b);
        load_type = l;
      },
    }

    debug!("chunk surroundings");
    let distance =
      surroundings_loader::distance_between(
        load_position.as_pnt(),
        chunk_position.as_pnt(),
      );
    match load_type {
      LoadType::Load => {
        stopwatch::time("update_thread.load_chunk", || {
          trace!("Loading distance {}", distance);
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          if load_state == Some(new_lod) {
            debug!("Not re-loading {:?} at {:?}", chunk_position, new_lod);
          } else {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          }
        })
      },
      LoadType::Downgrade => {
        stopwatch::time("update_thread.update_chunk", || {
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          let is_downgrade = load_state.map(|lod| new_lod < lod) == Some(true);
          if is_downgrade {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          } else {
            trace!("Not updating {:?} at {:?}", chunk_position, new_lod);
          }
        })
      },
      LoadType::Unload => {
        stopwatch::time("update_thread.unload", || {
          client.terrain.lock().unwrap().unload(update_view, &chunk_position);
        })
      },
    };

    if i >= 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}

fn load_or_request_chunk<UpdateServer, UpdateView>(
  client         : &client::T,
  update_server  : &mut UpdateServer,
  update_view    : &mut UpdateView,
  chunk_position : &chunk::position::T,
  lod            : lod::T,
) where
  UpdateServer: FnMut(protocol::ClientToServer),
  UpdateView: FnMut(view::update::T),
{
  let mut terrain = client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  let r =
    terrain.load_chunk(
      &client.id_allocator,
      &mut *rng,
      update_view,
      chunk_position,
      lod,
    );
  match r {
    Ok(()) => {},
    Err(voxels) => {
      update_server(
        protocol::ClientToServer::RequestVoxels {
          request_time_ns : time::precise_time_ns(),
          client_id       : client.id,
          voxels          : voxels,
        }
      );
      *client.pending_terrain_requests.lock().unwrap() += 1;
    },
  }
}

#[inline(never)]
fn process_voxel_updates<UpdateView>(
  client      : &client::T,
  update_view : &mut UpdateView,
) where
  UpdateView: FnMut(view::update::T),
{
  let terrain = &mut *client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  terrain.tick(
    &client.id_allocator,
    rng,
    update_view,
    &*client.player_position.lock().unwrap(),
  );
}

#[inline(never)]
fn process_server_updates<RecvServer, UpdateView, UpdateAudio, UpdateServer, EnqueueTerrainUpdate>(
  client: &client::T,
  recv_server: &mut RecvServer,
  update_view: &mut UpdateView,
  update_audio: &mut UpdateAudio,
  update_server: &mut UpdateServer,
  enqueue_terrain_update: &mut EnqueueTerrainUpdate,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView           : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainUpdate : FnMut(terrain::Load),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  while let Some(up) = recv_server() {
    apply_server_update(
      client,
      update_view,
      update_audio,
      update_server,
      enqueue_terrain_update,
      up,
    );

    if i > 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}
<commit_msg>Rename<commit_after>use std::sync::Mutex;
use stopwatch;
use time;

use common::protocol;
use common::surroundings_loader;
use common::surroundings_loader::LoadType;

use audio_thread;
use chunk;
use client;
use lod;
use server_update::apply_server_update;
use terrain;
use view;

const MAX_OUTSTANDING_TERRAIN_REQUESTS: u32 = 1;

pub fn update_thread<RecvServer, UpdateView0, UpdateView1, UpdateAudio, UpdateServer, EnqueueTerrainLoad>(
  quit                   : &Mutex<bool>,
  client                 : &client::T,
  recv_server            : &mut RecvServer,
  update_view0           : &mut UpdateView0,
  update_view1           : &mut UpdateView1,
  update_audio           : &mut UpdateAudio,
  update_server          : &mut UpdateServer,
  enqueue_terrain_update : &mut EnqueueTerrainLoad,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView0          : FnMut(view::update::T),
  UpdateView1          : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainLoad : FnMut(terrain::Load),
{
  'update_loop: loop {
    let should_quit = *quit.lock().unwrap();
    if should_quit {
      break 'update_loop
    } else {
      stopwatch::time("update_iteration", || {
        stopwatch::time("process_server_updates", || {
          process_server_updates(client, recv_server, update_view0, update_audio, update_server, enqueue_terrain_update);
        });

        stopwatch::time("update_surroundings", || {
          update_surroundings(client, update_view1, update_server);
        });

        stopwatch::time("process_voxel_updates", || {
          process_voxel_updates(client, update_view1);
        });
      })
    }
  }
}

#[inline(never)]
fn update_surroundings<UpdateView, UpdateServer>(
  client        : &client::T,
  update_view   : &mut UpdateView,
  update_server : &mut UpdateServer,
) where
  UpdateView   : FnMut(view::update::T),
  UpdateServer : FnMut(protocol::ClientToServer),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  let load_position = {
    let load_position = *client.load_position.lock().unwrap();
    load_position.unwrap_or_else(|| *client.player_position.lock().unwrap())
  };
  let load_position = chunk::position::of_world_position(&load_position);
  let mut surroundings_loader = client.surroundings_loader.lock().unwrap();
  let mut updates = surroundings_loader.updates(load_position.as_pnt()) ;
  loop {
    if *client.pending_terrain_requests.lock().unwrap() >= MAX_OUTSTANDING_TERRAIN_REQUESTS {
      trace!("update loop breaking");
      break;
    }

    let chunk_position;
    let load_type;
    match updates.next() {
      None => break,
      Some((b, l)) => {
        chunk_position = chunk::position::of_pnt(&b);
        load_type = l;
      },
    }

    debug!("chunk surroundings");
    let distance =
      surroundings_loader::distance_between(
        load_position.as_pnt(),
        chunk_position.as_pnt(),
      );
    match load_type {
      LoadType::Load => {
        stopwatch::time("update_thread.load_chunk", || {
          trace!("Loading distance {}", distance);
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          if load_state == Some(new_lod) {
            debug!("Not re-loading {:?} at {:?}", chunk_position, new_lod);
          } else {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          }
        })
      },
      LoadType::Downgrade => {
        stopwatch::time("update_thread.update_chunk", || {
          let new_lod = lod::of_distance(distance as u32);
          let load_state = client.terrain.lock().unwrap().load_state(&chunk_position);
          let is_downgrade = load_state.map(|lod| new_lod < lod) == Some(true);
          if is_downgrade {
            load_or_request_chunk(client, update_server, update_view, &chunk_position, new_lod);
          } else {
            trace!("Not updating {:?} at {:?}", chunk_position, new_lod);
          }
        })
      },
      LoadType::Unload => {
        stopwatch::time("update_thread.unload", || {
          client.terrain.lock().unwrap().unload(update_view, &chunk_position);
        })
      },
    };

    if i >= 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}

fn load_or_request_chunk<UpdateServer, UpdateView>(
  client         : &client::T,
  update_server  : &mut UpdateServer,
  update_view    : &mut UpdateView,
  chunk_position : &chunk::position::T,
  lod            : lod::T,
) where
  UpdateServer: FnMut(protocol::ClientToServer),
  UpdateView: FnMut(view::update::T),
{
  let mut terrain = client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  let r =
    terrain.load_chunk(
      &client.id_allocator,
      &mut *rng,
      update_view,
      chunk_position,
      lod,
    );
  match r {
    Ok(()) => {},
    Err(voxels) => {
      update_server(
        protocol::ClientToServer::RequestVoxels {
          request_time_ns : time::precise_time_ns(),
          client_id       : client.id,
          voxels          : voxels,
        }
      );
      *client.pending_terrain_requests.lock().unwrap() += 1;
    },
  }
}

#[inline(never)]
fn process_voxel_updates<UpdateView>(
  client      : &client::T,
  update_view : &mut UpdateView,
) where
  UpdateView: FnMut(view::update::T),
{
  let terrain = &mut *client.terrain.lock().unwrap();
  let rng = &mut *client.rng.lock().unwrap();
  terrain.tick(
    &client.id_allocator,
    rng,
    update_view,
    &*client.player_position.lock().unwrap(),
  );
}

#[inline(never)]
fn process_server_updates<RecvServer, UpdateView, UpdateAudio, UpdateServer, EnqueueTerrainLoad>(
  client: &client::T,
  recv_server: &mut RecvServer,
  update_view: &mut UpdateView,
  update_audio: &mut UpdateAudio,
  update_server: &mut UpdateServer,
  enqueue_terrain_update: &mut EnqueueTerrainLoad,
) where
  RecvServer           : FnMut() -> Option<protocol::ServerToClient>,
  UpdateView           : FnMut(view::update::T),
  UpdateAudio          : FnMut(audio_thread::Message),
  UpdateServer         : FnMut(protocol::ClientToServer),
  EnqueueTerrainLoad : FnMut(terrain::Load),
{
  let start = time::precise_time_ns();
  let mut i = 0;
  while let Some(up) = recv_server() {
    apply_server_update(
      client,
      update_view,
      update_audio,
      update_server,
      enqueue_terrain_update,
      up,
    );

    if i > 10 {
      i -= 10;
      if time::precise_time_ns() - start >= 1_000_000 {
        break
      }
    }
    i += 1;
  }
}
<|endoftext|>


<commit_before>use std::{
    collections::HashMap,
    fs,
    path::{Path, PathBuf},
};

use clap::{App, Arg, SubCommand};
use failure::bail;

use tools::{collect_tests, generate, install_format_hook, run, run_rustfmt, Mode, Overwrite, Result, Test, Verify};

const GRAMMAR_DIR: &str = "./crates/ra_syntax/src/grammar";
const INLINE_TESTS_DIR: &str = "./crates/ra_syntax/tests/data/parser/inline";

fn main() -> Result<()> {
    let matches = App::new("tasks")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .arg(
            Arg::with_name("verify")
                .long("--verify")
                .help("Verify that generated code is up-to-date")
                .global(true),
        )
        .subcommand(SubCommand::with_name("gen-syntax"))
        .subcommand(SubCommand::with_name("gen-tests"))
        .subcommand(SubCommand::with_name("install-code"))
        .subcommand(SubCommand::with_name("format"))
        .subcommand(SubCommand::with_name("format-hook"))
        .get_matches();
    let mode = if matches.is_present("verify") {
        Verify
    } else {
        Overwrite
    };
    match matches
        .subcommand_name()
        .expect("Subcommand must be specified")
    {
        "install-code" => install_code_extension()?,
        "gen-tests" => gen_tests(mode)?,
        "gen-syntax" => generate(Overwrite)?,
        "format" => run_rustfmt(mode)?,
        "format-hook" => install_format_hook()?,
        _ => unreachable!(),
    }
    Ok(())
}

fn gen_tests(mode: Mode) -> Result<()> {
    let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?;

    let inline_tests_dir = Path::new(INLINE_TESTS_DIR);
    if !inline_tests_dir.is_dir() {
        fs::create_dir_all(inline_tests_dir)?;
    }
    let existing = existing_tests(inline_tests_dir)?;

    for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
        panic!("Test is deleted: {}", t);
    }

    let mut new_idx = existing.len() + 2;
    for (name, test) in tests {
        let path = match existing.get(&name) {
            Some((path, _test)) => path.clone(),
            None => {
                let file_name = format!("{:04}_{}.rs", new_idx, name);
                new_idx += 1;
                inline_tests_dir.join(file_name)
            }
        };
        teraron::update(&path, &test.text, mode)?;
    }
    Ok(())
}

fn tests_from_dir(dir: &Path) -> Result<HashMap<String, Test>> {
    let mut res = HashMap::new();
    for entry in ::walkdir::WalkDir::new(dir) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue;
        }
        if entry.path().extension().unwrap_or_default() != "rs" {
            continue;
        }
        process_file(&mut res, entry.path())?;
    }
    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
    process_file(&mut res, &grammar_rs)?;
    return Ok(res);
    fn process_file(res: &mut HashMap<String, Test>, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;

        for (_, test) in collect_tests(&text) {
            if let Some(old_test) = res.insert(test.name.clone(), test) {
                bail!("Duplicate test: {}", old_test.name)
            }
        }
        Ok(())
    }
}

fn existing_tests(dir: &Path) -> Result<HashMap<String, (PathBuf, Test)>> {
    let mut res = HashMap::new();
    for file in fs::read_dir(dir)? {
        let file = file?;
        let path = file.path();
        if path.extension().unwrap_or_default() != "rs" {
            continue;
        }
        let name = {
            let file_name = path.file_name().unwrap().to_str().unwrap();
            file_name[5..file_name.len() - 3].to_string()
        };
        let text = fs::read_to_string(&path)?;
        let test = Test {
            name: name.clone(),
            text,
        };
        if let Some(old) = res.insert(name, (path, test)) {
            println!("Duplicate test: {:?}", old);
        }
    }
    Ok(res)
}

fn install_code_extension() -> Result<()> {
    run("cargo install --path crates/ra_lsp_server --force", ".")?;
    if cfg!(windows) {
        run(r"cmd.exe /c npm.cmd install", "./editors/code")?;
    } else {
        run(r"npm install", "./editors/code")?;
    }
    run(r"npm run package", "./editors/code")?;
    if cfg!(windows) {
        run(
            r"cmd.exe /c code.cmd --install-extension ./ra-lsp-0.0.1.vsix",
            "./editors/code",
        )?;
    } else {
        run(
            r"code --install-extension ./ra-lsp-0.0.1.vsix",
            "./editors/code",
        )?;
    }
    Ok(())
}
<commit_msg>fix installation on windows<commit_after>use std::{
    collections::HashMap,
    fs,
    path::{Path, PathBuf},
};

use clap::{App, Arg, SubCommand};
use failure::bail;

use tools::{collect_tests, generate, install_format_hook, run, run_rustfmt, Mode, Overwrite, Result, Test, Verify};

const GRAMMAR_DIR: &str = "./crates/ra_syntax/src/grammar";
const INLINE_TESTS_DIR: &str = "./crates/ra_syntax/tests/data/parser/inline";

fn main() -> Result<()> {
    let matches = App::new("tasks")
        .setting(clap::AppSettings::SubcommandRequiredElseHelp)
        .arg(
            Arg::with_name("verify")
                .long("--verify")
                .help("Verify that generated code is up-to-date")
                .global(true),
        )
        .subcommand(SubCommand::with_name("gen-syntax"))
        .subcommand(SubCommand::with_name("gen-tests"))
        .subcommand(SubCommand::with_name("install-code"))
        .subcommand(SubCommand::with_name("format"))
        .subcommand(SubCommand::with_name("format-hook"))
        .get_matches();
    let mode = if matches.is_present("verify") {
        Verify
    } else {
        Overwrite
    };
    match matches
        .subcommand_name()
        .expect("Subcommand must be specified")
    {
        "install-code" => install_code_extension()?,
        "gen-tests" => gen_tests(mode)?,
        "gen-syntax" => generate(Overwrite)?,
        "format" => run_rustfmt(mode)?,
        "format-hook" => install_format_hook()?,
        _ => unreachable!(),
    }
    Ok(())
}

fn gen_tests(mode: Mode) -> Result<()> {
    let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?;

    let inline_tests_dir = Path::new(INLINE_TESTS_DIR);
    if !inline_tests_dir.is_dir() {
        fs::create_dir_all(inline_tests_dir)?;
    }
    let existing = existing_tests(inline_tests_dir)?;

    for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
        panic!("Test is deleted: {}", t);
    }

    let mut new_idx = existing.len() + 2;
    for (name, test) in tests {
        let path = match existing.get(&name) {
            Some((path, _test)) => path.clone(),
            None => {
                let file_name = format!("{:04}_{}.rs", new_idx, name);
                new_idx += 1;
                inline_tests_dir.join(file_name)
            }
        };
        teraron::update(&path, &test.text, mode)?;
    }
    Ok(())
}

fn tests_from_dir(dir: &Path) -> Result<HashMap<String, Test>> {
    let mut res = HashMap::new();
    for entry in ::walkdir::WalkDir::new(dir) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue;
        }
        if entry.path().extension().unwrap_or_default() != "rs" {
            continue;
        }
        process_file(&mut res, entry.path())?;
    }
    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
    process_file(&mut res, &grammar_rs)?;
    return Ok(res);
    fn process_file(res: &mut HashMap<String, Test>, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;

        for (_, test) in collect_tests(&text) {
            if let Some(old_test) = res.insert(test.name.clone(), test) {
                bail!("Duplicate test: {}", old_test.name)
            }
        }
        Ok(())
    }
}

fn existing_tests(dir: &Path) -> Result<HashMap<String, (PathBuf, Test)>> {
    let mut res = HashMap::new();
    for file in fs::read_dir(dir)? {
        let file = file?;
        let path = file.path();
        if path.extension().unwrap_or_default() != "rs" {
            continue;
        }
        let name = {
            let file_name = path.file_name().unwrap().to_str().unwrap();
            file_name[5..file_name.len() - 3].to_string()
        };
        let text = fs::read_to_string(&path)?;
        let test = Test {
            name: name.clone(),
            text,
        };
        if let Some(old) = res.insert(name, (path, test)) {
            println!("Duplicate test: {:?}", old);
        }
    }
    Ok(res)
}

fn install_code_extension() -> Result<()> {
    run("cargo install --path crates/ra_lsp_server --force", ".")?;
    if cfg!(windows) {
        run(r"cmd.exe /c npm.cmd install", "./editors/code")?;
        run(r"cmd.exe /c npm.cmd run package", "./editors/code")?;
    } else {
        run(r"npm install", "./editors/code")?;
        run(r"npm run package", "./editors/code")?;
    }
    if cfg!(windows) {
        run(
            r"cmd.exe /c code.cmd --install-extension ./ra-lsp-0.0.1.vsix",
            "./editors/code",
        )?;
    } else {
        run(
            r"code --install-extension ./ra-lsp-0.0.1.vsix",
            "./editors/code",
        )?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use base::Event;
use cros_fuzz::fuzz_target;
use devices::virtio::{base_features, Block, Interrupt, Queue, VirtioDevice};
use tempfile;
use vm_memory::{GuestAddress, GuestMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

fuzz_target!(|bytes| {
    let size_u64 = size_of::<u64>();
    let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

    // The fuzz data is interpreted as:
    // starting index 8 bytes
    // command location 8 bytes
    // command 16 bytes
    // descriptors circular buffer 16 bytes * 3
    if bytes.len() < 4 * size_u64 {
        // Need an index to start.
        return;
    }

    let mut data_image = Cursor::new(bytes);

    let first_index = read_u64(&mut data_image);
    if first_index > MEM_SIZE / DESC_SIZE {
        return;
    }
    let first_offset = first_index * DESC_SIZE;
    if first_offset as usize + size_u64 > bytes.len() {
        return;
    }

    let command_addr = read_u64(&mut data_image);
    if command_addr > MEM_SIZE - CMD_SIZE as u64 {
        return;
    }
    if mem
        .write_all_at_addr(
            &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
            GuestAddress(command_addr as u64),
        )
        .is_err()
    {
        return;
    }

    data_image.seek(SeekFrom::Start(first_offset)).unwrap();
    let desc_table = read_u64(&mut data_image);

    if mem
        .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
        .is_err()
    {
        return;
    }

    let mut q = Queue::new(QUEUE_SIZE);
    q.ready = true;
    q.size = QUEUE_SIZE / 2;
    q.max_size = QUEUE_SIZE;

    let queue_evts: Vec<Event> = vec![Event::new().unwrap()];
    let queue_fd = queue_evts[0].as_raw_fd();
    let queue_evt = unsafe { Event::from_raw_fd(libc::dup(queue_fd)) };

    let features = base_features(false);

    let disk_file = tempfile::tempfile().unwrap();
    let mut block = Block::new(features, Box::new(disk_file), false, true, 512, None).unwrap();

    block.activate(
        mem,
        Interrupt::new(
            Arc::new(AtomicUsize::new(0)),
            Event::new().unwrap(),
            Event::new().unwrap(),
            None,   // msix_config
            0xFFFF, // VIRTIO_MSI_NO_VECTOR
        ),
        vec![q],
        queue_evts,
    );

    queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}
<commit_msg>fuzz: block_fuzzer: remove use of AsRawFd<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use base::Event;
use cros_fuzz::fuzz_target;
use devices::virtio::{base_features, Block, Interrupt, Queue, VirtioDevice};
use tempfile;
use vm_memory::{GuestAddress, GuestMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

fuzz_target!(|bytes| {
    let size_u64 = size_of::<u64>();
    let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

    // The fuzz data is interpreted as:
    // starting index 8 bytes
    // command location 8 bytes
    // command 16 bytes
    // descriptors circular buffer 16 bytes * 3
    if bytes.len() < 4 * size_u64 {
        // Need an index to start.
        return;
    }

    let mut data_image = Cursor::new(bytes);

    let first_index = read_u64(&mut data_image);
    if first_index > MEM_SIZE / DESC_SIZE {
        return;
    }
    let first_offset = first_index * DESC_SIZE;
    if first_offset as usize + size_u64 > bytes.len() {
        return;
    }

    let command_addr = read_u64(&mut data_image);
    if command_addr > MEM_SIZE - CMD_SIZE as u64 {
        return;
    }
    if mem
        .write_all_at_addr(
            &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
            GuestAddress(command_addr as u64),
        )
        .is_err()
    {
        return;
    }

    data_image.seek(SeekFrom::Start(first_offset)).unwrap();
    let desc_table = read_u64(&mut data_image);

    if mem
        .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
        .is_err()
    {
        return;
    }

    let mut q = Queue::new(QUEUE_SIZE);
    q.ready = true;
    q.size = QUEUE_SIZE / 2;
    q.max_size = QUEUE_SIZE;

    let queue_evts: Vec<Event> = vec![Event::new().unwrap()];
    let queue_evt = queue_evts[0].try_clone().unwrap();

    let features = base_features(false);

    let disk_file = tempfile::tempfile().unwrap();
    let mut block = Block::new(features, Box::new(disk_file), false, true, 512, None).unwrap();

    block.activate(
        mem,
        Interrupt::new(
            Arc::new(AtomicUsize::new(0)),
            Event::new().unwrap(),
            Event::new().unwrap(),
            None,   // msix_config
            0xFFFF, // VIRTIO_MSI_NO_VECTOR
        ),
        vec![q],
        queue_evts,
    );

    queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}
<|endoftext|>


<commit_before>extern mod extra;
use std::fmt;
use extra::container::Deque;
use extra::ringbuf::RingBuf;
use std::iter::Peekable;
#[deriving(Clone, Eq, ToStr)]
pub enum TokenEnum {
	EOF,
	NAME,
	OPERATOR,
	NUMBER,
	FLOAT,
	LPARENS,
	RPARENS,
	LBRACKET,
	RBRACKET,
	LBRACE,
	RBRACE,
	INDENTSTART,
	INDENTLEVEL,
	COMMA,
	EQUALSSIGN,
	SEMICOLON,
	MODULE,
	CLASS,
	INSTANCE,
	WHERE,
	LET,
	IN,
	CASE,
	OF,
	ARROW,
	TYPEDECL,
	DATA
}

#[deriving(Clone, Eq, ToStr)]
pub struct Location {
    column : int,
    row : int,
    absolute : int
}

impl fmt::Default for Location {
    fn fmt(loc: &Location, f: &mut fmt::Formatter) {
        write!(f.buf, "{}:{}", loc.row, loc.column)
    }
}

#[deriving(Clone)]
pub struct Token {
    token : TokenEnum,
    value : ~str,
    location : Location
}
impl Token {
    fn eof() -> Token {
        Token { token : EOF, value : ~"", location : Location { column : -1, row : -1, absolute : -1} }
    }

    fn new(token : TokenEnum, value : ~str, loc : Location) -> Token {
        Token { token : token, value : value, location : loc }
    }
    fn new_(token : TokenEnum, value : ~str) -> Token {
        Token::new(token, value, Location { column : -1, row : -1, absolute : -1 })
    }
}

impl Eq for Token {
    fn eq(&self, rhs : &Token) -> bool {
        self.token == rhs.token && self.value == rhs.value
    }
}

fn name_or_keyword(tok : &str) -> TokenEnum {
    match tok {
        "module" => MODULE,
        "class" => CLASS,
        "instance" => INSTANCE,
        "where" => WHERE,
        "let" => LET,
        "in" => IN,
        "case" => CASE,
        "of" => OF,
        "->" => ARROW,
        "data" => DATA,
        _ => NAME
    }
}

fn is_operator(first_char : char) -> bool {
    match first_char {
        '+' => true,
        '-' => true,
        '*' => true,
        '/' => true,
        '.' => true,
        '$' => true,
        ':' => true,
        '=' => true,
        '<' => true,
        '>' => true,
        '|' => true,
        '&' => true,
        _ => false
    }
}

pub struct Lexer<Stream> {
    priv input : Peekable<char, Stream>,
    priv location : Location,
    priv previousLocation : Location,
    priv unprocessedTokens : ~[Token],
    priv tokens : extra::ringbuf::RingBuf<Token>,
    priv indentLevels : ~[int],
    priv offset : uint
}

impl <Stream : Iterator<char>> Lexer<Stream> {

    pub fn new(input : Stream) -> Lexer<Stream> {
        let start = Location { column : 0, row : 0, absolute : 0};
        Lexer {
            input : input.peekable(),
            location : start,
            previousLocation : start,
            unprocessedTokens : ~[],
            tokens : extra::ringbuf::RingBuf::with_capacity(20),
            indentLevels : ~[],
            offset : 0}
    }
    pub fn module_next<'a>(&'a mut self) -> &'a Token {
        let mut newline = false;
        let n = self.next_indent_token(&mut newline);
        self.unprocessedTokens.push(n);
        let newTok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;
        let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;

        if newTok != LBRACE && newTok != MODULE {
            self.unprocessedTokens.push(Token::new(INDENTSTART, ~"{n}", loc));
        }
        if newline {
            self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", loc));
        }

        self.layout_independent_token(|_| false);
        self.current()
    }

    pub fn next_<'a>(&'a mut self) -> &'a Token {
        self.next(|_| false)
    }
    pub fn next<'a>(&'a mut self, parseError : |&Token| -> bool) -> &'a Token {
        if self.offset > 0 {
            self.offset -= 1;
            match self.tokens.iter().idx(self.tokens.len() - 1 - self.offset) {
                Some(token) => token,
                None => fail!("Impossible empty tokens stream")
            }
        }
        else if self.unprocessedTokens.len() > 0 {
            self.layout_independent_token(parseError);
            self.tokens.back().unwrap()
        }
        else {
            self.new_token(parseError)
        }
    }

    pub fn current<'a>(&'a self) -> &'a Token {
        match self.tokens.iter().idx(self.tokens.len() - 1 - self.offset) {
            Some(token) => token,
            None => fail!("Attempted to access Lexer::current() on when tokens is empty")
        }
    }

    pub fn backtrack(&mut self) {
        self.offset += 1;
    }

    pub fn valid(&self) -> bool {
        self.offset > 0 || match self.tokens.back() { None => true, Some(x) => x.token != EOF }
    }

    fn peek(&mut self) -> Option<char> {
        match self.input.peek() {
            Some(ch) => Some(*ch),
            None => None
        }
    }

    fn read_char(&mut self) -> Option<char> {
        match self.input.next() {
            Some(c) => {
                let previous_was_newline = self.previousLocation.row != self.location.row;
                self.previousLocation = self.location;
                self.location.absolute += 1;
                self.location.column += 1;
                if (!previous_was_newline && (c == '\n' || c == '\r'))
                {
                    self.location.column = 0;
                    self.location.row += 1;
                }
                Some(c)
            }
            None => None
        }
    }

    fn scan_digits(&mut self) -> ~str {
        let mut result = ~"";
        loop {
            match self.peek() {
                Some(x) => {
                    if !x.is_digit() {
                        return result;
                    }
                    self.read_char();
                    result.push_char(x)
                }
                None => break
            }
        }
        result
    }

    fn scan_number(&mut self, c : char, location : Location) -> Token {
        let mut number = c.to_str() + self.scan_digits();
        let mut token = NUMBER;
        match self.peek() {
            Some('.') => {
                token = FLOAT;
                number.push_char('.');
                number = number.append(self.scan_digits());
            }
            _ => ()
        }
        Token { token : token, value : number, location : location }
    }

    fn new_token<'a>(&'a mut self, parseError : |&Token| -> bool) -> &'a Token {
        let mut newline = false;
        let n = self.next_indent_token(&mut newline);
        self.unprocessedTokens.push(n);
        let newTok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;

        if newTok != LBRACE {
            match self.tokens.back() {
                Some(tok) => {
                    if tok.token == LET || tok.token == WHERE || tok.token == OF {
                        let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
                        let indentstart = Token { token : INDENTSTART, value : ~"{n}", location : loc };
                        self.unprocessedTokens.push(indentstart);
                    }
                }
                None => ()
            }
        }
        if newline {
            let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
            self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", loc));
        }
        self.layout_independent_token(parseError);
        self.tokens.back().unwrap()
    }

    fn layout_independent_token(&mut self, parseError : |&Token| -> bool) {
        if self.unprocessedTokens.len() > 0 {
            let tok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].clone();//TODO dont use clone
            match tok.token {
                INDENTLEVEL => {
                    if (self.indentLevels.len() > 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        //m == n
                        if (m == tok.location.column) {
                            debug!("Indents are same, inserted semicolon");
                            self.tokens.push_back(Token::new(SEMICOLON, ~";", tok.location));
                            self.unprocessedTokens.pop();
                            return;
                        }
                        else if (tok.location.column < m)// n < m
                        {
                            //TODO
                            debug!("n < m, insert \\}");
                            self.indentLevels.pop();
                            self.tokens.push_back(Token::new(RBRACE, ~"}", tok.location));
                            return;
                        }
                    }
                    self.unprocessedTokens.pop();
                    if (self.unprocessedTokens.len() == 0) {
                        self.new_token(parseError);
                        return;
                    }
                    else {
                        return self.layout_independent_token(parseError);
                    }
                }
                INDENTSTART => {
                    //{n} token
                    let n = tok.location.column;
                    if (self.indentLevels.len() != 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        if (n > m) {
                            debug!("n > m + INDENTSTART, insert \\{");
                            self.unprocessedTokens.pop();
                            self.tokens.push_back(Token::new(LBRACE, ~"{", tok.location));
                            self.indentLevels.push(n);
                            return;
                        }
                    }
                    if (n > 0)
                    {
                        self.tokens.push_back(Token::new(LBRACE, ~"{", tok.location));
                        self.unprocessedTokens.pop();
                        self.indentLevels.push(n);
                        return;
                    }
                    self.tokens.push_back(Token::new(LBRACE, ~"{", tok.location));
                    self.tokens.push_back(Token::new(RBRACE, ~"}", tok.location));
                    self.unprocessedTokens.pop();
                    self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", tok.location));
                    self.offset += 1;
                    return;
                }
                RBRACE => {
                    if (self.indentLevels.len() > 0 && self.indentLevels[self.indentLevels.len() - 1] == 0) {
                        self.tokens.push_back(self.unprocessedTokens.pop());
                        self.indentLevels.pop();
                        return;
                    }
                    else {
                        return;//parse-error
                    }
                }
                LBRACE => {
                    self.tokens.push_back(self.unprocessedTokens.pop());
                    self.indentLevels.push(0);
                    return;
                }

                _ => ()
            }
            if (self.indentLevels.len() != 0) {
                //L (t:ts) (m:ms) 	= 	} : (L (t:ts) ms) 	if m /= 0 and parse-error(t)
                let m = self.indentLevels[self.indentLevels.len() - 1];
                if (m != 0 && parseError(&tok))
                {
                    debug!("ParseError on token {:?}, inserting \\}", tok.token);
                    self.tokens.push_back(Token::new(RBRACE, ~"}", tok.location));
                    self.indentLevels.pop();
                    return;
                }
            }
            self.tokens.push_back(self.unprocessedTokens.pop());
            return;
        }
        else {
            if (self.indentLevels.len() == 0)//End of stream
            {
                return;
            }
            else if (self.indentLevels[self.indentLevels.len() - 1] != 0)//Keep pusing righ brackets
            {
                self.indentLevels.pop();
                self.tokens.push_back(Token::new(RBRACE, ~"}", self.location));
                return;
            }
        }
    }

    fn next_indent_token(&mut self, newline : &mut bool) -> Token {
        let mut c = ' ';
        //Skip all whitespace before the token
        while c.is_whitespace() {
            match self.read_char() {
                Some(x) => {
                    c = x;
                    if (self.location.column == 0)//newline detected
                    {
                        *newline = true;
                    }
                }
                None => { return Token::eof() }
            }
        }
        let startLocation = self.location;

        //Decide how to tokenize depending on what the first char is
        //ie if its an operator then more operators will follow
        if (is_operator(c))
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !is_operator(ch) {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => { break; }
                }
            }
            let tok = match result {
                ~"="  => EQUALSSIGN,
                ~"->" => ARROW,
                ~"::" => TYPEDECL,
                _    => OPERATOR
            };
            return Token { token : tok, value : result, location : startLocation };
        }
        else if (c.is_digit())
        {
            return self.scan_number(c, startLocation);
        }
        else if (c.is_alphabetic() || c == '_')
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !ch.is_alphanumeric() && ch != '_' {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => break
                }
            }
            return Token {
                token : name_or_keyword(result),
                location : startLocation,
                value : result};
        }
        let tok = match c {
            ';' => SEMICOLON,
            '(' => LPARENS,
            ')' => RPARENS,
            '[' => LBRACKET,
            ']' => RBRACKET,
            '{' => LBRACE,
            '}' => RBRACE,
            ',' => COMMA,
            _   => EOF
        };
        Token { token : tok, location : startLocation, value : c.to_str() }
    }
}

#[test]
fn simple() {
    let mut lexer = Lexer::new("test 2 + 3".chars());

    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
#[test]
fn let_bind() {
    let mut lexer = Lexer::new(
r"let
    test = 2 + 3
in test".chars());

    assert_eq!(*lexer.next_(), Token::new_(LET, ~"let"));
    assert_eq!(*lexer.next_(), Token::new_(LBRACE, ~"{"));
    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(EQUALSSIGN, ~"="));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
<commit_msg>Fix to the detection of \n\r lineendings<commit_after>extern mod extra;
use std::fmt;
use extra::container::Deque;
use extra::ringbuf::RingBuf;
use std::iter::Peekable;
#[deriving(Clone, Eq, ToStr)]
pub enum TokenEnum {
	EOF,
	NAME,
	OPERATOR,
	NUMBER,
	FLOAT,
	LPARENS,
	RPARENS,
	LBRACKET,
	RBRACKET,
	LBRACE,
	RBRACE,
	INDENTSTART,
	INDENTLEVEL,
	COMMA,
	EQUALSSIGN,
	SEMICOLON,
	MODULE,
	CLASS,
	INSTANCE,
	WHERE,
	LET,
	IN,
	CASE,
	OF,
	ARROW,
	TYPEDECL,
	DATA
}

#[deriving(Clone, Eq, ToStr)]
pub struct Location {
    column : int,
    row : int,
    absolute : int
}

impl fmt::Default for Location {
    fn fmt(loc: &Location, f: &mut fmt::Formatter) {
        write!(f.buf, "{}:{}", loc.row, loc.column)
    }
}

#[deriving(Clone)]
pub struct Token {
    token : TokenEnum,
    value : ~str,
    location : Location
}
impl Token {
    fn eof() -> Token {
        Token { token : EOF, value : ~"", location : Location { column : -1, row : -1, absolute : -1} }
    }

    fn new(token : TokenEnum, value : ~str, loc : Location) -> Token {
        Token { token : token, value : value, location : loc }
    }
    fn new_(token : TokenEnum, value : ~str) -> Token {
        Token::new(token, value, Location { column : -1, row : -1, absolute : -1 })
    }
}

impl Eq for Token {
    fn eq(&self, rhs : &Token) -> bool {
        self.token == rhs.token && self.value == rhs.value
    }
}

fn name_or_keyword(tok : &str) -> TokenEnum {
    match tok {
        "module" => MODULE,
        "class" => CLASS,
        "instance" => INSTANCE,
        "where" => WHERE,
        "let" => LET,
        "in" => IN,
        "case" => CASE,
        "of" => OF,
        "->" => ARROW,
        "data" => DATA,
        _ => NAME
    }
}

fn is_operator(first_char : char) -> bool {
    match first_char {
        '+' => true,
        '-' => true,
        '*' => true,
        '/' => true,
        '.' => true,
        '$' => true,
        ':' => true,
        '=' => true,
        '<' => true,
        '>' => true,
        '|' => true,
        '&' => true,
        _ => false
    }
}

pub struct Lexer<Stream> {
    priv input : Peekable<char, Stream>,
    priv location : Location,
    priv previousLocation : Location,
    priv unprocessedTokens : ~[Token],
    priv tokens : extra::ringbuf::RingBuf<Token>,
    priv indentLevels : ~[int],
    priv offset : uint
}

impl <Stream : Iterator<char>> Lexer<Stream> {

    pub fn new(input : Stream) -> Lexer<Stream> {
        let start = Location { column : 0, row : 0, absolute : 0};
        Lexer {
            input : input.peekable(),
            location : start,
            previousLocation : start,
            unprocessedTokens : ~[],
            tokens : extra::ringbuf::RingBuf::with_capacity(20),
            indentLevels : ~[],
            offset : 0}
    }
    pub fn module_next<'a>(&'a mut self) -> &'a Token {
        let mut newline = false;
        let n = self.next_indent_token(&mut newline);
        self.unprocessedTokens.push(n);
        let newTok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;
        let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;

        if newTok != LBRACE && newTok != MODULE {
            self.unprocessedTokens.push(Token::new(INDENTSTART, ~"{n}", loc));
        }
        if newline {
            self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", loc));
        }

        self.layout_independent_token(|_| false);
        self.current()
    }

    pub fn next_<'a>(&'a mut self) -> &'a Token {
        self.next(|_| false)
    }
    pub fn next<'a>(&'a mut self, parseError : |&Token| -> bool) -> &'a Token {
        if self.offset > 0 {
            self.offset -= 1;
            match self.tokens.iter().idx(self.tokens.len() - 1 - self.offset) {
                Some(token) => token,
                None => fail!("Impossible empty tokens stream")
            }
        }
        else if self.unprocessedTokens.len() > 0 {
            self.layout_independent_token(parseError);
            self.tokens.back().unwrap()
        }
        else {
            self.new_token(parseError)
        }
    }

    pub fn current<'a>(&'a self) -> &'a Token {
        match self.tokens.iter().idx(self.tokens.len() - 1 - self.offset) {
            Some(token) => token,
            None => fail!("Attempted to access Lexer::current() on when tokens is empty")
        }
    }

    pub fn backtrack(&mut self) {
        self.offset += 1;
    }

    pub fn valid(&self) -> bool {
        self.offset > 0 || match self.tokens.back() { None => true, Some(x) => x.token != EOF }
    }

    fn peek(&mut self) -> Option<char> {
        match self.input.peek() {
            Some(ch) => Some(*ch),
            None => None
        }
    }

    fn read_char(&mut self) -> Option<char> {
        match self.input.next() {
            Some(c) => {
                self.previousLocation = self.location;
                self.location.absolute += 1;
                self.location.column += 1;
                if (c == '\n' || c == '\r')
                {
                    self.location.column = 0;
                    self.location.row += 1;
                    //If this is a \n\r line ending skip the next char without increasing the location
                    let x = '\r';
                    if c == '\n' && self.input.peek() == Some(&x) {
                        self.input.next();
                    }
                }
                Some(c)
            }
            None => None
        }
    }

    fn scan_digits(&mut self) -> ~str {
        let mut result = ~"";
        loop {
            match self.peek() {
                Some(x) => {
                    if !x.is_digit() {
                        return result;
                    }
                    self.read_char();
                    result.push_char(x)
                }
                None => break
            }
        }
        result
    }

    fn scan_number(&mut self, c : char, location : Location) -> Token {
        let mut number = c.to_str() + self.scan_digits();
        let mut token = NUMBER;
        match self.peek() {
            Some('.') => {
                token = FLOAT;
                number.push_char('.');
                number = number.append(self.scan_digits());
            }
            _ => ()
        }
        Token { token : token, value : number, location : location }
    }

    fn new_token<'a>(&'a mut self, parseError : |&Token| -> bool) -> &'a Token {
        let mut newline = false;
        let n = self.next_indent_token(&mut newline);
        self.unprocessedTokens.push(n);
        let newTok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;

        if newTok != LBRACE {
            match self.tokens.back() {
                Some(tok) => {
                    if tok.token == LET || tok.token == WHERE || tok.token == OF {
                        let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
                        let indentstart = Token { token : INDENTSTART, value : ~"{n}", location : loc };
                        self.unprocessedTokens.push(indentstart);
                    }
                }
                None => ()
            }
        }
        if newline {
            let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
            self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", loc));
        }
        self.layout_independent_token(parseError);
        self.tokens.back().unwrap()
    }

    fn layout_independent_token(&mut self, parseError : |&Token| -> bool) {
        if self.unprocessedTokens.len() > 0 {
            let tok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].clone();//TODO dont use clone
            match tok.token {
                INDENTLEVEL => {
                    if (self.indentLevels.len() > 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        //m == n
                        if (m == tok.location.column) {
                            debug!("Indents are same, inserted semicolon");
                            self.tokens.push_back(Token::new(SEMICOLON, ~";", tok.location));
                            self.unprocessedTokens.pop();
                            return;
                        }
                        else if (tok.location.column < m)// n < m
                        {
                            //TODO
                            debug!("n < m, insert \\}");
                            self.indentLevels.pop();
                            self.tokens.push_back(Token::new(RBRACE, ~"}", tok.location));
                            return;
                        }
                    }
                    self.unprocessedTokens.pop();
                    if (self.unprocessedTokens.len() == 0) {
                        self.new_token(parseError);
                        return;
                    }
                    else {
                        return self.layout_independent_token(parseError);
                    }
                }
                INDENTSTART => {
                    //{n} token
                    let n = tok.location.column;
                    if (self.indentLevels.len() != 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        if (n > m) {
                            debug!("n > m + INDENTSTART, insert \\{");
                            self.unprocessedTokens.pop();
                            self.tokens.push_back(Token::new(LBRACE, ~"{", tok.location));
                            self.indentLevels.push(n);
                            return;
                        }
                    }
                    if (n > 0)
                    {
                        self.tokens.push_back(Token::new(LBRACE, ~"{", tok.location));
                        self.unprocessedTokens.pop();
                        self.indentLevels.push(n);
                        return;
                    }
                    self.tokens.push_back(Token::new(LBRACE, ~"{", tok.location));
                    self.tokens.push_back(Token::new(RBRACE, ~"}", tok.location));
                    self.unprocessedTokens.pop();
                    self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", tok.location));
                    self.offset += 1;
                    return;
                }
                RBRACE => {
                    if (self.indentLevels.len() > 0 && self.indentLevels[self.indentLevels.len() - 1] == 0) {
                        self.tokens.push_back(self.unprocessedTokens.pop());
                        self.indentLevels.pop();
                        return;
                    }
                    else {
                        return;//parse-error
                    }
                }
                LBRACE => {
                    self.tokens.push_back(self.unprocessedTokens.pop());
                    self.indentLevels.push(0);
                    return;
                }

                _ => ()
            }
            if (self.indentLevels.len() != 0) {
                //L (t:ts) (m:ms) 	= 	} : (L (t:ts) ms) 	if m /= 0 and parse-error(t)
                let m = self.indentLevels[self.indentLevels.len() - 1];
                if (m != 0 && parseError(&tok))
                {
                    debug!("ParseError on token {:?}, inserting \\}", tok.token);
                    self.tokens.push_back(Token::new(RBRACE, ~"}", tok.location));
                    self.indentLevels.pop();
                    return;
                }
            }
            self.tokens.push_back(self.unprocessedTokens.pop());
            return;
        }
        else {
            if (self.indentLevels.len() == 0)//End of stream
            {
                return;
            }
            else if (self.indentLevels[self.indentLevels.len() - 1] != 0)//Keep pusing righ brackets
            {
                self.indentLevels.pop();
                self.tokens.push_back(Token::new(RBRACE, ~"}", self.location));
                return;
            }
        }
    }

    fn next_indent_token(&mut self, newline : &mut bool) -> Token {
        let mut c = ' ';
        //Skip all whitespace before the token
        while c.is_whitespace() {
            match self.read_char() {
                Some(x) => {
                    c = x;
                    if (self.location.column == 0)//newline detected
                    {
                        *newline = true;
                    }
                }
                None => { return Token::eof() }
            }
        }
        let startLocation = self.location;

        //Decide how to tokenize depending on what the first char is
        //ie if its an operator then more operators will follow
        if (is_operator(c))
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !is_operator(ch) {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => { break; }
                }
            }
            let tok = match result {
                ~"="  => EQUALSSIGN,
                ~"->" => ARROW,
                ~"::" => TYPEDECL,
                _    => OPERATOR
            };
            return Token { token : tok, value : result, location : startLocation };
        }
        else if (c.is_digit())
        {
            return self.scan_number(c, startLocation);
        }
        else if (c.is_alphabetic() || c == '_')
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !ch.is_alphanumeric() && ch != '_' {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => break
                }
            }
            return Token {
                token : name_or_keyword(result),
                location : startLocation,
                value : result};
        }
        let tok = match c {
            ';' => SEMICOLON,
            '(' => LPARENS,
            ')' => RPARENS,
            '[' => LBRACKET,
            ']' => RBRACKET,
            '{' => LBRACE,
            '}' => RBRACE,
            ',' => COMMA,
            _   => EOF
        };
        Token { token : tok, location : startLocation, value : c.to_str() }
    }
}

#[test]
fn simple() {
    let mut lexer = Lexer::new("test 2 + 3".chars());

    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
#[test]
fn let_bind() {
    let mut lexer = Lexer::new(
r"let
    test = 2 + 3
in test".chars());

    assert_eq!(*lexer.next_(), Token::new_(LET, ~"let"));
    assert_eq!(*lexer.next_(), Token::new_(LBRACE, ~"{"));
    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(EQUALSSIGN, ~"="));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
<|endoftext|>