<commit_before>use crate::config::{Config, RunType};
use items::process_items;
use lazy_static::lazy_static;
use log::trace;
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use regex::Regex;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use syn::*;
use walkdir::{DirEntry, WalkDir};

mod attributes;
mod expressions;
mod items;
mod macros;
mod statements;
#[cfg(test)]
mod tests;

pub(crate) mod prelude {
    pub(crate) use super::*;
    pub(crate) use attributes::*;
    pub(crate) use expressions::*;
    pub(crate) use macros::*;
    pub(crate) use statements::*;
}

/// Enumeration representing which lines to ignore
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Lines {
    /// Ignore all lines in the file
    All,
    /// A single line to ignore in the file
    Line(usize),
}

/// Represents the results of analysis of a single file. Does not store the file
/// in question as this is expected to be maintained by the user.
#[derive(Clone, Debug, Default)]
pub struct LineAnalysis {
    /// This represents lines that should be ignored in coverage
    /// but may be identifed as coverable in the DWARF tables
    pub ignore: HashSet<Lines>,
    /// This represents lines that should be included in coverage
    /// But may be ignored. Doesn't make sense to cover ALL the lines so this
    /// is just an index.
    pub cover: HashSet<usize>,
    /// Some logical lines may be split between physical lines this shows the
    /// mapping from physical line to logical line to prevent false positives
    /// from expressions split across physical lines
    pub logical_lines: HashMap<usize, usize>,
}

/// When the `LineAnalysis` results are mapped to their files there needs to be
/// an easy way to get the information back. For the container used implement
/// this trait
pub trait SourceAnalysisQuery {
    /// Returns true if the line in the given file should be ignored
    fn should_ignore(&self, path: &Path, l: &usize) -> bool;
    /// Takes a path and line number and normalises it to the logical line
    /// that should be represented in the statistics
    fn normalise(&self, path: &Path, l: usize) -> (PathBuf, usize);
}

#[derive(Copy, Clone, Debug)]
pub(crate) enum SubResult {
    Ok,
    Unreachable,
}

impl SourceAnalysisQuery for HashMap<PathBuf, LineAnalysis> {
    fn should_ignore(&self, path: &Path, l: &usize) -> bool {
        if self.contains_key(path) {
            self.get(path).unwrap().should_ignore(*l)
        } else {
            false
        }
    }

    fn normalise(&self, path: &Path, l: usize) -> (PathBuf, usize) {
        let pb = path.to_path_buf();
        match self.get(path) {
            Some(s) => match s.logical_lines.get(&l) {
                Some(o) => (pb, *o),
                _ => (pb, l),
            },
            _ => (pb, l),
        }
    }
}

impl LineAnalysis {
    /// Creates a new LineAnalysis object
    fn new() -> LineAnalysis {
        Default::default()
    }

    /// Ignore all lines in the file
    pub fn ignore_all(&mut self) {
        self.ignore.clear();
        self.cover.clear();
        self.ignore.insert(Lines::All);
    }

    /// Ignore all tokens in the given token stream
    pub fn ignore_tokens<T>(&mut self, tokens: T)
    where
        T: ToTokens,
    {
        for token in tokens.into_token_stream() {
            self.ignore_span(token.span());
        }
    }

    /// Adds the lines of the provided span to the ignore set
    pub fn ignore_span(&mut self, span: Span) {
        // If we're already ignoring everything no need to ignore this span
        if !self.ignore.contains(&Lines::All) {
            for i in span.start().line..(span.end().line + 1) {
                self.ignore.insert(Lines::Line(i));
                if self.cover.contains(&i) {
                    self.cover.remove(&i);
                }
            }
        }
    }

    /// Cover all tokens in the given tokenstream
    pub fn cover_token_stream(&mut self, tokens: TokenStream, contents: Option<&str>) {
        for token in tokens {
            self.cover_span(token.span(), contents);
        }
    }

    /// Adds the lines of the provided span to the cover set
    pub fn cover_span(&mut self, span: Span, contents: Option<&str>) {
        // Not checking for Lines::All because I trust we've called cover_span
        // for a reason.
        let mut useful_lines: HashSet<usize> = HashSet::new();
        if let Some(ref c) = contents {
            lazy_static! {
                static ref SINGLE_LINE: Regex = Regex::new(r"\s*//").unwrap();
                static ref MULTI_START: Regex = Regex::new(r"/\*").unwrap();
                static ref MULTI_END: Regex = Regex::new(r"\*/").unwrap();
            }
            let len = span.end().line - span.start().line;
            let mut is_comment = false;
            for (i, line) in c.lines().enumerate().skip(span.start().line - 1).take(len) {
                let is_code = if MULTI_START.is_match(line) {
                    if !MULTI_END.is_match(line) {
                        is_comment = true;
                    }
                    false
                } else if is_comment {
                    if MULTI_END.is_match(line) {
                        is_comment = false;
                    }
                    false
                } else {
                    true
                };
                if is_code && !SINGLE_LINE.is_match(line) {
                    useful_lines.insert(i + 1);
                }
            }
        }
        for i in span.start().line..(span.end().line + 1) {
            if !self.ignore.contains(&Lines::Line(i)) && useful_lines.contains(&i) {
                self.cover.insert(i);
            }
        }
    }

    /// Shows whether the line should be ignored by tarpaulin
    pub fn should_ignore(&self, line: usize) -> bool {
        self.ignore.contains(&Lines::Line(line)) || self.ignore.contains(&Lines::All)
    }

    /// Adds a line to the list of lines to ignore
    fn add_to_ignore(&mut self, lines: &[usize]) {
        if !self.ignore.contains(&Lines::All) {
            for l in lines {
                self.ignore.insert(Lines::Line(*l));
                if self.cover.contains(l) {
                    self.cover.remove(l);
                }
            }
        }
    }
}

/// Returns true if the file is a rust source file
fn is_source_file(entry: &DirEntry) -> bool {
    let p = entry.path();
    p.extension() == Some(OsStr::new("rs"))
}

/// Returns true if the folder is a target folder
fn is_target_folder(entry: &DirEntry, root: &Path) -> bool {
    let target = root.join("target");
    entry.path().starts_with(&target)
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry
        .path()
        .iter()
        .any(|x| x.to_string_lossy().starts_with('.'))
}

/// Returns a list of files and line numbers to ignore (not indexes!)
pub fn get_line_analysis(config: &Config) -> HashMap<PathBuf, LineAnalysis> {
    let mut result: HashMap<PathBuf, LineAnalysis> = HashMap::new();

    let mut ignored_files: HashSet<PathBuf> = HashSet::new();
    let root = config.root();

    let walker = WalkDir::new(&root).into_iter();
    for e in walker
        .filter_entry(|e| !(is_target_folder(e, &root) || is_hidden(e)))
        .filter_map(|e| e.ok())
        .filter(|e| is_source_file(e))
    {
        if !ignored_files.contains(e.path()) {
            analyse_package(e.path(), &root, &config, &mut result, &mut ignored_files);
        } else {
            let mut analysis = LineAnalysis::new();
            analysis.ignore_all();
            result.insert(e.path().to_path_buf(), analysis);
            ignored_files.remove(e.path());
        }
    }
    for e in &ignored_files {
        let mut analysis = LineAnalysis::new();
        analysis.ignore_all();
        result.insert(e.to_path_buf(), analysis);
    }

    debug_printout(&result, config);

    result
}

/// Printout a debug summary of the results of source analysis if debug logging
/// is enabled
pub fn debug_printout(result: &HashMap<PathBuf, LineAnalysis>, config: &Config) {
    if config.debug {
        for (ref path, ref analysis) in result {
            trace!(
                "Source analysis for {}",
                config.strip_base_dir(path).display()
            );
            let mut lines = Vec::new();
            for l in &analysis.ignore {
                match l {
                    Lines::All => {
                        lines.clear();
                        trace!("All lines are ignorable");
                        break;
                    }
                    Lines::Line(i) => {
                        lines.push(i);
                    }
                }
            }
            if !lines.is_empty() {
                lines.sort();
                trace!("Ignorable lines: {:?}", lines);
                lines.clear()
            }
            for c in &analysis.cover {
                lines.push(c);
            }

            if !lines.is_empty() {
                lines.sort();
                trace!("Coverable lines: {:?}", lines);
            }
        }
    }
}

/// Analyse the crates lib.rs for some common false positives
fn analyse_lib_rs(file: &Path, result: &mut HashMap<PathBuf, LineAnalysis>) {
    if let Ok(f) = File::open(file) {
        let read_file = BufReader::new(f);
        if let Some(Ok(first)) = read_file.lines().next() {
            if !(first.starts_with("pub") || first.starts_with("fn")) {
                let file = file.to_path_buf();
                if result.contains_key(&file) {
                    let l = result.get_mut(&file).unwrap();
                    l.add_to_ignore(&[1]);
                } else {
                    let mut l = LineAnalysis::new();
                    l.add_to_ignore(&[1]);
                    result.insert(file, l);
                }
            }
        }
    }
}

/// Provides context to the source analysis stage including the tarpaulin
/// config and the source code being analysed.
pub(crate) struct Context<'a> {
    /// Program config
    config: &'a Config,
    /// Contents of the source file
    file_contents: &'a str,
    /// path to the file being analysed
    file: &'a Path,
    /// Other parts of context are immutable like tarpaulin config and users
    /// source code. This is discovered during hence use of interior mutability
    ignore_mods: RefCell<HashSet<PathBuf>>,
}

/// Analyses a package of the target crate.
fn analyse_package(
    path: &Path,
    root: &Path,
    config: &Config,
    result: &mut HashMap<PathBuf, LineAnalysis>,
    filtered_files: &mut HashSet<PathBuf>,
) {
    if let Some(file) = path.to_str() {
        let skip_cause_test = config.ignore_tests && path.starts_with(root.join("tests"));
        let skip_cause_example = path.starts_with(root.join("examples"))
            && !config.run_types.contains(&RunType::Examples);
        if !(skip_cause_test || skip_cause_example) {
            let file = File::open(file);
            if let Ok(mut file) = file {
                let mut content = String::new();
                let _ = file.read_to_string(&mut content);
                let file = parse_file(&content);
                if let Ok(file) = file {
                    let mut analysis = LineAnalysis::new();
                    let ctx = Context {
                        config,
                        file_contents: &content,
                        file: path,
                        ignore_mods: RefCell::new(HashSet::new()),
                    };

                    find_ignorable_lines(&content, &mut analysis);
                    process_items(&file.items, &ctx, &mut analysis);
                    // Check there's no conflict!
                    result.insert(path.to_path_buf(), analysis);

                    let mut ignored_files = ctx.ignore_mods.into_inner();
                    for f in ignored_files.drain() {
                        if f.is_file() {
                            filtered_files.insert(f);
                        } else {
                            let walker = WalkDir::new(f).into_iter();
                            for e in walker.filter_map(|e| e.ok()).filter(|e| is_source_file(e)) {
                                filtered_files.insert(e.path().to_path_buf());
                            }
                        }
                    }
                    // This could probably be done with the DWARF if I could find a discriminating factor
                    // to why lib.rs:1 shows up as a real line!
                    if path.ends_with("src/lib.rs") {
                        analyse_lib_rs(path, result);
                    }
                }
            }
        }
    }
}

/// Finds lines from the raw string which are ignorable.
/// These are often things like close braces, semi colons that may regiser as
/// false positives.
fn find_ignorable_lines(content: &str, analysis: &mut LineAnalysis) {
    let lines = content
        .lines()
        .enumerate()
        .filter(|&(_, x)| !x.chars().any(|x| !"(){}[]?;\t ,".contains(x)))
        .map(|(i, _)| i + 1)
        .collect::<Vec<usize>>();
    analysis.add_to_ignore(&lines);

    let lines = content
        .lines()
        .enumerate()
        .filter(|&(_, x)| {
            let mut x = x.to_string();
            x.retain(|c| !c.is_whitespace());
            x == "}else{"
        })
        .map(|(i, _)| i + 1)
        .collect::<Vec<usize>>();
    analysis.add_to_ignore(&lines);
}

pub fn visit_generics(generics: &Generics, analysis: &mut LineAnalysis) {
    if let Some(ref wh) = generics.where_clause {
        analysis.ignore_tokens(wh);
    }
}
<commit_msg>Add comments to ignorable lines<commit_after>use crate::config::{Config, RunType};
use items::process_items;
use lazy_static::lazy_static;
use log::trace;
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use regex::Regex;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use syn::*;
use walkdir::{DirEntry, WalkDir};

mod attributes;
mod expressions;
mod items;
mod macros;
mod statements;
#[cfg(test)]
mod tests;

pub(crate) mod prelude {
    pub(crate) use super::*;
    pub(crate) use attributes::*;
    pub(crate) use expressions::*;
    pub(crate) use macros::*;
    pub(crate) use statements::*;
}

/// Enumeration representing which lines to ignore
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Lines {
    /// Ignore all lines in the file
    All,
    /// A single line to ignore in the file
    Line(usize),
}

/// Represents the results of analysis of a single file. Does not store the file
/// in question as this is expected to be maintained by the user.
#[derive(Clone, Debug, Default)]
pub struct LineAnalysis {
    /// This represents lines that should be ignored in coverage
    /// but may be identifed as coverable in the DWARF tables
    pub ignore: HashSet<Lines>,
    /// This represents lines that should be included in coverage
    /// But may be ignored. Doesn't make sense to cover ALL the lines so this
    /// is just an index.
    pub cover: HashSet<usize>,
    /// Some logical lines may be split between physical lines this shows the
    /// mapping from physical line to logical line to prevent false positives
    /// from expressions split across physical lines
    pub logical_lines: HashMap<usize, usize>,
}

/// When the `LineAnalysis` results are mapped to their files there needs to be
/// an easy way to get the information back. For the container used implement
/// this trait
pub trait SourceAnalysisQuery {
    /// Returns true if the line in the given file should be ignored
    fn should_ignore(&self, path: &Path, l: &usize) -> bool;
    /// Takes a path and line number and normalises it to the logical line
    /// that should be represented in the statistics
    fn normalise(&self, path: &Path, l: usize) -> (PathBuf, usize);
}

#[derive(Copy, Clone, Debug)]
pub(crate) enum SubResult {
    Ok,
    Unreachable,
}

impl SourceAnalysisQuery for HashMap<PathBuf, LineAnalysis> {
    fn should_ignore(&self, path: &Path, l: &usize) -> bool {
        if self.contains_key(path) {
            self.get(path).unwrap().should_ignore(*l)
        } else {
            false
        }
    }

    fn normalise(&self, path: &Path, l: usize) -> (PathBuf, usize) {
        let pb = path.to_path_buf();
        match self.get(path) {
            Some(s) => match s.logical_lines.get(&l) {
                Some(o) => (pb, *o),
                _ => (pb, l),
            },
            _ => (pb, l),
        }
    }
}

impl LineAnalysis {
    /// Creates a new LineAnalysis object
    fn new() -> LineAnalysis {
        Default::default()
    }

    /// Ignore all lines in the file
    pub fn ignore_all(&mut self) {
        self.ignore.clear();
        self.cover.clear();
        self.ignore.insert(Lines::All);
    }

    /// Ignore all tokens in the given token stream
    pub fn ignore_tokens<T>(&mut self, tokens: T)
    where
        T: ToTokens,
    {
        for token in tokens.into_token_stream() {
            self.ignore_span(token.span());
        }
    }

    /// Adds the lines of the provided span to the ignore set
    pub fn ignore_span(&mut self, span: Span) {
        // If we're already ignoring everything no need to ignore this span
        if !self.ignore.contains(&Lines::All) {
            for i in span.start().line..(span.end().line + 1) {
                self.ignore.insert(Lines::Line(i));
                if self.cover.contains(&i) {
                    self.cover.remove(&i);
                }
            }
        }
    }

    /// Cover all tokens in the given tokenstream
    pub fn cover_token_stream(&mut self, tokens: TokenStream, contents: Option<&str>) {
        for token in tokens {
            self.cover_span(token.span(), contents);
        }
    }

    /// Adds the lines of the provided span to the cover set
    pub fn cover_span(&mut self, span: Span, contents: Option<&str>) {
        // Not checking for Lines::All because I trust we've called cover_span
        // for a reason.
        let mut useful_lines: HashSet<usize> = HashSet::new();
        if let Some(ref c) = contents {
            lazy_static! {
                static ref SINGLE_LINE: Regex = Regex::new(r"\s*//").unwrap();
                static ref MULTI_START: Regex = Regex::new(r"/\*").unwrap();
                static ref MULTI_END: Regex = Regex::new(r"\*/").unwrap();
            }
            let len = span.end().line - span.start().line;
            let mut is_comment = false;
            for (i, line) in c.lines().enumerate().skip(span.start().line - 1).take(len) {
                let is_code = if MULTI_START.is_match(line) {
                    if !MULTI_END.is_match(line) {
                        is_comment = true;
                    }
                    false
                } else if is_comment {
                    if MULTI_END.is_match(line) {
                        is_comment = false;
                    }
                    false
                } else {
                    true
                };
                if is_code && !SINGLE_LINE.is_match(line) {
                    useful_lines.insert(i + 1);
                }
            }
        }
        for i in span.start().line..(span.end().line + 1) {
            if !self.ignore.contains(&Lines::Line(i)) && useful_lines.contains(&i) {
                self.cover.insert(i);
            }
        }
    }

    /// Shows whether the line should be ignored by tarpaulin
    pub fn should_ignore(&self, line: usize) -> bool {
        self.ignore.contains(&Lines::Line(line)) || self.ignore.contains(&Lines::All)
    }

    /// Adds a line to the list of lines to ignore
    fn add_to_ignore(&mut self, lines: &[usize]) {
        if !self.ignore.contains(&Lines::All) {
            for l in lines {
                self.ignore.insert(Lines::Line(*l));
                if self.cover.contains(l) {
                    self.cover.remove(l);
                }
            }
        }
    }
}

/// Returns true if the file is a rust source file
fn is_source_file(entry: &DirEntry) -> bool {
    let p = entry.path();
    p.extension() == Some(OsStr::new("rs"))
}

/// Returns true if the folder is a target folder
fn is_target_folder(entry: &DirEntry, root: &Path) -> bool {
    let target = root.join("target");
    entry.path().starts_with(&target)
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry
        .path()
        .iter()
        .any(|x| x.to_string_lossy().starts_with('.'))
}

/// Returns a list of files and line numbers to ignore (not indexes!)
pub fn get_line_analysis(config: &Config) -> HashMap<PathBuf, LineAnalysis> {
    let mut result: HashMap<PathBuf, LineAnalysis> = HashMap::new();

    let mut ignored_files: HashSet<PathBuf> = HashSet::new();
    let root = config.root();

    let walker = WalkDir::new(&root).into_iter();
    for e in walker
        .filter_entry(|e| !(is_target_folder(e, &root) || is_hidden(e)))
        .filter_map(|e| e.ok())
        .filter(|e| is_source_file(e))
    {
        if !ignored_files.contains(e.path()) {
            analyse_package(e.path(), &root, &config, &mut result, &mut ignored_files);
        } else {
            let mut analysis = LineAnalysis::new();
            analysis.ignore_all();
            result.insert(e.path().to_path_buf(), analysis);
            ignored_files.remove(e.path());
        }
    }
    for e in &ignored_files {
        let mut analysis = LineAnalysis::new();
        analysis.ignore_all();
        result.insert(e.to_path_buf(), analysis);
    }

    debug_printout(&result, config);

    result
}

/// Printout a debug summary of the results of source analysis if debug logging
/// is enabled
pub fn debug_printout(result: &HashMap<PathBuf, LineAnalysis>, config: &Config) {
    if config.debug {
        for (ref path, ref analysis) in result {
            trace!(
                "Source analysis for {}",
                config.strip_base_dir(path).display()
            );
            let mut lines = Vec::new();
            for l in &analysis.ignore {
                match l {
                    Lines::All => {
                        lines.clear();
                        trace!("All lines are ignorable");
                        break;
                    }
                    Lines::Line(i) => {
                        lines.push(i);
                    }
                }
            }
            if !lines.is_empty() {
                lines.sort();
                trace!("Ignorable lines: {:?}", lines);
                lines.clear()
            }
            for c in &analysis.cover {
                lines.push(c);
            }

            if !lines.is_empty() {
                lines.sort();
                trace!("Coverable lines: {:?}", lines);
            }
        }
    }
}

/// Analyse the crates lib.rs for some common false positives
fn analyse_lib_rs(file: &Path, result: &mut HashMap<PathBuf, LineAnalysis>) {
    if let Ok(f) = File::open(file) {
        let read_file = BufReader::new(f);
        if let Some(Ok(first)) = read_file.lines().next() {
            if !(first.starts_with("pub") || first.starts_with("fn")) {
                let file = file.to_path_buf();
                if result.contains_key(&file) {
                    let l = result.get_mut(&file).unwrap();
                    l.add_to_ignore(&[1]);
                } else {
                    let mut l = LineAnalysis::new();
                    l.add_to_ignore(&[1]);
                    result.insert(file, l);
                }
            }
        }
    }
}

/// Provides context to the source analysis stage including the tarpaulin
/// config and the source code being analysed.
pub(crate) struct Context<'a> {
    /// Program config
    config: &'a Config,
    /// Contents of the source file
    file_contents: &'a str,
    /// path to the file being analysed
    file: &'a Path,
    /// Other parts of context are immutable like tarpaulin config and users
    /// source code. This is discovered during hence use of interior mutability
    ignore_mods: RefCell<HashSet<PathBuf>>,
}

/// Analyses a package of the target crate.
fn analyse_package(
    path: &Path,
    root: &Path,
    config: &Config,
    result: &mut HashMap<PathBuf, LineAnalysis>,
    filtered_files: &mut HashSet<PathBuf>,
) {
    if let Some(file) = path.to_str() {
        let skip_cause_test = config.ignore_tests && path.starts_with(root.join("tests"));
        let skip_cause_example = path.starts_with(root.join("examples"))
            && !config.run_types.contains(&RunType::Examples);
        if !(skip_cause_test || skip_cause_example) {
            let file = File::open(file);
            if let Ok(mut file) = file {
                let mut content = String::new();
                let _ = file.read_to_string(&mut content);
                let file = parse_file(&content);
                if let Ok(file) = file {
                    let mut analysis = LineAnalysis::new();
                    let ctx = Context {
                        config,
                        file_contents: &content,
                        file: path,
                        ignore_mods: RefCell::new(HashSet::new()),
                    };

                    find_ignorable_lines(&content, &mut analysis);
                    process_items(&file.items, &ctx, &mut analysis);
                    // Check there's no conflict!
                    result.insert(path.to_path_buf(), analysis);

                    let mut ignored_files = ctx.ignore_mods.into_inner();
                    for f in ignored_files.drain() {
                        if f.is_file() {
                            filtered_files.insert(f);
                        } else {
                            let walker = WalkDir::new(f).into_iter();
                            for e in walker.filter_map(|e| e.ok()).filter(|e| is_source_file(e)) {
                                filtered_files.insert(e.path().to_path_buf());
                            }
                        }
                    }
                    // This could probably be done with the DWARF if I could find a discriminating factor
                    // to why lib.rs:1 shows up as a real line!
                    if path.ends_with("src/lib.rs") {
                        analyse_lib_rs(path, result);
                    }
                }
            }
        }
    }
}

/// Finds lines from the raw string which are ignorable.
/// These are often things like close braces, semi colons that may regiser as
/// false positives.
fn find_ignorable_lines(content: &str, analysis: &mut LineAnalysis) {
    lazy_static! {
        static ref IGNORABLE: Regex = Regex::new(r"^((\s*///)|([\[\]\{\}\(\)\s;\?,/]*$))").unwrap();
    }
    let lines = content
        .lines()
        .enumerate()
        .filter(|&(_, x)| IGNORABLE.is_match(&x))
        .map(|(i, _)| i + 1)
        .collect::<Vec<usize>>();
    analysis.add_to_ignore(&lines);

    let lines = content
        .lines()
        .enumerate()
        .filter(|&(_, x)| {
            let mut x = x.to_string();
            x.retain(|c| !c.is_whitespace());
            x == "}else{"
        })
        .map(|(i, _)| i + 1)
        .collect::<Vec<usize>>();
    analysis.add_to_ignore(&lines);
}

pub fn visit_generics(generics: &Generics, analysis: &mut LineAnalysis) {
    if let Some(ref wh) = generics.where_clause {
        analysis.ignore_tokens(wh);
    }
}
<|endoftext|>


<commit_before>use std::str::CharIndices;
use std::ascii::AsciiExt;
use std::iter::Peekable;

use super::error::Error;

// use ascii end of transmission as EOF
const EOF: char = 0x4 as char;

macro_rules! my_try {
    ($e:expr) => ({
        match $e {
            Ok(x) => x,
            Err(x) => return Some(Err(x)),
        }
    });
}

#[derive(Clone, Copy, Debug)]
pub enum Token<'input> {
    OpenParen,
    ClosingParen,
    Dot,
    Quote,
    WhiteSpace,
    Char(char),
    Integer(i64),
    String(&'input str),
    Symbol(&'input str),
    Begin,
    Define,
    If,
    Let,
    Loop,
    Lambda,
    Recur,
}

// Tokenzer state
#[derive(Debug, Clone, Copy)]
enum State<'input> {
    // start
    NewToken,
    Finished(Spanned<Token<'input>>),
    // start
    EatInteger(usize),
    WhiteSpace(usize),
    Minus(usize),
    Symbol(usize),
    StringStart(usize),
    StringBackslash(usize),
    Pound(usize),
    CharBegin(usize),
    EscapedChar(usize),
    FinishedChar(usize, char),
}
use self::State::*;

#[derive(Debug)]
pub struct Tokenizer<'input> {
    text: &'input str,
    chars: Peekable<CharIndices<'input>>,
    state: State<'input>,
}

pub type Spanned<T> = (usize, T, usize);

impl<'input> Tokenizer<'input> {
    pub fn new(text: &'input str) -> Tokenizer<'input> {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
            state: NewToken,
        }
    }

    fn next_char(&mut self) -> Option<(usize, char)> {
        self.chars.next()
    }

    fn peek_next(&mut self) -> Option<&(usize, char)> {
        self.chars.peek()
    }
}

impl<'input> Iterator for Tokenizer<'input> {
    type Item = Result<Spanned<Token<'input>>, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let next;
            match self.peek_next() {
                Some(&(pos, c)) => next = (self.state, pos, c),
                // check if we still need something
                None => next = (self.state, self.text.len(), EOF),
            }
/*
                {
                    let last_token = match self.state {
                        EscapedChar(start) => Ok((start, Token::Char('\\'), self.text.len())),

                    };
                    // fuse iterator
                    self.state = NewToken;
                    return Some(last_token);
                },
            }
            */

            self.state = match next {
                // pos + 1 is safe, because all these chars have width 1 byte
                (NewToken, pos, '(') => { self.next_char(); Finished((pos, Token::OpenParen, pos+1)) },
                (NewToken, pos, ')') => { self.next_char(); Finished((pos, Token::ClosingParen, pos+1)) },
                (NewToken, pos, '\'') => { self.next_char(); Finished((pos, Token::Quote, pos+1)) },
                (NewToken, pos, '.') => { self.next_char(); Finished((pos, Token::Dot, pos+1)) },
                (NewToken, pos, '-') => Minus(pos),
                (NewToken, pos, '"') => StringStart(pos),
                (NewToken, pos, '#') => Pound(pos),
                (NewToken, _, EOF) => return None,
                (NewToken, pos, c) if whitespace(c) => WhiteSpace(pos),
                (NewToken, pos, c) if numeric(c) => EatInteger(pos),
                (NewToken, pos, _) => Symbol(pos),

                // minus special case
                (Minus(start), _, c) if numeric(c) => EatInteger(start),
                (Minus(start), end, c) if end_of_item(c) => Finished((start, Token::Symbol(&self.text[start..end]), end)),
                (Minus(start), _, _) => Symbol(start),

                // whitespace
                (WhiteSpace(pos), _, c) if whitespace(c) => WhiteSpace(pos),
                (WhiteSpace(start), end, _) => Finished((start, Token::WhiteSpace, end)),

                // chars
                (Pound(pos), _, '\\') => CharBegin(pos),
                (Pound(start), end, c) if end_of_item(c) => Finished((start, Token::Symbol(&self.text[start..end]), end)),
                (Pound(pos), _, _) => Symbol(pos),

                (CharBegin(pos), _, '\\') => EscapedChar(pos),
                (CharBegin(_), _, EOF) => return Some(Err(Error::UnexpectedEofChar(self.text.len()))),
                (CharBegin(pos), _, c) if printable_char(c) => FinishedChar(pos, c),
                (CharBegin(_), pos, _) => return Some(Err(Error::NonAsciiChar(pos))),

                (EscapedChar(pos), _, c) if unescape_char(c).is_some() => FinishedChar(pos, unescape_char(c).unwrap()),
                (EscapedChar(start), end, EOF) => Finished((start, Token::Char('\\'), end)),
                (EscapedChar(start), end, _) => return Some(Err(Error::InvalidEscape(start+2, next_char(&self.text, end)))), // +2 bc #\ is 2 bytes

                (FinishedChar(start, x), end, c) if end_of_item(c) => Finished((start, Token::Char(x), end)),
                (FinishedChar(_, _), end, _) => return Some(Err(Error::InvalidToken(end, end))),

                // integers
                (EatInteger(start), _, c) if numeric(c) => EatInteger(start),
                (EatInteger(start), pos, c) if end_of_item(c) =>
                    // safe because we checked that text[start..pos] is a valid number
                    Finished((start, Token::Integer(self.text[start..pos].parse().unwrap()), pos)),
                (EatInteger(_), pos, _) => return Some(Err(Error::InvalidToken(pos, pos))),

                // strings
                (StringStart(start), end, '"') => {
                    self.next_char(); // bump
                    let string = &self.text[start+1..end];
                    for (i, c) in string.char_indices() {
                        if !c.is_ascii() {
                            return Some(Err(Error::NonAsciiChar(i+1))) // +1 bc start+1 above
                        }
                    }
                    Finished((start, Token::String(string), end))
                },
                (StringStart(_), _, EOF) => return Some(Err(Error::UnexpectedEofString(self.text.len()))),
                (StringStart(start), _, '\\') => StringBackslash(start),
                (StringStart(start), _, _) => StringStart(start),
                (StringBackslash(start), _, '"') => StringStart(start),
                (StringBackslash(start), _, c) if unescape_char(c).is_some() => StringStart(start),
                (StringBackslash(_), _, EOF) => return Some(Err(Error::UnexpectedEofString(self.text.len()))),
                (StringBackslash(_), pos, _) => return Some(Err(Error::InvalidEscape(pos-1, next_char(&self.text, pos)))), // -1 bc \ is 1 byte

                // symbols
                (Symbol(start), end, c) if end_of_item(c) => {
                    let token = match &self.text[start..end] {
                        "begin" => Token::Begin,
                        "define" => Token::Define,
                        "if" => Token::If,
                        "let" => Token::Let,
                        "loop" => Token::Loop,
                        "lambda" => Token::Lambda,
                        "recur" => Token::Recur,
                        x => Token::Symbol(x),
                    };

                    Finished((start, token, end))
                },
                (Symbol(start), _, _) => Symbol(start),
                (Finished(_), _, _) => unreachable!(),
            };

            match self.state {
                Finished(token) => {
                    self.state = NewToken;
                    return Some(Ok(token))
                },
                _ if next.2 == EOF => self.state = NewToken, // fuse iterator
                _  => { self.next_char(); }, // bump
            }
        }
    }
}

fn whitespace(x: char) -> bool {
    x == ' ' ||
    x == '\n' ||
    x == '\t'
}

fn end_of_item(x: char) -> bool {
    whitespace(x) ||
    x == ')' ||
    x == EOF
}

pub fn escape_char(x: char) -> Option<char> {
    match x {
        '\n' => Some('n'),
        ' ' => Some('s'),
        '\t' => Some('t'),
        '\\' => Some('\\'),
        _ => None,
    }
}

fn unescape_char(x: char) -> Option<char> {
    match x {
        'n' => Some('\n'),
        's' => Some(' '),
        't' => Some('\t'),
        '\\' => Some('\\'),
        _ => None,
    }
}

fn next_char(text: &str, pos: usize) -> usize {
    pos + char::len_utf8(text.as_bytes()[pos] as char)
}

fn numeric(x: char) -> bool {
    '0' <= x && x <= '9'
}

fn printable_char(x: char) -> bool {
    x >= '!' &&
    x <= '~'
}

#[allow(dead_code)]
pub fn unescape_string(input: &str) -> String {
    let mut chars = input.chars();
    let mut s = String::with_capacity(input.chars().count());

    while let Some(c) = chars.next() {
        if c == '\\' {
            if let Some(next) = chars.next() {
                match next {
                    'n' => s.push('\n'),
                    't' => s.push('\t'),
                    '"' => s.push('"'),
                    's' => s.push(' '),
                    '\\' => s.push('\\'),
                    x => {
                        s.push('\\');
                        s.push(x);
                    }
                }
                continue;
            }
        }
        s.push(c);
    }
    s
}
<commit_msg>remove old code, fix string span<commit_after>use std::str::CharIndices;
use std::ascii::AsciiExt;
use std::iter::Peekable;

use super::error::Error;

// use ascii end of transmission as EOF
const EOF: char = 0x4 as char;

macro_rules! my_try {
    ($e:expr) => ({
        match $e {
            Ok(x) => x,
            Err(x) => return Some(Err(x)),
        }
    });
}

#[derive(Clone, Copy, Debug)]
pub enum Token<'input> {
    OpenParen,
    ClosingParen,
    Dot,
    Quote,
    WhiteSpace,
    Char(char),
    Integer(i64),
    String(&'input str),
    Symbol(&'input str),
    Begin,
    Define,
    If,
    Let,
    Loop,
    Lambda,
    Recur,
}

// Tokenzer state
#[derive(Debug, Clone, Copy)]
enum State<'input> {
    // start
    NewToken,
    Finished(Spanned<Token<'input>>),
    // start
    EatInteger(usize),
    WhiteSpace(usize),
    Minus(usize),
    Symbol(usize),
    StringStart(usize),
    StringBackslash(usize),
    Pound(usize),
    CharBegin(usize),
    EscapedChar(usize),
    FinishedChar(usize, char),
}
use self::State::*;

#[derive(Debug)]
pub struct Tokenizer<'input> {
    text: &'input str,
    chars: Peekable<CharIndices<'input>>,
    state: State<'input>,
}

pub type Spanned<T> = (usize, T, usize);

impl<'input> Tokenizer<'input> {
    pub fn new(text: &'input str) -> Tokenizer<'input> {
        Tokenizer {
            text: text,
            chars: text.char_indices().peekable(),
            state: NewToken,
        }
    }

    fn next_char(&mut self) -> Option<(usize, char)> {
        self.chars.next()
    }

    fn peek_next(&mut self) -> Option<&(usize, char)> {
        self.chars.peek()
    }
}

impl<'input> Iterator for Tokenizer<'input> {
    type Item = Result<Spanned<Token<'input>>, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let next;
            match self.peek_next() {
                Some(&(pos, c)) => next = (self.state, pos, c),
                // check if we still need something
                None => next = (self.state, self.text.len(), EOF),
            }

            self.state = match next {
                // pos + 1 is safe, because all these chars have width 1 byte
                (NewToken, pos, '(') => { self.next_char(); Finished((pos, Token::OpenParen, pos+1)) },
                (NewToken, pos, ')') => { self.next_char(); Finished((pos, Token::ClosingParen, pos+1)) },
                (NewToken, pos, '\'') => { self.next_char(); Finished((pos, Token::Quote, pos+1)) },
                (NewToken, pos, '.') => { self.next_char(); Finished((pos, Token::Dot, pos+1)) },
                (NewToken, pos, '-') => Minus(pos),
                (NewToken, pos, '"') => StringStart(pos),
                (NewToken, pos, '#') => Pound(pos),
                (NewToken, _, EOF) => return None,
                (NewToken, pos, c) if whitespace(c) => WhiteSpace(pos),
                (NewToken, pos, c) if numeric(c) => EatInteger(pos),
                (NewToken, pos, _) => Symbol(pos),

                // minus special case
                (Minus(start), _, c) if numeric(c) => EatInteger(start),
                (Minus(start), end, c) if end_of_item(c) => Finished((start, Token::Symbol(&self.text[start..end]), end)),
                (Minus(start), _, _) => Symbol(start),

                // whitespace
                (WhiteSpace(pos), _, c) if whitespace(c) => WhiteSpace(pos),
                (WhiteSpace(start), end, _) => Finished((start, Token::WhiteSpace, end)),

                // chars
                (Pound(pos), _, '\\') => CharBegin(pos),
                (Pound(start), end, c) if end_of_item(c) => Finished((start, Token::Symbol(&self.text[start..end]), end)),
                (Pound(pos), _, _) => Symbol(pos),

                (CharBegin(pos), _, '\\') => EscapedChar(pos),
                (CharBegin(_), _, EOF) => return Some(Err(Error::UnexpectedEofChar(self.text.len()))),
                (CharBegin(pos), _, c) if printable_char(c) => FinishedChar(pos, c),
                (CharBegin(_), pos, _) => return Some(Err(Error::NonAsciiChar(pos))),

                (EscapedChar(pos), _, c) if unescape_char(c).is_some() => FinishedChar(pos, unescape_char(c).unwrap()),
                (EscapedChar(start), end, EOF) => Finished((start, Token::Char('\\'), end)),
                (EscapedChar(start), end, _) => return Some(Err(Error::InvalidEscape(start+2, next_char(&self.text, end)))), // +2 bc #\ is 2 bytes

                (FinishedChar(start, x), end, c) if end_of_item(c) => Finished((start, Token::Char(x), end)),
                (FinishedChar(_, _), end, _) => return Some(Err(Error::InvalidToken(end, end))),

                // integers
                (EatInteger(start), _, c) if numeric(c) => EatInteger(start),
                (EatInteger(start), pos, c) if end_of_item(c) =>
                    // safe because we checked that text[start..pos] is a valid number
                    Finished((start, Token::Integer(self.text[start..pos].parse().unwrap()), pos)),
                (EatInteger(_), pos, _) => return Some(Err(Error::InvalidToken(pos, pos))),

                // strings
                (StringStart(start), end, '"') => {
                    self.next_char(); // bump
                    let string = &self.text[start+1..end];
                    for (i, c) in string.char_indices() {
                        if !c.is_ascii() {
                            return Some(Err(Error::NonAsciiChar(i+1))) // +1 bc start+1 above
                        }
                    }
                    Finished((start, Token::String(string), end+1))
                },
                (StringStart(_), _, EOF) => return Some(Err(Error::UnexpectedEofString(self.text.len()))),
                (StringStart(start), _, '\\') => StringBackslash(start),
                (StringStart(start), _, _) => StringStart(start),
                (StringBackslash(start), _, '"') => StringStart(start),
                (StringBackslash(start), _, c) if unescape_char(c).is_some() => StringStart(start),
                (StringBackslash(_), _, EOF) => return Some(Err(Error::UnexpectedEofString(self.text.len()))),
                (StringBackslash(_), pos, _) => return Some(Err(Error::InvalidEscape(pos-1, next_char(&self.text, pos)))), // -1 bc \ is 1 byte

                // symbols
                (Symbol(start), end, c) if end_of_item(c) => {
                    let token = match &self.text[start..end] {
                        "begin" => Token::Begin,
                        "define" => Token::Define,
                        "if" => Token::If,
                        "let" => Token::Let,
                        "loop" => Token::Loop,
                        "lambda" => Token::Lambda,
                        "recur" => Token::Recur,
                        x => Token::Symbol(x),
                    };

                    Finished((start, token, end))
                },
                (Symbol(start), _, _) => Symbol(start),
                (Finished(_), _, _) => unreachable!(),
            };

            match self.state {
                Finished(token) => {
                    self.state = NewToken;
                    return Some(Ok(token))
                },
                _ if next.2 == EOF => self.state = NewToken, // fuse iterator
                _  => { self.next_char(); }, // bump
            }
        }
    }
}

fn whitespace(x: char) -> bool {
    x == ' ' ||
    x == '\n' ||
    x == '\t'
}

fn end_of_item(x: char) -> bool {
    whitespace(x) ||
    x == ')' ||
    x == EOF
}

pub fn escape_char(x: char) -> Option<char> {
    match x {
        '\n' => Some('n'),
        ' ' => Some('s'),
        '\t' => Some('t'),
        '\\' => Some('\\'),
        _ => None,
    }
}

fn unescape_char(x: char) -> Option<char> {
    match x {
        'n' => Some('\n'),
        's' => Some(' '),
        't' => Some('\t'),
        '\\' => Some('\\'),
        _ => None,
    }
}

fn next_char(text: &str, pos: usize) -> usize {
    pos + char::len_utf8(text.as_bytes()[pos] as char)
}

fn numeric(x: char) -> bool {
    '0' <= x && x <= '9'
}

fn printable_char(x: char) -> bool {
    x >= '!' &&
    x <= '~'
}

#[allow(dead_code)]
pub fn unescape_string(input: &str) -> String {
    let mut chars = input.chars();
    let mut s = String::with_capacity(input.chars().count());

    while let Some(c) = chars.next() {
        if c == '\\' {
            if let Some(next) = chars.next() {
                match next {
                    'n' => s.push('\n'),
                    't' => s.push('\t'),
                    '"' => s.push('"'),
                    's' => s.push(' '),
                    '\\' => s.push('\\'),
                    x => {
                        s.push('\\');
                        s.push(x);
                    }
                }
                continue;
            }
        }
        s.push(c);
    }
    s
}
<|endoftext|>


<commit_before>///! Token traits, variants, and related data structures.
use std;
use std::fmt;
use std::ops::Range;
use std::default::Default;
use std::hash::{Hash, Hasher};
use std::collections::{LinkedList,HashMap,HashSet};

use symbol;
use symbol::Symbol;
use grammar::Rule;
use grammar::TokenIndex;
use util::intrusive;
use util::intrusive::Ref;

/// Per-token data stored in the index.
struct IndexData {
    /// Reference to the token itself.
    token: Ref<Token>,

    /// Rules in which the token appears on the left-hand side.  These rules
    /// define the token.
    // FIXME: do we need this?  It may make more sense to store rules in their
    //     parent LHS nonterminal or synthetic, since terminals (and
    //     potentially other token types we dream up) don't have rules.
    rules_for: LinkedList<Rule>,

    /// (rule,vec(position)) pairs for rules that contain the token on their
    /// right-hand sides.  Rules in this list are used-by relations.
    rules_containing: LinkedList<(Ref<Rule>,Vec<usize>)>
}

/* ****************************************************************
 * Tokens, and their Exquisite Varieties
 */
/// Data-type used solely to identify the underlying concrete token type.
pub enum Type {
    /// [Terminal](struct.Terminal.html) token.
    Terminal,
    /// [Nonterminal](struct.Nonterminal.html) token.
    Nonterminal,
    /// [Synthetic](struct.Synthetic.html) token.
    Synthetic
}

static TOKEN_SCOPE_SEPARATOR: char = ':';

/// Provides methods to calculate a token's first set.
///
/// A compound token's first set is defined as the union of the first sets for
/// all rules with that token on the left-hand side; a terminal token is its
/// own first set.
///
/// A rule's first set is the union of first sets for leading tokens on its
/// right-hand side, up to and including the first
/// non-[nullable](trait.Nullable.html) token.
pub trait HasFirstSet {
    /// Find the token's first set, and store it into the given vector.
    fn first_set_into(&self, out: &mut Vec<Ref<Token>>);

    /// Calculate and return the token's first set.
    fn first_set(&self) -> Vec<Ref<Token>> {
        let mut out = Vec::new();
        self.first_set_into(&mut out);
        out
    }
}

/// Provides a method to determine whether a token is "nullable", i.e., has any
/// rules that may reduce to an empty sequence.
pub trait Nullable {
    /// Check if the token is nullable.
    fn is_nullable(&self) -> bool { false }
}

/// A token that may be repeated as a Synthetic.
pub trait Repeatable {
    /// Fetch the repetition range for this token.
    fn get_repetitions(&self) -> Range<usize> { 1..2 } /* default 1 */
}

/// Abstract interface to all Token variants.
pub trait Token:
    Eq +
    fmt::Debug +
    symbol::Nameable +
    intrusive::ExplicitlySized +
    intrusive::RefCounted +
    HasFirstSet +
    Repeatable +
    Nullable {
    /// Get the underlying token's type.
    fn token_type(&self) -> Type;

    /// Create a SyntheticToken that represents a variable- or fixed-length
    /// number of repetitions of another token.
    fn repeat(&self, range: Range<usize>) -> Synthetic {
        Synthetic{base: <Self as intrusive::RefCounted>::ref_to_self(), repetitions: range, ..Default::default()}
    }

}

impl<T> intrusive::RefCounted for T where T: Token {
    fn get_refcount(&self) -> usize { self.refcount }
    fn add_ref(&mut self) -> usize { self.refcount += 1; self.refcount }
    fn remove_ref(&mut self) -> usize { self.refcount -= 1; self.refcount }
}

/* ****************************************************************
 * Spec
 */
/// Summary of a token's parameters relevant to its use within a grammar.
/// Spec is used to find and create tokens in
/// a [TokenIndex](struct.TokenIndex.html), which also uses it internally as
/// the key type in mappings to Tokens.
pub struct Spec {
    name: Symbol,
    repetitions: Range<usize>,
    label: Option<Symbol>,
    // parent_scope: Ref<Namespace>
}

impl Default for Spec {
    fn default() -> Spec {
        Spec{name: symbol::Inline::new(""), repetitions: 1..2, label: None}
    }
}

/* ****************************************************************
 * BaseToken -- common base for concrete token variants.
 */

/// Common data shared by all concrete token types.
#[derive(Debug)]
pub struct BaseToken {
    order: usize,
    name: Symbol
}

impl Hash for BaseToken {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state)
    }
}

impl Eq for BaseToken {}
impl PartialEq<BaseToken> for BaseToken
{
    fn eq(&self, other: &BaseToken) -> bool {
        self.name == other.name
    }
}

default_nameable_impl!(BaseToken, name);
default_refcounted_impl!(BaseToken, refcount);
// default_has_root_scope_impl!(BaseToken, Grammar, parent_scope);
// default_has_parent_scope_impl!(BaseToken, Namespace, parent_scope);

/* ****************************************************************
 * LabeledToken
 */
#[derive(Debug)]
pub struct LabeledToken {
    refcount: usize,
    label: Symbol,
    token: Ref<Token>
}

impl symbol::Nameable for LabeledToken { fn name(&self) -> Symbol { self.token.name() } }
default_refcounted_impl!(LabeledToken, refcount);

/* ****************************************************************
 * Synthetic
 */

/// Token representing some constraint on another token.  Synthetics are
/// currently used to represent arbitrary finite repetitions of other tokens.
#[derive(Debug)]
pub struct Synthetic {
    refcount: usize,
    base: Ref<Token>,
    repetitions: Range<usize>,
    rules: HashSet<Ref<Rule>>
}

impl Token for Synthetic {
    fn token_type(&self) -> Type { Type::Synthetic }
}

impl intrusive::ExplicitlySized for Synthetic {
    fn get_type_size(&self) -> usize { std::mem::size_of::<Self>() }
    fn get_type_align(&self) -> usize { std::mem::align_of::<Self>() }
}

impl Repeatable for Synthetic {
    fn get_repetitions(&self) -> Range<usize> {
        self.repetitions
    }
}

impl Default for Synthetic {
    fn default() -> Synthetic {
        Synthetic{refcount: 0, base: Ref::null(), repetitions: 1..2, rules: HashSet::new()}
    }
}

impl Hash for Synthetic {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.base.hash(state);
        self.repetitions.start.hash(state);
        self.repetitions.end.hash(state);
        for ref rule in self.rules.iter() { rule.hash(state) }
    }
}

impl Eq for Synthetic {}
impl PartialEq<Synthetic> for Synthetic
{
    fn eq(&self, other: &Synthetic) -> bool {
        self.base == other.base && self.repetitions == other.repetitions
    }
}

impl Nullable for Synthetic {
    fn is_nullable(&self) -> bool {
        self.repetitions.start == 0 || self.base.is_nullable()
    }
}

impl HasFirstSet for Synthetic { fn first_set_into(&self, out: &mut Vec<Ref<Token>>) { out.push(self.base.clone()) } }
impl symbol::Nameable for Synthetic { fn name(&self) -> Symbol { self.base.name() } }
// impl HasRootScope<Grammar> for Synthetic { fn root_scope(&self) -> Ref<Grammar> { self.base.root_scope() } }
// impl HasParentScope<Namespace> for Synthetic { fn parent_scope(&self) -> Ref<Namespace> { self.base.parent_scope() } }

// impl intrusive::RefCounted for Synthetic {
//     fn get_refcount(&self) -> usize { self.refcount }
//     fn add_ref(&mut self) -> usize { self.refcount += 1; self.refcount }
//     fn remove_ref(&mut self) -> usize { self.refcount -= 1; self.refcount }
// }

/* ****************************************************************
 * Terminal
 */

/// A terminal token.
#[derive(Debug)]
pub struct Terminal {
    refcount: usize,
    base: BaseToken,
}

impl Token for Terminal {
    fn token_type(&self) -> Type { Type::Terminal }
}
impl intrusive::ExplicitlySized for Terminal {
    fn get_type_size(&self) -> usize { std::mem::size_of::<Self>() }
    fn get_type_align(&self) -> usize { std::mem::align_of::<Self>() }
}

impl Repeatable for Terminal {}
impl Nullable for Terminal { fn is_nullable(&self) -> bool { false } }
impl HasFirstSet for Terminal {
    fn first_set_into(&self, out: &mut Vec<Ref<Token>>)  {
        out.push(<Self as intrusive::RefCounted>::ref_to_self())
    }
}

impl Hash for Terminal {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.base.hash(state)
    }
}

impl Eq for Terminal {}
impl PartialEq<Terminal> for Terminal
{
    fn eq(&self, other: &Terminal) -> bool {
        self.base == other.base
    }
}

default_nameable_impl!(Terminal, base.name);
// default_refcounted_impl!(Terminal, base.refcount);
// default_has_root_scope_impl!(Terminal, Grammar, base.parent_scope);
// default_has_parent_scope_impl!(Terminal, Namespace, base.parent_scope);

/* ****************************************************************
 * Nonterminal
 */

/// Any compund (non-atomic) element within a grammar.  Nonterminals serve as
/// syntactic and/or semantic groupings of terminal tokens.
#[derive(Debug)]
pub struct Nonterminal {
    refcount: usize,
    base: BaseToken,
    rules: HashSet<Ref<Rule>>,
    index: TokenIndex,
}

impl Repeatable for Nonterminal {}
default_nameable_impl!(Nonterminal, base.name);
// default_refcounted_impl!(Nonterminal, base.refcount);
// default_has_root_scope_impl!(Nonterminal, Grammar, base.parent_scope);
// default_has_parent_scope_impl!(Nonterminal, Namespace, base.parent_scope);

impl Nonterminal {
    /// Fetch a reference to the token as a `Namespace`.  If the token is not
    /// a `Namespace`, returns Ref(None).
    fn get_scope_index(&self) -> &TokenIndex {
        &self.index
    }

    fn is_superposition(&self) -> bool {
        self.rules.iter().all(|r| r.rhs.len() == 1 && match r.rhs[0].token_type() {
            Type::Terminal => true, _ => false } )
    }

}
impl Token for Nonterminal {
    fn token_type(&self) -> Type { Type::Nonterminal }
}
impl intrusive::ExplicitlySized for Nonterminal {
    fn get_type_size(&self) -> usize { std::mem::size_of::<Self>() }
    fn get_type_align(&self) -> usize { std::mem::align_of::<Self>() }
}

impl Nullable for Nonterminal {
    fn is_nullable(&self) -> bool {
        self.rules.iter().any(|r| r.rhs.len() == 0 || r.rhs.iter().all(|rt| rt.is_nullable()))
    }
}

impl HasFirstSet for Nonterminal {
    fn first_set_into(&self, out: &mut Vec<Ref<Token>>) {
        for rule in self.rules.iter() { rule.first_set_into(out) }
    }
}

impl Hash for Nonterminal {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.base.hash(state);
        for ref rule in self.rules.iter() { rule.hash(state) }
    }
}

impl Eq for Nonterminal {}

impl PartialEq<Nonterminal> for Nonterminal
{
    fn eq(&self, other: &Nonterminal) -> bool {
        self.base == other.base && match self.rules.difference(&other.rules).size_hint() {
            (_, Some(x)) => x == 0,
            _ => false }
    }
}
<commit_msg>grammar/token: latest try<commit_after>//! Token traits, variants, and related data structures.
use std;
use std::fmt;
use std::any::Any;
use std::raw::TraitObject;
//use std::ops::Range;
use std::default::Default;
use std::hash::{hash,Hash, Hasher,SipHasher};
use std::collections::{LinkedList,HashSet};

use symbol;
use ordered;
use symbol::{PackFormat,Symbol};
use grammar::Rule;
use grammar::TokenIndex;
use util::intrusive;
use util::intrusive::{Ref,Reference,RefCountHolder};

/// Data used to quickly find information related to a particular token.
#[derive(Debug)]
pub struct IndexData {
    /// Rules in which the token appears on the left-hand side.  These rules
    /// define the token.
    // FIXME: do we need this?  It may make more sense to store rules in their
    //     parent LHS nonterminal or synthetic, since terminals (and
    //     potentially other token types we dream up) don't have rules.
    pub rules_for: Option<Box<LinkedList<Rule>>>,

    /// (rule,vec(position)) pairs for rules that contain the token on their
    /// right-hand sides.  Rules in this list are used-by relations.
    pub rules_containing: LinkedList<(Ref<Rule>,Vec<usize>)>
}

impl Default for IndexData {
    fn default() -> IndexData {
        IndexData{rules_for: None,
                  rules_containing: LinkedList::new()}
    }
}

/// Trait providing access to a token's index data.
pub trait Indexed {
    fn index_data(&self) -> &IndexData;
}

/* ****************************************************************
 * Tokens, and their Exquisite Varieties
 */
/// Data-type used solely to identify the underlying concrete token type.
pub enum Type {
    /// [Terminal](struct.Terminal.html) token.
    Terminal,
    /// [Nonterminal](struct.Nonterminal.html) token.
    Nonterminal,
    /// [Synthetic](struct.Synthetic.html) token.
    Synthetic,

    /// [Labeled](struct.LabeledToken.html) token.
    Labeled
}

static TOKEN_SCOPE_SEPARATOR: char = ':';

pub trait Typed {
    fn token_type(&self) -> Type;
}

/// Provides methods to calculate a token's first set.
///
/// A compound token's first set is defined as the union of the first sets for
/// all rules with that token on the left-hand side; a terminal token is its
/// own first set.
///
/// A rule's first set is the union of first sets for leading tokens on its
/// right-hand side, up to and including the first
/// non-[nullable](trait.Nullable.html) token.
pub trait HasFirstSet {
    /// Find the token's first set, and store it into the given vector.
    fn first_set_into(&self, out: &mut Vec<Ref<Token>>);

    /// Calculate and return the token's first set.
    fn first_set(&self) -> Vec<Ref<Token>> {
        let mut out = Vec::new();
        self.first_set_into(&mut out);
        out
    }
}

/// Provides a method to determine whether a token is "nullable", i.e., has any
/// rules that may reduce to an empty sequence.
pub trait Nullable {
    /// Check if the token is nullable.
    fn is_nullable(&self) -> bool { false }
}

/// A token that may be repeated as a Synthetic.
pub trait Repeatable: intrusive::RefCounted {
    /// Fetch the repetition range for this token.
    fn get_repetitions(&self) -> std::ops::Range<usize> { 1..2 } /* default 1 */
}

/* ****************************************************************
 * Token
 */
/// Abstract interface to all Token variants.
pub trait Token:
    fmt::Debug +
    ordered::Ordered +
    symbol::Nameable +
    intrusive::RefCounted +
    Indexed +
    HasFirstSet +
    Repeatable +
    Nullable {
    /// Get the underlying token's type.
    fn token_type(&self) -> Type;
}

impl Eq for Token {}

/// We Consider two tokens to be identical if they live at the same address.
/// This is a complete hack, but will have to do since neither Hash nor
/// PartialEq are object-safe.
impl PartialEq<Token> for Token {
    fn eq(&self, other: &Token) -> bool {
        use std::mem::transmute;
        unsafe { transmute::<_,TraitObject>(self).data ==
                 transmute::<_,TraitObject>(other).data }
    }
}

/* ****************************************************************
 * Spec
 */
#[derive(Copy,Debug)]
pub struct Range<T>{start: T, end: T}
impl<T> Range<T> {
    pub fn from_range(r: std::ops::Range<T>) -> Range<T> {
        Range{start: r.start, end: r.end}
    }

    pub fn as_range(&self) -> std::ops::Range<T> where T: Copy {
        std::ops::Range{start: self.start, end: self.end}
    }
}

impl<T> Eq for Range<T> where T: Eq {}
impl<T> PartialEq<Range<T>> for Range<T> where T: Eq {
    fn eq(&self, other: &Range<T>) -> bool {
        self.start == other.start && self.end == other.end
    }
}

/* ****************************************************************
 * Spec
 */
/// Summary of a token's parameters relevant to its use within a grammar.
/// Spec is used to find and create tokens in
/// a [TokenIndex](struct.TokenIndex.html), which also uses it internally as
/// the key type in mappings to Tokens.
#[derive(Copy)]
pub struct Spec {
    pub name: Symbol,

    /// Number of occurrences the token should match.  Specifying this value as
    /// anything other than `1..2` (one repetition) will create
    /// a Synthetic token.
    pub repetitions: Range<usize>,

    /// Label for the token.  If not specified, the token will be an
    /// unlabeled/standard token.
    pub label: Option<Symbol>,

    // /// Reference to base token to use for synthetic and labeled specs.
    // /// If this is not specified, one will be automatically selected
    // /// and/or created.
    // pub base: Option<Ref<Token>>
    // parent_scope: Ref<Namespace>
}

impl Spec {
    /// Determine whether the spec calls for a synthetic (repeated) token.
    pub fn is_synthetic(&self) -> bool {
        self.repetitions.as_range() != (1..2)
    }

    /// Determine whether the spec calls for a labeled token instance.
    pub fn is_labeled(&self) -> bool {
        match self.label {
            Some(..) => true,
            _ => false }
    }

    /// Determine whether the spec implies the existence of some base token.
    pub fn requires_base(&self) -> bool {
        self.is_synthetic() || self.is_labeled()
    }

    /// Create a copy of this Spec with the topmost modifier (label or
    /// repetitions) removed.
    pub fn base_spec(&self) -> Spec {
        if self.is_labeled()
        { Spec{name: self.name,
               repetitions: self.repetitions,
               ..Default::default()} }
        else if self.is_synthetic()
        { Spec{name: self.name,
               ..Default::default()} }
        else { *self }
    }
}

impl Default for Spec {
    fn default() -> Spec {
        Spec{name: symbol::Inline::new("").pack(),
             repetitions: Range::from_range(1..2),
             label: None}
    }
}

impl Eq for Spec {}

impl PartialEq<Spec> for Spec {
    fn eq(&self, other: &Spec) -> bool {
        self.name == other.name &&
            self.repetitions == other.repetitions &&
            self.label == other.label
    }
}

impl Hash for Spec {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        self.repetitions.start.hash(state);
        self.repetitions.end.hash(state);
        self.label.hash(state);
    }
}

/* ****************************************************************
 * BaseToken -- common base for concrete token variants.
 */

/// Common data shared by all concrete token types.
#[derive(Debug)]
pub struct BaseToken {
    pub name: Symbol,
    pub order: usize,
    pub index: IndexData
}

impl BaseToken {
    pub fn new(name: Symbol, order: usize) -> BaseToken {
        BaseToken{name: name, order: order, index: <IndexData as Default>::default()}
    }
}

impl ordered::Ordered for BaseToken {
    fn get_order(&self) -> usize { self.order }
}

impl Hash for BaseToken {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state)
    }
}

impl Eq for BaseToken {}
impl PartialEq<BaseToken> for BaseToken
{
    fn eq(&self, other: &BaseToken) -> bool {
        self.name == other.name
    }
}

default_nameable_impl!(BaseToken, name);
// default_has_root_scope_impl!(BaseToken, Grammar, parent_scope);
// default_has_parent_scope_impl!(BaseToken, Namespace, parent_scope);

/* ****************************************************************
 * LabeledToken
 */
#[derive(Debug)]
pub struct LabeledToken {
    refcount: usize,
    label: Symbol,
    token: Ref<Token>
}

impl LabeledToken {
    pub fn new(base: Ref<Token>, label: Symbol) -> LabeledToken {
        LabeledToken{refcount: 1, label: label, token: base}
    }
}

impl Indexed for LabeledToken { fn index_data(&self) -> &IndexData { self.token.index_data() } }
impl symbol::Nameable for LabeledToken { fn name(&self) -> Symbol { self.token.name() } }
default_refcounted_impl!(LabeledToken);

/* ****************************************************************
 * Synthetic
 */

/// Token representing some constraint on another token.  Synthetics are
/// currently used to represent arbitrary finite repetitions of other tokens.
#[derive(Debug)]
pub struct Synthetic {
    refcount: usize,
    base: Ref<Token>,
    repetitions: Range<usize>,
    rules: HashSet<Ref<Rule>>
}

impl Synthetic {
    pub fn new(base: Ref<Token>, repetitions: Range<usize>) -> Synthetic {
        Synthetic{refcount: 1, base: base, repetitions: repetitions,
                  rules: HashSet::new()}
    }
}
impl Token for Synthetic {
    fn token_type(&self) -> Type { Type::Synthetic }
}

impl Indexed for Synthetic { fn index_data(&self) -> &IndexData { self.base.index_data() } }

impl ordered::Ordered for Synthetic {
    fn get_order(&self) -> usize { self.base.get_order() }
}

impl Repeatable for Synthetic {
    fn get_repetitions(&self) -> std::ops::Range<usize> {
        self.repetitions.as_range()
    }
}

impl Default for Synthetic {
    fn default() -> Synthetic {
        Synthetic{refcount: 0, base: Ref::null(), repetitions: Range::from_range(1..2), rules: HashSet::new()}
    }
}

impl Hash for Synthetic {
    fn hash<H: Hasher>(&self, state: &mut H) {
        unsafe { self.base.data_id() }.hash(state);
        self.repetitions.start.hash(state);
        self.repetitions.end.hash(state);
        for ref rule in self.rules.iter() { rule.hash(state) }
    }
}

impl Eq for Synthetic {}
impl PartialEq<Synthetic> for Synthetic
{
    fn eq(&self, other: &Synthetic) -> bool {
        self.base.data_id() == other.base.data_id() && self.repetitions == other.repetitions
    }
}

impl Nullable for Synthetic {
    fn is_nullable(&self) -> bool {
        self.repetitions.start == 0 || self.base.is_nullable()
    }
}

impl HasFirstSet for Synthetic { fn first_set_into(&self, out: &mut Vec<Ref<Token>>) { out.push(self.base.clone()) } }
impl symbol::Nameable for Synthetic { fn name(&self) -> Symbol { self.base.name() } }
default_refcounted_impl!(Synthetic);
// impl HasRootScope<Grammar> for Synthetic { fn root_scope(&self) -> Ref<Grammar> { self.base.root_scope() } }
// impl HasParentScope<Namespace> for Synthetic { fn parent_scope(&self) -> Ref<Namespace> { self.base.parent_scope() } }

// impl intrusive::RefCounted for Synthetic {
//     fn get_refcount(&self) -> usize { self.refcount }
//     fn add_ref(&mut self) -> usize { self.refcount += 1; self.refcount }
//     fn remove_ref(&mut self) -> usize { self.refcount -= 1; self.refcount }
// }

/* ****************************************************************
 * Terminal
 */

/// A terminal token.
#[derive(Debug)]
pub struct Terminal {
    refcount: usize,
    base: BaseToken,
}

impl Terminal {
    pub fn new(sym: Symbol, ord: usize) -> Terminal {
        Terminal{refcount: 1,
                 base: BaseToken::new(sym, ord)}
    }
}

impl Indexed for Terminal { fn index_data(&self) -> &IndexData { &self.base.index } }

impl ordered::Ordered for Terminal {
    fn get_order(&self) -> usize { self.base.order }
}

impl Token for Terminal {
    fn token_type(&self) -> Type { Type::Terminal }
}

impl Repeatable for Terminal {}
impl Nullable for Terminal { fn is_nullable(&self) -> bool { false } }
impl HasFirstSet for Terminal {
    fn first_set_into(&self, out: &mut Vec<Ref<Token>>)  {
        let x = Reference::<Token>::from_trait_obj(self);
        out.push(x);
    }
}

impl Hash for Terminal {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.base.hash(state)
    }
}

impl Eq for Terminal {}
impl PartialEq<Terminal> for Terminal
{
    fn eq(&self, other: &Terminal) -> bool {
        self.base == other.base
    }
}

default_nameable_impl!(Terminal, base.name);
default_refcounted_impl!(Terminal);
// default_has_root_scope_impl!(Terminal, Grammar, base.parent_scope);
// default_has_parent_scope_impl!(Terminal, Namespace, base.parent_scope);

/* ****************************************************************
 * Nonterminal
 */

/// Any compund (non-atomic) element within a grammar.  Nonterminals serve as
/// syntactic and/or semantic groupings of terminal tokens.
#[derive(Debug)]
pub struct Nonterminal {
    refcount: usize,
    base: BaseToken,
    rules: HashSet<Ref<Rule>>,
    index: Option<Box<TokenIndex>>,
}

impl Repeatable for Nonterminal {}
default_nameable_impl!(Nonterminal, base.name);
default_refcounted_impl!(Nonterminal);

// default_has_root_scope_impl!(Nonterminal, Grammar, base.parent_scope);
// default_has_parent_scope_impl!(Nonterminal, Namespace, base.parent_scope);

impl Nonterminal {
    pub fn new(sym: Symbol, ord: usize) -> Nonterminal {
        Nonterminal{refcount: 1,
                    base: BaseToken::new(sym, ord),
                    rules: HashSet::new(),
                    index: None}
    }

    fn is_superposition(&self) -> bool {
        self.rules.iter().all(|r| r.rhs.len() == 1 && match r.rhs[0].token_type() {
            Type::Terminal => true, _ => false } )
    }

}
impl Token for Nonterminal {
    fn token_type(&self) -> Type { Type::Nonterminal }
}

impl Indexed for Nonterminal { fn index_data(&self) -> &IndexData { &self.base.index } }

impl ordered::Ordered for Nonterminal {
    fn get_order(&self) -> usize { self.base.order }
}

impl Nullable for Nonterminal {
    fn is_nullable(&self) -> bool {
        self.rules.iter().any(|r| r.rhs.len() == 0 || r.rhs.iter().all(|rt| rt.is_nullable()))
    }
}

impl HasFirstSet for Nonterminal {
    fn first_set_into(&self, out: &mut Vec<Ref<Token>>) {
        for rule in self.rules.iter() { rule.first_set_into(out) }
    }
}

impl Hash for Nonterminal {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.base.hash(state);
        for ref rule in self.rules.iter() { rule.hash(state) }
    }
}

impl Eq for Nonterminal {}

impl PartialEq<Nonterminal> for Nonterminal
{
    fn eq(&self, other: &Nonterminal) -> bool {
        self.base == other.base && match self.rules.difference(&other.rules).size_hint() {
            (_, Some(x)) => x == 0,
            _ => false }
    }
}
<|endoftext|>


<commit_before>use scene::{BoxedScene, Scene, SceneChangeEvent, SceneName};
use engine::EngineData;
use text_renderer::TextRenderer;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::surface::Surface;
use std::io::Result;
use std::path::Path;
use uorustlibs::art::{ArtReader, Art};
use uorustlibs::hues::{HueReader, HueGroup, Hue};
use uorustlibs::color::Color as ColorTrait;
use uorustlibs::map::{MapReader, Block};

use std::io::{Error, };
use sdl2::render::{Renderer, Texture, TextureQuery};
use sdl2::rect::Rect;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

const MAX_BLOCKS_WIDTH: usize = 64;
const MAX_BLOCKS_HEIGHT: usize = 64;

pub struct MapScene {
    reader: Result<MapReader>,
    x: u32,
    y: u32,
    texture: Option<Texture>
}

impl MapScene {
    pub fn new<'a>(renderer: &mut Renderer, engine_data: &mut EngineData<'a>) -> BoxedScene<SceneName, EngineData<'a>> {
        let mut scene = Box::new(MapScene {
            reader: MapReader::new(&Path::new("./assets/map0.mul"), 768, 512),
            x: 0,
            y: 0,
            texture: None
        });
        scene.draw_page(renderer, engine_data);
        scene
    }

    pub fn draw_page(&mut self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        let mut blocks = vec![];

        match self.reader {
            Ok(ref mut reader) => {

                for y in 0..MAX_BLOCKS_WIDTH {
                    for x in 0..MAX_BLOCKS_HEIGHT {
                        blocks.push(reader.read_block_from_coordinates(self.x + x as u32, self.y + y as u32));
                    }
                }
            },
            _ => ()
        };
        let mut surface = Surface::new(1024, 768, PixelFormatEnum::RGBA8888).unwrap();
        for y in 0..MAX_BLOCKS_HEIGHT {
            for x in 0..MAX_BLOCKS_WIDTH {
                match blocks[x + (y * 8)] {
                    Ok(ref block) => {
                        let block_surface = self.draw_block(block);
                        block_surface.blit(None, &mut surface, Some(Rect::new(x as i32 * 8, y as i32* 8, block_surface.width(), block_surface.height())));
                    },
                    _ => ()
                }
            }
        }
        self.texture = Some(renderer.create_texture_from_surface(&surface).unwrap());
    }

    pub fn draw_block(&self, block: &Block) -> Surface {
        let mut surface = Surface::new(8, 8, PixelFormatEnum::RGBA8888).unwrap();
        surface.with_lock_mut(|bitmap| {
            let mut read_idx = 0;

            for y in 0..8 {
                for x in 0..8 {
                    let target = (x + (y * 8));
                    let height = (block.cells[target].altitude as i16 + 128) as u8;
                    bitmap[target * 4] = 255;
                    bitmap[target * 4 + 1] = height;
                    bitmap[target * 4 + 2] = height;
                    bitmap[target * 4 + 3] = height;
                }
            };
        });
        surface
    }
}

impl<'a> Scene<SceneName, EngineData<'a>> for MapScene {
    fn render(&self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        renderer.clear();
        match self.texture {
            Some(ref texture) => {
                renderer.copy(texture, None, None).unwrap();
            },
            None => ()
        };
        renderer.present();
    }

    fn handle_event(&mut self, event: &Event, renderer: &mut Renderer, engine_data: &mut EngineData) -> Option<SceneChangeEvent<SceneName>> {
        match *event {
            Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                Some(SceneChangeEvent::PopScene)
            },
            Event::KeyDown { keycode: Some(Keycode::Left), .. } => {
                if self.x > 0 {
                    self.x -= 1;
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Right), .. } => {
                self.x += 1;
                self.draw_page(renderer, engine_data);
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Up), .. } => {
                if self.y > 0 {
                    self.y -= 1;
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Down), .. } => {
                self.y += 1;
                self.draw_page(renderer, engine_data);
                None
            },
             _ => None
        }
    }
}
<commit_msg>Basic map rendering now works correctly<commit_after>use scene::{BoxedScene, Scene, SceneChangeEvent, SceneName};
use engine::EngineData;
use text_renderer::TextRenderer;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::surface::Surface;
use std::io::Result;
use std::path::Path;
use uorustlibs::art::{ArtReader, Art};
use uorustlibs::hues::{HueReader, HueGroup, Hue};
use uorustlibs::color::Color as ColorTrait;
use uorustlibs::map::{MapReader, Block};

use std::io::{Error, };
use sdl2::render::{Renderer, Texture, TextureQuery};
use sdl2::rect::Rect;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

const MAX_BLOCKS_WIDTH: usize = 1024 / 8;
const MAX_BLOCKS_HEIGHT: usize = 768 / 8;

pub struct MapScene {
    reader: Result<MapReader>,
    x: u32,
    y: u32,
    texture: Option<Texture>
}

impl MapScene {
    pub fn new<'a>(renderer: &mut Renderer, engine_data: &mut EngineData<'a>) -> BoxedScene<SceneName, EngineData<'a>> {
        let mut scene = Box::new(MapScene {
            reader: MapReader::new(&Path::new("./assets/map0.mul"), 768, 512),
            x: 200,
            y: 200,
            texture: None
        });
        scene.draw_page(renderer, engine_data);
        scene
    }

    pub fn draw_page(&mut self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        let mut blocks = vec![];

        match self.reader {
            Ok(ref mut reader) => {
                for y in 0..MAX_BLOCKS_HEIGHT {
                    for x in 0..MAX_BLOCKS_WIDTH {
                        let block = reader.read_block_from_coordinates(self.x + x as u32, self.y + y as u32);
                        blocks.push(block);
                    }
                }
            },
            _ => ()
        };
        let mut surface = Surface::new(1024, 768, PixelFormatEnum::RGBA8888).unwrap();
        for y in 0..MAX_BLOCKS_HEIGHT {
            for x in 0..MAX_BLOCKS_WIDTH {
                match blocks[x + (y * MAX_BLOCKS_WIDTH)] {
                    Ok(ref block) => {
                        let block_surface = self.draw_block(block);
                        block_surface.blit(None, &mut surface, Some(Rect::new(x as i32 * 8, y as i32* 8, block_surface.width(), block_surface.height())));
                    },
                    _ => ()
                }
            }
        }
        self.texture = Some(renderer.create_texture_from_surface(&surface).unwrap());
    }

    pub fn draw_block(&self, block: &Block) -> Surface {
        let mut surface = Surface::new(8, 8, PixelFormatEnum::RGBA8888).unwrap();
        surface.with_lock_mut(|bitmap| {
            let mut read_idx = 0;

            for y in 0..8 {
                for x in 0..8 {
                    let target = (x + (y * 8));
                    let height = (block.cells[target].altitude as i16 + 128) as u8;
                    bitmap[target * 4] = 255;
                    bitmap[target * 4 + 1] = height;
                    bitmap[target * 4 + 2] = height;
                    bitmap[target * 4 + 3] = height;
                }
            };
        });
        surface
    }
}

impl<'a> Scene<SceneName, EngineData<'a>> for MapScene {
    fn render(&self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        renderer.clear();
        match self.texture {
            Some(ref texture) => {
                renderer.copy(texture, None, None).unwrap();
            },
            None => ()
        };
        renderer.present();
    }

    fn handle_event(&mut self, event: &Event, renderer: &mut Renderer, engine_data: &mut EngineData) -> Option<SceneChangeEvent<SceneName>> {
        match *event {
            Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                Some(SceneChangeEvent::PopScene)
            },
            Event::KeyDown { keycode: Some(Keycode::Left), .. } => {
                if self.x > 0 {
                    self.x -= (MAX_BLOCKS_WIDTH as u32 / 4);
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Right), .. } => {
                self.x += (MAX_BLOCKS_WIDTH as u32 / 4);
                self.draw_page(renderer, engine_data);
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Up), .. } => {
                if self.y > 0 {
                    self.y -= (MAX_BLOCKS_HEIGHT as u32 / 4);
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Down), .. } => {
                self.y += (MAX_BLOCKS_HEIGHT as u32 / 4);
                self.draw_page(renderer, engine_data);
                None
            },
             _ => None
        }
    }
}
<|endoftext|>


<commit_before>//! Compiler plugin for Rust-PHF
//!
//! See the documentation for the `phf` crate for more details.
#[crate_id="github.com/sfackler/rust-phf/phf_mac"];
#[crate_type="dylib"];
#[doc(html_root_url="http://www.rust-ci.org/sfackler/rust-phf/doc")];
#[feature(managed_boxes, macro_registrar)];

extern mod extra;
extern mod syntax;
extern mod phf;

use extra::time;
use std::hashmap::HashMap;
use std::rand;
use std::os;
use std::vec;
use syntax::ast;
use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{COMMA, EOF, FAT_ARROW};

static DEFAULT_LAMBDA: uint = 5;

#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("phf_map"),
             NormalTT(~BasicMacroExpander {
                expander: expand_mphf_map,
                span: None
             },
             None));
}

struct Entry {
    key_str: @str,
    key: @Expr,
    value: @Expr
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_owned());
    let mut entries = ~[];

    let mut bad = false;
    while parser.token != EOF {
        let key = parser.parse_expr();

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(s, _) => s,
                    _ => {
                        cx.span_err(key.span, "expected string literal");
                        bad = true;
                        @""
                    }
                }
            }
            _ => {
                cx.span_err(key.span, "expected string literal");
                bad = true;
                @""
            }
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_err(parser.span, "expected `=>`");
            return MacResult::dummy_expr();
        }

        let value = parser.parse_expr();

        entries.push(Entry {
            key_str: key_str,
            key: key,
            value: value
        });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return MacResult::dummy_expr();
        }
    }

    if entries.len() > phf::MAX_SIZE {
        cx.span_err(parser.span,
                    format!("maps with more than {} items are not supported",
                            phf::MAX_SIZE));
        return MacResult::dummy_expr();
    }

    if bad {
        return MacResult::dummy_expr();
    }

    entries.sort_by(|a, b| a.key_str.cmp(&b.key_str));
    if check_for_duplicates(cx, sp, entries) {
        return MacResult::dummy_expr();
    }

    let start = time::precise_time_s();
    let state;
    loop {
        match generate_hash(entries) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = time::precise_time_s() - start;
    if os::getenv("PHF_STATS").is_some() {
        cx.parse_sess().span_diagnostic.span_note(sp, format!("PHF generation took {} seconds", time));
    }

    let len = entries.len();
    let k1 = state.k1;
    let k2 = state.k2;
    let disps = state.disps.iter().map(|&(d1, d2)| {
            quote_expr!(&*cx, ($d1, $d2))
        }).collect();
    let disps = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(disps, MutImmutable),
        span: sp,
    };
    let entries = state.map.iter().map(|&idx| {
            match idx {
                Some(idx) => {
                    let Entry { key, value, .. } = entries[idx];
                    quote_expr!(&*cx, Some(($key, $value)))
                }
                None => quote_expr!(&*cx, None),
            }
        }).collect();
    let entries = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(entries, MutImmutable),
        span: sp,
    };

    MRExpr(quote_expr!(cx, PhfMap {
        len: $len,
        k1: $k1,
        k2: $k2,
        disps: &'static $disps,
        entries: &'static $entries,
    }))
}

fn check_for_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut in_dup = false;
    for window in entries.windows(2) {
        let ref a = window[0];
        let ref b = window[1];
        if a.key_str == b.key_str {
            dups = true;
            if !in_dup {
                cx.span_err(sp, format!("duplicate key \"{}\"", a.key_str));
                cx.span_note(a.key.span, "one occurrence here");
                in_dup = true;
            }
            cx.span_note(b.key.span, "one occurrence here");
        } else {
            in_dup = false;
        }
    }

    dups
}

struct HashState {
    k1: u64,
    k2: u64,
    disps: ~[(uint, uint)],
    map: ~[Option<uint>],
}

fn generate_hash(entries: &[Entry]) -> Option<HashState> {
    struct Bucket {
        idx: uint,
        keys: ~[uint],
    }

    struct Hashes {
        g: uint,
        f1: uint,
        f2: uint,
    }

    let k1 = rand::random();
    let k2 = rand::random();

    let hashes = entries.iter().map(|entry| {
            let (g, f1, f2) = phf::hash(entry.key_str, k1, k2);
            Hashes {
                g: g,
                f1: f1,
                f2: f2
            }
        }).to_owned_vec();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = vec::from_fn(buckets_len,
                                   |i| Bucket { idx: i, keys: ~[] });

    for (i, hash) in hashes.iter().enumerate() {
        buckets[hash.g % buckets_len].keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

    let table_len = entries.len();
    let mut map = vec::from_elem(table_len, None);
    let mut disps = vec::from_elem(buckets_len, None);
    let mut try_map = HashMap::new();
    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len) {
            'disps: for d2 in range(0, table_len) {
                try_map.clear();
                for &key in bucket.keys.iter() {
                    let idx = phf::displace(hashes[key].f1, hashes[key].f2,
                                            d1, d2) % table_len;
                    if try_map.find(&idx).is_some() || map[idx].is_some() {
                        continue 'disps;
                    }
                    try_map.insert(idx, key);
                }

                // We've picked a good set of disps
                disps[bucket.idx] = Some((d1, d2));
                for (&idx, &key) in try_map.iter() {
                    map[idx] = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    let disps = disps.move_iter().map(|i| i.expect("should have a bucket")).collect();

    Some(HashState {
        k1: k1,
        k2: k2,
        disps: disps,
        map: map,
    })
}
<commit_msg>Use convenience span_note<commit_after>//! Compiler plugin for Rust-PHF
//!
//! See the documentation for the `phf` crate for more details.
#[crate_id="github.com/sfackler/rust-phf/phf_mac"];
#[crate_type="dylib"];
#[doc(html_root_url="http://www.rust-ci.org/sfackler/rust-phf/doc")];
#[feature(managed_boxes, macro_registrar)];

extern mod extra;
extern mod syntax;
extern mod phf;

use extra::time;
use std::hashmap::HashMap;
use std::rand;
use std::os;
use std::vec;
use syntax::ast;
use syntax::ast::{Name, TokenTree, LitStr, MutImmutable, Expr, ExprVec, ExprLit};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MRExpr,
                        NormalTT,
                        BasicMacroExpander};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{COMMA, EOF, FAT_ARROW};

static DEFAULT_LAMBDA: uint = 5;

#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("phf_map"),
             NormalTT(~BasicMacroExpander {
                expander: expand_mphf_map,
                span: None
             },
             None));
}

struct Entry {
    key_str: @str,
    key: @Expr,
    value: @Expr
}

fn expand_mphf_map(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult {
    let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(),
                                                tts.to_owned());
    let mut entries = ~[];

    let mut bad = false;
    while parser.token != EOF {
        let key = parser.parse_expr();

        let key_str = match key.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(s, _) => s,
                    _ => {
                        cx.span_err(key.span, "expected string literal");
                        bad = true;
                        @""
                    }
                }
            }
            _ => {
                cx.span_err(key.span, "expected string literal");
                bad = true;
                @""
            }
        };

        if !parser.eat(&FAT_ARROW) {
            cx.span_err(parser.span, "expected `=>`");
            return MacResult::dummy_expr();
        }

        let value = parser.parse_expr();

        entries.push(Entry {
            key_str: key_str,
            key: key,
            value: value
        });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return MacResult::dummy_expr();
        }
    }

    if entries.len() > phf::MAX_SIZE {
        cx.span_err(parser.span,
                    format!("maps with more than {} items are not supported",
                            phf::MAX_SIZE));
        return MacResult::dummy_expr();
    }

    if bad {
        return MacResult::dummy_expr();
    }

    entries.sort_by(|a, b| a.key_str.cmp(&b.key_str));
    if check_for_duplicates(cx, sp, entries) {
        return MacResult::dummy_expr();
    }

    let start = time::precise_time_s();
    let state;
    loop {
        match generate_hash(entries) {
            Some(s) => {
                state = s;
                break;
            }
            None => {}
        }
    }
    let time = time::precise_time_s() - start;
    if os::getenv("PHF_STATS").is_some() {
        cx.span_note(sp, format!("PHF generation took {} seconds", time));
    }

    let len = entries.len();
    let k1 = state.k1;
    let k2 = state.k2;
    let disps = state.disps.iter().map(|&(d1, d2)| {
            quote_expr!(&*cx, ($d1, $d2))
        }).collect();
    let disps = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(disps, MutImmutable),
        span: sp,
    };
    let entries = state.map.iter().map(|&idx| {
            match idx {
                Some(idx) => {
                    let Entry { key, value, .. } = entries[idx];
                    quote_expr!(&*cx, Some(($key, $value)))
                }
                None => quote_expr!(&*cx, None),
            }
        }).collect();
    let entries = @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(entries, MutImmutable),
        span: sp,
    };

    MRExpr(quote_expr!(cx, PhfMap {
        len: $len,
        k1: $k1,
        k2: $k2,
        disps: &'static $disps,
        entries: &'static $entries,
    }))
}

fn check_for_duplicates(cx: &mut ExtCtxt, sp: Span, entries: &[Entry]) -> bool {
    let mut dups = false;
    let mut in_dup = false;
    for window in entries.windows(2) {
        let ref a = window[0];
        let ref b = window[1];
        if a.key_str == b.key_str {
            dups = true;
            if !in_dup {
                cx.span_err(sp, format!("duplicate key \"{}\"", a.key_str));
                cx.span_note(a.key.span, "one occurrence here");
                in_dup = true;
            }
            cx.span_note(b.key.span, "one occurrence here");
        } else {
            in_dup = false;
        }
    }

    dups
}

struct HashState {
    k1: u64,
    k2: u64,
    disps: ~[(uint, uint)],
    map: ~[Option<uint>],
}

fn generate_hash(entries: &[Entry]) -> Option<HashState> {
    struct Bucket {
        idx: uint,
        keys: ~[uint],
    }

    struct Hashes {
        g: uint,
        f1: uint,
        f2: uint,
    }

    let k1 = rand::random();
    let k2 = rand::random();

    let hashes = entries.iter().map(|entry| {
            let (g, f1, f2) = phf::hash(entry.key_str, k1, k2);
            Hashes {
                g: g,
                f1: f1,
                f2: f2
            }
        }).to_owned_vec();

    let buckets_len = (entries.len() + DEFAULT_LAMBDA - 1) / DEFAULT_LAMBDA;
    let mut buckets = vec::from_fn(buckets_len,
                                   |i| Bucket { idx: i, keys: ~[] });

    for (i, hash) in hashes.iter().enumerate() {
        buckets[hash.g % buckets_len].keys.push(i);
    }

    // Sort descending
    buckets.sort_by(|a, b| b.keys.len().cmp(&a.keys.len()));

    let table_len = entries.len();
    let mut map = vec::from_elem(table_len, None);
    let mut disps = vec::from_elem(buckets_len, None);
    let mut try_map = HashMap::new();
    'buckets: for bucket in buckets.iter() {
        for d1 in range(0, table_len) {
            'disps: for d2 in range(0, table_len) {
                try_map.clear();
                for &key in bucket.keys.iter() {
                    let idx = phf::displace(hashes[key].f1, hashes[key].f2,
                                            d1, d2) % table_len;
                    if try_map.find(&idx).is_some() || map[idx].is_some() {
                        continue 'disps;
                    }
                    try_map.insert(idx, key);
                }

                // We've picked a good set of disps
                disps[bucket.idx] = Some((d1, d2));
                for (&idx, &key) in try_map.iter() {
                    map[idx] = Some(key);
                }
                continue 'buckets;
            }
        }

        // Unable to find displacements for a bucket
        return None;
    }

    let disps = disps.move_iter().map(|i| i.expect("should have a bucket")).collect();

    Some(HashState {
        k1: k1,
        k2: k2,
        disps: disps,
        map: map,
    })
}
<|endoftext|>


<commit_before>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let file = try!(File::open(inp));
    // TODO: read 12 bytes from the Blender file
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    read_blend_file(inp);
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<commit_msg>Read 12 bytes from the Blender file into a string.<commit_after>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let file = try!(File::open(inp));
    // read 12 bytes from the Blender file
    let mut take = file.take(12u64);
    let mut header = String::new();
    try!(take.read_to_string(&mut header));
    println!("header = \"{}\"", header);
    // TODO: compare first 7 chars to "BLENDER"
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    read_blend_file(inp);
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<|endoftext|>