<commit_before>extern mod std;
use std::getopts::*;
use std::time::*;
//use core::container::Map;
use core::hashmap::linear::LinearMap;
use core::io::ReaderUtil;

fn print_usage(program: &str, _opts: &[std::getopts::Opt]) {
    io::println(fmt!("Usage: %s [options]", program));
    io::println("-o\t\tOutput");
    io::println("-h --help\tUsage");
}

// fn make_absolute(p: &Path) -> Path

fn get_license(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x.to_lower(),
        None => ~"bsd3"
    }
}

fn get_proj(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x,
        None => {
            match os::getcwd().filename() {
                Some(dir) => dir,
                None => ~"project"
            }
        }
    }
}

fn get_org(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x,
            None => {
                match os::getenv("USER") {
                    Some(dir) => dir,
                    None => ~"organization"
                }
            }
        }
}

fn get_year(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x,
        None => {
            let time = now();
            time.strftime("%Y")
        }
    }
}

fn load_file_template(file: ~str) -> ~str {
    io::println(file);
    let open_file = &io::file_reader(&Path(file));
    let test_reader = result::get(open_file);
    test_reader.each_line(fn@ (line: &str) -> bool {
        io::println(line);
        true
    });
   // check if file exist. If true, replace with a regexp. Else raise an error
    ~"error"
}

fn main() {
    let args = os::args();

    let program = copy args[0];

    let opts = ~[
        optflag(~"h"),
        optflag(~"help"),
        optopt(~"year"),
        optopt(~"proj"),
        optopt(~"org"),
        optopt(~"license")
    ];
    let matches = match getopts(vec::tail(args), opts) {
        result::Ok(m) => { m }
        result::Err(f) => { fail!(fail_str(f)) }
    };
    if opt_present(&matches, "h") || opt_present(&matches, "help") {
        print_usage(program, opts);
        return;
    }
    let year = opt_maybe_str(&matches, ~"year");
    let proj = opt_maybe_str(&matches, ~"proj");
    let org = opt_maybe_str(&matches, ~"org");
    let license = opt_maybe_str(&matches, ~"license");

    // io::println(get_year(year));
    // io::println(get_proj(proj));
    // io::println(get_org(org));
    // io::println(get_license(license));

    let mut context: LinearMap<~str, ~str> = LinearMap::new::<~str, ~str>();
    context.insert(~"year", get_year(year));
    context.insert(~"proj", get_proj(proj));
    context.insert(~"org", get_org(org));
    context.insert(~"license", get_license(license));

    let template = load_file_template(~"template-" + *context.get(&~"license") + ~".txt");
    // io::println(template);
    // let content = generate_license(template);
}<commit_msg>Read while stream and put it in string<commit_after>extern mod std;
use std::getopts::*;
use std::time::*;
//use core::container::Map;
use core::hashmap::linear::LinearMap;
use core::io::ReaderUtil;

fn print_usage(program: &str, _opts: &[std::getopts::Opt]) {
    io::println(fmt!("Usage: %s [options]", program));
    io::println("-o\t\tOutput");
    io::println("-h --help\tUsage");
}

// fn make_absolute(p: &Path) -> Path

fn get_license(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x.to_lower(),
        None => ~"bsd3"
    }
}

fn get_proj(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x,
        None => {
            match os::getcwd().filename() {
                Some(dir) => dir,
                None => ~"project"
            }
        }
    }
}

fn get_org(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x,
            None => {
                match os::getenv("USER") {
                    Some(dir) => dir,
                    None => ~"organization"
                }
            }
        }
}

fn get_year(args: Option<~str>) -> ~str {
    match args {
        Some(x) => x,
        None => {
            let time = now();
            time.strftime("%Y")
        }
    }
}

fn load_file_template(file: ~str) -> ~[u8] {
    let open_file = &io::file_reader(&Path(file));
    if result::is_err(open_file) {
        fail!(~"Cannot open license file");
    }
    let license = result::get(open_file);
    license.read_whole_stream()
}

fn main() {
    let args = os::args();

    let program = copy args[0];

    let opts = ~[
        optflag(~"h"),
        optflag(~"help"),
        optopt(~"year"),
        optopt(~"proj"),
        optopt(~"org"),
        optopt(~"license")
    ];
    let matches = match getopts(vec::tail(args), opts) {
        result::Ok(m) => { m }
        result::Err(f) => { fail!(fail_str(f)) }
    };
    if opt_present(&matches, "h") || opt_present(&matches, "help") {
        print_usage(program, opts);
        return;
    }
    let year = opt_maybe_str(&matches, ~"year");
    let proj = opt_maybe_str(&matches, ~"proj");
    let org = opt_maybe_str(&matches, ~"org");
    let license = opt_maybe_str(&matches, ~"license");

    // io::println(get_year(year));
    // io::println(get_proj(proj));
    // io::println(get_org(org));
    // io::println(get_license(license));

    let mut context: LinearMap<~str, ~str> = LinearMap::new::<~str, ~str>();
    context.insert(~"year", get_year(year));
    context.insert(~"proj", get_proj(proj));
    context.insert(~"org", get_org(org));
    context.insert(~"license", get_license(license));

    let template = load_file_template(~"template-" + *context.get(&~"license") + ~".txt");
    let template_str = str::from_bytes(template);
    io::println(template_str);
    // io::println(template);
    // let content = generate_license(template);
}<|endoftext|>


<commit_before>use std::borrow::Cow;

use rust_decimal::Decimal;

use ast::*;
use ir::{Number, ConversionRatio};
use unit_graph::UnitGraph;
use symbols::SymbolTable;
use canonical::{self, CanonicalUnit};

#[derive(Debug, Clone)]
pub enum DeclError<'a> {
    UndeclaredName {
        name: Ident<'a>,
        span: Span<'a>,
    },
    UndeclaredUnit {
        name: UnitName<'a>,
        span: Span<'a>,
    },
    DuplicateUnitDecl {
        name: UnitName<'a>,
        span: Span<'a>,
    },
    UnknownAttribute {
        name: Ident<'a>,
        span: Span<'a>,
    },
    DuplicateConst {
        name: Ident<'a>,
        span: Span<'a>,
    },
    UnsupportedConstExpr {
        expr: Expr<'a>,
        span: Span<'a>,
    },
    MismatchedUnit {
        expected: UnitExpr<'a>,
        found: UnitExpr<'a>,
        span: Span<'a>,
    },
    ConversionFailed(ConvertError, Span<'a>),
}

impl<'a> From<canonical::UndeclaredUnit<'a>> for DeclError<'a> {
    fn from(canonical::UndeclaredUnit {name, span}: canonical::UndeclaredUnit<'a>) -> Self {
        DeclError::UndeclaredUnit {name, span}
    }
}

#[derive(Debug, Clone)]
pub enum EvalError<'a> {
    UndeclaredName {
        name: Ident<'a>,
        span: Span<'a>,
    },
    UndeclaredUnit {
        name: UnitName<'a>,
        span: Span<'a>,
    },
    MismatchedUnit {
        expected: UnitExpr<'a>,
        found: UnitExpr<'a>,
        span: Span<'a>,
    },
    ConversionFailed(ConvertError, Span<'a>),
}

impl<'a> From<canonical::UndeclaredUnit<'a>> for EvalError<'a> {
    fn from(canonical::UndeclaredUnit {name, span}: canonical::UndeclaredUnit<'a>) -> Self {
        EvalError::UndeclaredUnit {name, span}
    }
}

#[derive(Debug, Clone)]
pub enum ConvertError {
}

#[derive(Debug, Clone, Default)]
pub struct Interpreter<'a> {
    units: UnitGraph<'a>,
    symbols: SymbolTable<'a>,
}

impl<'a> Interpreter<'a> {
    /// Load declarations into the global scope of the interpreter
    pub fn load_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        // We need to do this in several passes in order to satisfy the potential items each type
        // of declaration might need.

        // Macros need to be expanded before anything else
        let program = self.expand_macros(Cow::Borrowed(program))?;

        self.load_unit_decls(&program)?;
        // Constants need units so we can verify their declared unit if provided.
        self.load_const_decls(&program)?;
        // Conversions need both units and constants (+ const functions) so we can evaluate their
        // declared ratios
        self.load_conversion_decls(&program)?;

        // Functions will need the units loaded so we can verify that their parameter and return
        // units are declared. We also check the body of each function at this point to make sure
        // all unit conversions are mathematically valid so we need those loaded as well.
        self.load_fn_decls(&program)?;

        Ok(())
    }

    /// Expand any macros used in the program
    ///
    /// Any declared macros will be collected along the way and used as necessary while
    /// recursively expanding the macros
    fn expand_macros<'c>(&self, program: Cow<'c, Program<'a>>) -> Result<Cow<'c, Program<'a>>, DeclError<'a>> {
        // By using Cow here, we avoid an unnecessary clone if the program has no macros

        for decl in &program.decls {
            //TODO: Macro declarations will be processed here eventually

            if let Decl::MacroInvoke(_) = decl {
                unimplemented!();
            }
        }
        Ok(program)
    }

    /// Load declared units into the global unit graph of the interpreter
    fn load_unit_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            if let &Decl::UnitDecl(UnitDecl {ref attrs, unit_name, span, ..}) = decl {
                //TODO: insert the unit into the unit graph and add the single conversion from
                //unitless to the unit

                for &Attribute {name, tokens: _, span} in attrs {
                    match name {
                        "prefix" => {}, //TODO
                        _ => return Err(DeclError::UnknownAttribute {name, span}),
                    }
                }

                self.units.insert_unit(unit_name, span)
                    .map_err(|_| DeclError::DuplicateUnitDecl {name: unit_name, span})?;
            }
        }
        Ok(())
    }

    /// Load declared constants and `const fn`s into the global scope of the interpreter
    fn load_const_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            //TODO: const fn declarations will be processed and unit checked here eventually

            if let &Decl::Constant(Constant {name, unit: ref unit_expr, ref value, span}) = decl {
                let unit = CanonicalUnit::from_unit_expr(unit_expr, &self.units)?;
                let value = match value {
                    &Expr::Number(NumericLiteral {ref value, span: const_span}, ref const_unit) => {
                        // Units must match
                        if CanonicalUnit::from_unit_expr(const_unit, &self.units)? == unit {
                            *value
                        }
                        else {
                            return Err(DeclError::MismatchedUnit {
                                expected: unit_expr.clone(),
                                found: const_unit.clone(),
                                span: const_span,
                            })
                        }
                    },
                    _ => return Err(DeclError::UnsupportedConstExpr {expr: value.clone(), span}),
                };
                self.symbols.insert_const(name, value, unit, span).map_err(|_| {
                    DeclError::DuplicateConst {name, span}
                })?;
            }
        }
        Ok(())
    }

    /// Load unit conversions and evaluate any const exprs found within them
    fn load_conversion_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            match decl {
                // Aliases need to be processed later when all the units have already been walked
                Decl::UnitDecl(UnitDecl {unit_name, alias_for: Some(alias), ..}) => {
                    let unit_id = self.units.unit_id(*unit_name)
                        .expect("bug: unit should have already been declared");
                    let left_unit = CanonicalUnit::from(unit_id);
                    let right_unit = CanonicalUnit::from_unit_expr(alias, &self.units)?;

                    // Alias means: 1 left_unit == 1 right_unit
                    self.units.add_conversion(ConversionRatio {
                        left: Number {
                            value: Decimal::from(1),
                            unit: left_unit,
                        },
                        right: Number {
                            value: Decimal::from(1),
                            unit: right_unit,
                        },
                    })
                },
                Decl::ConversionDecl(ConversionDecl {left, right, ..}) => {
                    let left = self.reduce_const_expr(left)?;
                    let right = self.reduce_const_expr(right)?;
                    self.units.add_conversion(ConversionRatio {left, right});
                },
                _ => {},
            }
        }
        Ok(())
    }

    /// Load function declarations into the global scope and check their bodies to ensure that any
    /// conversions are valid
    fn load_fn_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            if let Decl::Function(_) = decl {
                unimplemented!();
            }
        }
        Ok(())
    }

    fn reduce_const_expr(&self, expr: &Expr<'a>) -> Result<Number, DeclError<'a>> {
        match expr {
            &Expr::Number(NumericLiteral {value, span: _}, ref unit) => {
                let unit = CanonicalUnit::from_unit_expr(unit, &self.units)?;
                Ok(Number {value, unit})
            },
            &Expr::Ident(ref path, span) => {
                if path.len() != 1 { unimplemented!() }
                let name = path.first().unwrap();
                self.symbols.get_const(name).cloned().ok_or_else(|| DeclError::UndeclaredName {name, span})
            },
            Expr::ConvertTo(expr, target_unit, span) => {
                let number = self.reduce_const_expr(expr)?;
                let target_unit = CanonicalUnit::from_unit_expr(target_unit, &self.units)?;
                self.convert(number, target_unit).map_err(|err| DeclError::ConversionFailed(err, *span))
            },
            Expr::Add(_, _, span) | Expr::Sub(_, _, span) | Expr::Mul(_, _, span) |
            Expr::Div(_, _, span) | Expr::Mod(_, _, span) | Expr::Pow(_, _, span) |
            Expr::Call(_, _, span) | Expr::MacroCall(MacroInvoke {span, ..}) |
            Expr::Return(_, span) | Expr::UnitValue(span) => {
                Err(DeclError::UnsupportedConstExpr {expr: expr.clone(), span: *span})
            },
            Expr::Block(_) => unimplemented!(),
        }
    }

    pub fn evaluate_expr(&self, expr: &Expr<'a>) -> Result<Number, EvalError<'a>> {
        Ok(match expr {
            &Expr::Number(NumericLiteral {value, span: _}, ref unit) => {
                let unit = CanonicalUnit::from_unit_expr(unit, &self.units)?;
                Number {value, unit}
            },
            &Expr::Ident(ref path, span) => {
                if path.len() != 1 { unimplemented!() }
                let name = path.first().unwrap();
                //TODO: Allow getting variables defined with `let` in the interpreter
                self.symbols.get_const(name).cloned().ok_or_else(|| EvalError::UndeclaredName {name, span})?
            },
            Expr::ConvertTo(expr, target_unit, span) => {
                let number = self.evaluate_expr(expr)?;
                let target_unit = CanonicalUnit::from_unit_expr(target_unit, &self.units)?;
                self.convert(number, target_unit).map_err(|err| EvalError::ConversionFailed(err, *span))?
            },
            _ => unimplemented!(),
        })
    }

    fn convert(&self, value: Number, target_unit: CanonicalUnit) -> Result<Number, ConvertError> {
        if value.unit == target_unit {
            // No conversion necessary
            Ok(value)
        }
        // Can convert from unitless to any unit
        else if value.unit.is_unitless() {
            Ok(Number {
                value: value.value,
                unit: target_unit,
            })
        }
        else {
            //TODO: Convert
            unimplemented!();
        }
    }
}
<commit_msg>Adding addition and subtraction and no longer implicitly promoting unitless to a unit<commit_after>use std::borrow::Cow;

use rust_decimal::Decimal;

use ast::*;
use ir::{Number, ConversionRatio};
use unit_graph::UnitGraph;
use symbols::SymbolTable;
use canonical::{self, CanonicalUnit};

#[derive(Debug, Clone)]
pub enum DeclError<'a> {
    UndeclaredName {
        name: Ident<'a>,
        span: Span<'a>,
    },
    UndeclaredUnit {
        name: UnitName<'a>,
        span: Span<'a>,
    },
    DuplicateUnitDecl {
        name: UnitName<'a>,
        span: Span<'a>,
    },
    UnknownAttribute {
        name: Ident<'a>,
        span: Span<'a>,
    },
    DuplicateConst {
        name: Ident<'a>,
        span: Span<'a>,
    },
    UnsupportedConstExpr {
        expr: Expr<'a>,
        span: Span<'a>,
    },
    MismatchedUnit {
        expected: UnitExpr<'a>,
        found: UnitExpr<'a>,
        span: Span<'a>,
    },
    ConversionFailed(ConvertError, Span<'a>),
}

impl<'a> From<canonical::UndeclaredUnit<'a>> for DeclError<'a> {
    fn from(canonical::UndeclaredUnit {name, span}: canonical::UndeclaredUnit<'a>) -> Self {
        DeclError::UndeclaredUnit {name, span}
    }
}

#[derive(Debug, Clone)]
pub enum EvalError<'a> {
    UndeclaredName {
        name: Ident<'a>,
        span: Span<'a>,
    },
    UndeclaredUnit {
        name: UnitName<'a>,
        span: Span<'a>,
    },
    MismatchedUnit {
        expected: UnitExpr<'a>,
        found: UnitExpr<'a>,
        span: Span<'a>,
    },
    ConversionFailed(ConvertError, Span<'a>),
}

impl<'a> From<canonical::UndeclaredUnit<'a>> for EvalError<'a> {
    fn from(canonical::UndeclaredUnit {name, span}: canonical::UndeclaredUnit<'a>) -> Self {
        EvalError::UndeclaredUnit {name, span}
    }
}

#[derive(Debug, Clone)]
pub enum ConvertError {
}

#[derive(Debug, Clone, Default)]
pub struct Interpreter<'a> {
    units: UnitGraph<'a>,
    symbols: SymbolTable<'a>,
}

impl<'a> Interpreter<'a> {
    /// Load declarations into the global scope of the interpreter
    pub fn load_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        // We need to do this in several passes in order to satisfy the potential items each type
        // of declaration might need.

        // Macros need to be expanded before anything else
        let program = self.expand_macros(Cow::Borrowed(program))?;

        self.load_unit_decls(&program)?;
        // Constants need units so we can verify their declared unit if provided.
        self.load_const_decls(&program)?;
        // Conversions need both units and constants (+ const functions) so we can evaluate their
        // declared ratios
        self.load_conversion_decls(&program)?;

        // Functions will need the units loaded so we can verify that their parameter and return
        // units are declared. We also check the body of each function at this point to make sure
        // all unit conversions are mathematically valid so we need those loaded as well.
        self.load_fn_decls(&program)?;

        Ok(())
    }

    /// Expand any macros used in the program
    ///
    /// Any declared macros will be collected along the way and used as necessary while
    /// recursively expanding the macros
    fn expand_macros<'c>(&self, program: Cow<'c, Program<'a>>) -> Result<Cow<'c, Program<'a>>, DeclError<'a>> {
        // By using Cow here, we avoid an unnecessary clone if the program has no macros

        for decl in &program.decls {
            //TODO: Macro declarations will be processed here eventually

            if let Decl::MacroInvoke(_) = decl {
                unimplemented!();
            }
        }
        Ok(program)
    }

    /// Load declared units into the global unit graph of the interpreter
    fn load_unit_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            if let &Decl::UnitDecl(UnitDecl {ref attrs, unit_name, span, ..}) = decl {
                //TODO: insert the unit into the unit graph and add the single conversion from
                //unitless to the unit

                for &Attribute {name, tokens: _, span} in attrs {
                    match name {
                        "prefix" => {}, //TODO
                        _ => return Err(DeclError::UnknownAttribute {name, span}),
                    }
                }

                self.units.insert_unit(unit_name, span)
                    .map_err(|_| DeclError::DuplicateUnitDecl {name: unit_name, span})?;
            }
        }
        Ok(())
    }

    /// Load declared constants and `const fn`s into the global scope of the interpreter
    fn load_const_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            //TODO: const fn declarations will be processed and unit checked here eventually

            if let &Decl::Constant(Constant {name, unit: ref unit_expr, ref value, span}) = decl {
                let unit = CanonicalUnit::from_unit_expr(unit_expr, &self.units)?;
                let value = match value {
                    &Expr::Number(NumericLiteral {ref value, span: const_span}, ref const_unit) => {
                        // Units must match
                        if CanonicalUnit::from_unit_expr(const_unit, &self.units)? == unit {
                            *value
                        }
                        else {
                            return Err(DeclError::MismatchedUnit {
                                expected: unit_expr.clone(),
                                found: const_unit.clone(),
                                span: const_span,
                            })
                        }
                    },
                    _ => return Err(DeclError::UnsupportedConstExpr {expr: value.clone(), span}),
                };
                self.symbols.insert_const(name, value, unit, span).map_err(|_| {
                    DeclError::DuplicateConst {name, span}
                })?;
            }
        }
        Ok(())
    }

    /// Load unit conversions and evaluate any const exprs found within them
    fn load_conversion_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            match decl {
                // Aliases need to be processed later when all the units have already been walked
                Decl::UnitDecl(UnitDecl {unit_name, alias_for: Some(alias), ..}) => {
                    let unit_id = self.units.unit_id(*unit_name)
                        .expect("bug: unit should have already been declared");
                    let left_unit = CanonicalUnit::from(unit_id);
                    let right_unit = CanonicalUnit::from_unit_expr(alias, &self.units)?;

                    // Alias means: 1 left_unit == 1 right_unit
                    self.units.add_conversion(ConversionRatio {
                        left: Number {
                            value: Decimal::from(1),
                            unit: left_unit,
                        },
                        right: Number {
                            value: Decimal::from(1),
                            unit: right_unit,
                        },
                    })
                },
                Decl::ConversionDecl(ConversionDecl {left, right, ..}) => {
                    let left = self.reduce_const_expr(left)?;
                    let right = self.reduce_const_expr(right)?;
                    self.units.add_conversion(ConversionRatio {left, right});
                },
                _ => {},
            }
        }
        Ok(())
    }

    /// Load function declarations into the global scope and check their bodies to ensure that any
    /// conversions are valid
    fn load_fn_decls(&mut self, program: &Program<'a>) -> Result<(), DeclError<'a>> {
        for decl in &program.decls {
            if let Decl::Function(_) = decl {
                unimplemented!();
            }
        }
        Ok(())
    }

    fn reduce_const_expr(&self, expr: &Expr<'a>) -> Result<Number, DeclError<'a>> {
        match expr {
            &Expr::Number(NumericLiteral {value, span: _}, ref unit) => {
                let unit = CanonicalUnit::from_unit_expr(unit, &self.units)?;
                Ok(Number {value, unit})
            },
            &Expr::Ident(ref path, span) => {
                if path.len() != 1 { unimplemented!() }
                let name = path.first().unwrap();
                self.symbols.get_const(name).cloned().ok_or_else(|| DeclError::UndeclaredName {name, span})
            },
            Expr::ConvertTo(expr, target_unit, span) => {
                let number = self.reduce_const_expr(expr)?;
                let target_unit = CanonicalUnit::from_unit_expr(target_unit, &self.units)?;
                if number.unit.is_unitless() {
                    Ok(Number {
                        value: number.value,
                        unit: target_unit,
                    })
                }
                else {
                    self.convert(number, target_unit)
                        .map_err(|err| DeclError::ConversionFailed(err, *span))
                }
            },
            Expr::Add(_, _, span) | Expr::Sub(_, _, span) | Expr::Mul(_, _, span) |
            Expr::Div(_, _, span) | Expr::Mod(_, _, span) | Expr::Pow(_, _, span) |
            Expr::Call(_, _, span) | Expr::MacroCall(MacroInvoke {span, ..}) |
            Expr::Return(_, span) | Expr::UnitValue(span) => {
                Err(DeclError::UnsupportedConstExpr {expr: expr.clone(), span: *span})
            },
            Expr::Block(_) => unimplemented!(),
        }
    }

    pub fn evaluate_expr(&self, expr: &Expr<'a>) -> Result<Number, EvalError<'a>> {
        Ok(match expr {
            &Expr::Number(NumericLiteral {value, span: _}, ref unit) => {
                let unit = CanonicalUnit::from_unit_expr(unit, &self.units)?;
                Number {value, unit}
            },
            &Expr::Ident(ref path, span) => {
                if path.len() != 1 { unimplemented!() }
                let name = path.first().unwrap();
                //TODO: Allow getting variables defined with `let` in the interpreter
                self.symbols.get_const(name).cloned().ok_or_else(|| EvalError::UndeclaredName {name, span})?
            },
            Expr::ConvertTo(expr, target_unit, span) => {
                let number = self.evaluate_expr(expr)?;
                let target_unit = CanonicalUnit::from_unit_expr(target_unit, &self.units)?;
                if number.unit.is_unitless() {
                    Number {
                        value: number.value,
                        unit: target_unit,
                    }
                }
                else {
                    self.convert(number, target_unit)
                        .map_err(|err| EvalError::ConversionFailed(err, *span))?
                }
            },
            Expr::Add(lhs, rhs, span) => {
                let lhs = self.evaluate_expr(lhs)?;
                let rhs = self.convert(self.evaluate_expr(rhs)?, lhs.unit.clone())
                    .map_err(|err| EvalError::ConversionFailed(err, *span))?;
                Number {
                    value: lhs.value + rhs.value,
                    unit: lhs.unit,
                }
            },
            Expr::Sub(lhs, rhs, span) => {
                let lhs = self.evaluate_expr(lhs)?;
                let rhs = self.convert(self.evaluate_expr(rhs)?, lhs.unit.clone())
                    .map_err(|err| EvalError::ConversionFailed(err, *span))?;
                Number {
                    value: lhs.value - rhs.value,
                    unit: lhs.unit,
                }
            },
            _ => unimplemented!(),
        })
    }

    /// Attempts to convert the given value to the given unit
    ///
    /// Does not automatically promote unitless to the target unit as that is not valid in all cases
    fn convert(&self, value: Number, target_unit: CanonicalUnit) -> Result<Number, ConvertError> {
        if value.unit == target_unit {
            // No conversion necessary
            Ok(value)
        }
        else {
            //TODO: Convert
            unimplemented!();
        }
    }
}
<|endoftext|>


<commit_before>/**
 * 15.3 Do syntax
 * http://static.rust-lang.org/doc/master/tutorial.html#do-syntax
 *
 * @license MIT license <http://www.opensource.org/licenses/mit-license.php>
 */
// The do expression makes it easier to call functions that take procedures as arguments.

// A function that takes a procedure as argument:
fn call_it(op: proc(v: int)) {
	    op(10)
}

fn main() {
	// As a caller, if we use a closure to provide the final operator argument, we can write it in a way that has a pleasant, block-like structure.
	call_it(proc(n) {
		    println(n.to_str());
	});

	// This is such a useful pattern that Rust has a special form of function call for these functions.
	do call_it() |n| {
		    println(n.to_str());
	}

}
<commit_msg>tutorial-15_3-do.rs : add each example<commit_after>/**
 * 15.3 Do syntax
 * http://static.rust-lang.org/doc/master/tutorial.html#do-syntax
 *
 * @license MIT license <http://www.opensource.org/licenses/mit-license.php>
 */
// The do expression makes it easier to call functions that take procedures as arguments.

// A function that takes a procedure as argument:
fn call_it(op: proc(v: int)) {
	op(10)
}

fn each(v: &[int], op: |v: &int|) {
	let mut n = 0;
	while n < v.len() {
		op(&v[n]);
		n += 1;
	}
}

fn main() {
	// As a caller, if we use a closure to provide the final operator argument, we can write it in a way that has a pleasant, block-like structure.
	call_it(proc(n) {
		println(n.to_str());
	});
	each([1, 2, 3], |n: &int| {
		println(n.to_str());
	});

	// This is such a useful pattern that Rust has a special form of function call for these functions.
	do call_it() |n| {
		println(n.to_str());
	}
}
<|endoftext|>


<commit_before>use latch::{LatchProbe, SpinLatch};
use log::Event::*;
use job::StackJob;
use registry::{self, WorkerThread};
use std::any::Any;
use unwind;

use FnContext;

#[cfg(test)]
mod test;

/// Takes two closures and *potentially* runs them in parallel. It
/// returns a pair of the results from those closures.
///
/// Conceptually, calling `join()` is similar to spawning two threads,
/// one executing each of the two closures. However, the
/// implementation is quite different and incurs very low
/// overhead. The underlying technique is called "work stealing": the
/// Rayon runtime uses a fixed pool of worker threads and attempts to
/// only execute code in parallel when there are idle CPUs to handle
/// it.
///
/// When `join` is called from outside the thread pool, the calling
/// thread will block while the closures execute in the pool.  When
/// `join` is called within the pool, the calling thread still actively
/// participates in the thread pool. It will begin by executing closure
/// A (on the current thread). While it is doing that, it will advertise
/// closure B as being available for other threads to execute. Once closure A
/// has completed, the current thread will try to execute closure B;
/// if however closure B has been stolen, then it will look for other work
/// while waiting for the thief to fully execute closure B. (This is the
/// typical work-stealing strategy).
///
/// # Examples
///
/// This example uses join to perform a quick-sort (note this is not a
/// particularly optimized implementation: if you **actually** want to
/// sort for real, you should prefer [the `par_sort` method] offered
/// by Rayon).
///
/// [the `par_sort` method]: ../slice/trait.ParallelSliceMut.html#method.par_sort
///
/// ```rust
/// # use rayon_core as rayon;
/// let mut v = vec![5, 1, 8, 22, 0, 44];
/// quick_sort(&mut v);
/// assert_eq!(v, vec![0, 1, 5, 8, 22, 44]);
///
/// fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
///    if v.len() > 1 {
///        let mid = partition(v);
///        let (lo, hi) = v.split_at_mut(mid);
///        rayon::join(|| quick_sort(lo),
///                    || quick_sort(hi));
///    }
/// }
///
/// // Partition rearranges all items `<=` to the pivot
/// // item (arbitrary selected to be the last item in the slice)
/// // to the first half of the slice. It then returns the
/// // "dividing point" where the pivot is placed.
/// fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
///     let pivot = v.len() - 1;
///     let mut i = 0;
///     for j in 0..pivot {
///         if v[j] <= v[pivot] {
///             v.swap(i, j);
///             i += 1;
///         }
///     }
///     v.swap(i, pivot);
///     i
/// }
/// ```
///
/// # Warning about blocking I/O
///
/// The assumption is that the closures given to `join()` are
/// CPU-bound tasks that do not perform I/O or other blocking
/// operations. If you do perform I/O, and that I/O should block
/// (e.g., waiting for a network request), the overall performance may
/// be poor.  Moreover, if you cause one closure to be blocked waiting
/// on another (for example, using a channel), that could lead to a
/// deadlock.
///
/// # Panics
///
/// No matter what happens, both closures will always be executed.  If
/// a single closure panics, whether it be the first or second
/// closure, that panic will be propagated and hence `join()` will
/// panic with the same panic value. If both closures panic, `join()`
/// will panic with the panic value from the first closure.
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce() -> RA + Send,
          B: FnOnce() -> RB + Send,
          RA: Send,
          RB: Send
{
    join_context(|_| oper_a(), |_| oper_b())
}

/// Identical to `join`, except that the closures have a parameter
/// that provides context for the way the closure has been called,
/// especially indicating whether they're executing on a different
/// thread than where `join_context` was called.  This will occur if
/// the second job is stolen by a different thread, or if
/// `join_context` was called from outside the thread pool to begin
/// with.
pub fn join_context<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce(FnContext) -> RA + Send,
          B: FnOnce(FnContext) -> RB + Send,
          RA: Send,
          RB: Send
{
    registry::in_worker(|worker_thread, injected| unsafe {
        log!(Join { worker: worker_thread.index() });

        // Create virtual wrapper for task b; this all has to be
        // done here so that the stack frame can keep it all live
        // long enough.
        let job_b = StackJob::new(|migrated| oper_b(FnContext::new(migrated)),
                                  SpinLatch::new());
        let job_b_ref = job_b.as_job_ref();
        worker_thread.push(job_b_ref);

        // Execute task a; hopefully b gets stolen in the meantime.
        let status_a = unwind::halt_unwinding(move || oper_a(FnContext::new(injected)));
        let result_a = match status_a {
            Ok(v) => v,
            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err),
        };

        // Now that task A has finished, try to pop job B from the
        // local stack.  It may already have been popped by job A; it
        // may also have been stolen. There may also be some tasks
        // pushed on top of it in the stack, and we will have to pop
        // those off to get to it.
        while !job_b.latch.probe() {
            if let Some(job) = worker_thread.take_local_job() {
                if job == job_b_ref {
                    // Found it! Let's run it.
                    //
                    // Note that this could panic, but it's ok if we unwind here.
                    log!(PoppedRhs { worker: worker_thread.index() });
                    let result_b = job_b.run_inline(injected);
                    return (result_a, result_b);
                } else {
                    log!(PoppedJob { worker: worker_thread.index() });
                    worker_thread.execute(job);
                }
            } else {
                // Local deque is empty. Time to steal from other
                // threads.
                log!(LostJob { worker: worker_thread.index() });
                worker_thread.wait_until(&job_b.latch);
                debug_assert!(job_b.latch.probe());
                break;
            }
        }

        return (result_a, job_b.into_result());
    })
}

/// If job A panics, we still cannot return until we are sure that job
/// B is complete. This is because it may contain references into the
/// enclosing stack frame(s).
#[cold] // cold path
unsafe fn join_recover_from_panic(worker_thread: &WorkerThread,
                                  job_b_latch: &SpinLatch,
                                  err: Box<Any + Send>)
                                  -> !
{
    worker_thread.wait_until(job_b_latch);
    unwind::resume_unwinding(err)
}
<commit_msg>link to par_sort across multiple crates<commit_after>use latch::{LatchProbe, SpinLatch};
use log::Event::*;
use job::StackJob;
use registry::{self, WorkerThread};
use std::any::Any;
use unwind;

use FnContext;

#[cfg(test)]
mod test;

/// Takes two closures and *potentially* runs them in parallel. It
/// returns a pair of the results from those closures.
///
/// Conceptually, calling `join()` is similar to spawning two threads,
/// one executing each of the two closures. However, the
/// implementation is quite different and incurs very low
/// overhead. The underlying technique is called "work stealing": the
/// Rayon runtime uses a fixed pool of worker threads and attempts to
/// only execute code in parallel when there are idle CPUs to handle
/// it.
///
/// When `join` is called from outside the thread pool, the calling
/// thread will block while the closures execute in the pool.  When
/// `join` is called within the pool, the calling thread still actively
/// participates in the thread pool. It will begin by executing closure
/// A (on the current thread). While it is doing that, it will advertise
/// closure B as being available for other threads to execute. Once closure A
/// has completed, the current thread will try to execute closure B;
/// if however closure B has been stolen, then it will look for other work
/// while waiting for the thief to fully execute closure B. (This is the
/// typical work-stealing strategy).
///
/// # Examples
///
/// This example uses join to perform a quick-sort (note this is not a
/// particularly optimized implementation: if you **actually** want to
/// sort for real, you should prefer [the `par_sort` method] offered
/// by Rayon).
///
/// [the `par_sort` method]: ../rayon/slice/trait.ParallelSliceMut.html#method.par_sort
///
/// ```rust
/// # use rayon_core as rayon;
/// let mut v = vec![5, 1, 8, 22, 0, 44];
/// quick_sort(&mut v);
/// assert_eq!(v, vec![0, 1, 5, 8, 22, 44]);
///
/// fn quick_sort<T:PartialOrd+Send>(v: &mut [T]) {
///    if v.len() > 1 {
///        let mid = partition(v);
///        let (lo, hi) = v.split_at_mut(mid);
///        rayon::join(|| quick_sort(lo),
///                    || quick_sort(hi));
///    }
/// }
///
/// // Partition rearranges all items `<=` to the pivot
/// // item (arbitrary selected to be the last item in the slice)
/// // to the first half of the slice. It then returns the
/// // "dividing point" where the pivot is placed.
/// fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
///     let pivot = v.len() - 1;
///     let mut i = 0;
///     for j in 0..pivot {
///         if v[j] <= v[pivot] {
///             v.swap(i, j);
///             i += 1;
///         }
///     }
///     v.swap(i, pivot);
///     i
/// }
/// ```
///
/// # Warning about blocking I/O
///
/// The assumption is that the closures given to `join()` are
/// CPU-bound tasks that do not perform I/O or other blocking
/// operations. If you do perform I/O, and that I/O should block
/// (e.g., waiting for a network request), the overall performance may
/// be poor.  Moreover, if you cause one closure to be blocked waiting
/// on another (for example, using a channel), that could lead to a
/// deadlock.
///
/// # Panics
///
/// No matter what happens, both closures will always be executed.  If
/// a single closure panics, whether it be the first or second
/// closure, that panic will be propagated and hence `join()` will
/// panic with the same panic value. If both closures panic, `join()`
/// will panic with the panic value from the first closure.
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce() -> RA + Send,
          B: FnOnce() -> RB + Send,
          RA: Send,
          RB: Send
{
    join_context(|_| oper_a(), |_| oper_b())
}

/// Identical to `join`, except that the closures have a parameter
/// that provides context for the way the closure has been called,
/// especially indicating whether they're executing on a different
/// thread than where `join_context` was called.  This will occur if
/// the second job is stolen by a different thread, or if
/// `join_context` was called from outside the thread pool to begin
/// with.
pub fn join_context<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce(FnContext) -> RA + Send,
          B: FnOnce(FnContext) -> RB + Send,
          RA: Send,
          RB: Send
{
    registry::in_worker(|worker_thread, injected| unsafe {
        log!(Join { worker: worker_thread.index() });

        // Create virtual wrapper for task b; this all has to be
        // done here so that the stack frame can keep it all live
        // long enough.
        let job_b = StackJob::new(|migrated| oper_b(FnContext::new(migrated)),
                                  SpinLatch::new());
        let job_b_ref = job_b.as_job_ref();
        worker_thread.push(job_b_ref);

        // Execute task a; hopefully b gets stolen in the meantime.
        let status_a = unwind::halt_unwinding(move || oper_a(FnContext::new(injected)));
        let result_a = match status_a {
            Ok(v) => v,
            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err),
        };

        // Now that task A has finished, try to pop job B from the
        // local stack.  It may already have been popped by job A; it
        // may also have been stolen. There may also be some tasks
        // pushed on top of it in the stack, and we will have to pop
        // those off to get to it.
        while !job_b.latch.probe() {
            if let Some(job) = worker_thread.take_local_job() {
                if job == job_b_ref {
                    // Found it! Let's run it.
                    //
                    // Note that this could panic, but it's ok if we unwind here.
                    log!(PoppedRhs { worker: worker_thread.index() });
                    let result_b = job_b.run_inline(injected);
                    return (result_a, result_b);
                } else {
                    log!(PoppedJob { worker: worker_thread.index() });
                    worker_thread.execute(job);
                }
            } else {
                // Local deque is empty. Time to steal from other
                // threads.
                log!(LostJob { worker: worker_thread.index() });
                worker_thread.wait_until(&job_b.latch);
                debug_assert!(job_b.latch.probe());
                break;
            }
        }

        return (result_a, job_b.into_result());
    })
}

/// If job A panics, we still cannot return until we are sure that job
/// B is complete. This is because it may contain references into the
/// enclosing stack frame(s).
#[cold] // cold path
unsafe fn join_recover_from_panic(worker_thread: &WorkerThread,
                                  job_b_latch: &SpinLatch,
                                  err: Box<Any + Send>)
                                  -> !
{
    worker_thread.wait_until(job_b_latch);
    unwind::resume_unwinding(err)
}
<|endoftext|>


<commit_before>use num::Complex;

pub fn butterfly_2(input: &[Complex<f32>],
               input_stride: usize,
               output: &mut [Complex<f32>],
               output_stride: usize,
               twiddles: &[Complex<f32>],
               num_rows: usize) {
    let mut out_idx_1 = 0us;
    let mut out_idx_2 = num_rows * output_stride;
    let mut twiddle_idx = 0us;
    // I think the chunks method might be slower than necessary...
    for row in input.chunks(2 * input_stride) {
        unsafe {
            let twiddle = twiddles.get_unchecked(twiddle_idx);
            *output.get_unchecked_mut(out_idx_1) =
                row.get_unchecked(0) + row.get_unchecked(input_stride) * twiddle;
            *output.get_unchecked_mut(out_idx_2) =
                row.get_unchecked(0) - row.get_unchecked(input_stride) * twiddle;
        }
        out_idx_1 += output_stride;
        out_idx_2 += output_stride;
        twiddle_idx += input_stride;
    }
}
<commit_msg>Changed from 'chunks' to explicit pointer incrementing in butterfly_2<commit_after>use num::Complex;

pub fn butterfly_2(input: &[Complex<f32>],
               input_stride: usize,
               output: &mut [Complex<f32>],
               output_stride: usize,
               twiddles: &[Complex<f32>],
               num_rows: usize) {
    let mut out_idx_1 = 0us;
    let mut out_idx_2 = num_rows * output_stride;
    let mut twiddle_idx = 0us;
    let mut row = input.as_ptr();
    let input_stride = input_stride as isize;
    for _ in range(0, num_rows) {
        unsafe {
            let twiddle = twiddles.get_unchecked(twiddle_idx);
            *output.get_unchecked_mut(out_idx_1) =
                *row.offset(0) + *row.offset(input_stride) * twiddle;
            *output.get_unchecked_mut(out_idx_2) =
                *row.offset(0) - *row.offset(input_stride) * twiddle;
        }
        out_idx_1 += output_stride;
        out_idx_2 += output_stride;
        twiddle_idx += input_stride as usize;
        row = unsafe { row.offset(2 * input_stride) };
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::io::Result as IoResult;
use std::result::Result;
use std::iter::{Iterator, IntoIterator};
use std::collections::HashMap;
use std::collections::hash_map;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

pub type Pixels = usize;
pub type FileName = String;
pub type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
pub enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

#[derive(Clone, Eq, PartialEq, Hash)]
pub enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Clone, Eq, PartialEq, Hash)]
pub enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    x_pixels: Pixels,
    y_pixels: Pixels,
}

impl ImageDimensions {
    fn new(x: Pixels, y: Pixels) -> ImageDimensions {
        ImageDimensions {
            x_pixels: x,
            y_pixels: y,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash)]
pub struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
pub enum PageOps {
    Identify(FileName, FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

pub trait ElementaryPageOperations {
    fn identify(file_name: FileName, path: FilePath) -> Self;
    fn rescale(amount: Pixels, dir: Direction)       -> Self;
    fn expand_left_edge(amount: Pixels)              -> Self;
    fn expand_right_edge(amount: Pixels)             -> Self;
    fn expand_top_edge(amount: Pixels)               -> Self;
    fn expand_bottom_edge(amount: Pixels)            -> Self;
    fn trim_left_edge(amount: Pixels)                -> Self;
    fn trim_right_edge(amount: Pixels)               -> Self;
    fn trim_top_edge(amount: Pixels)                 -> Self;
    fn trim_bottom_edge(amount: Pixels)              -> Self;
    fn set_resolution(res: ImageResolution)          -> Self;
}

trait RunOperation {
    fn run_operation(op: Self) -> OperationResults;
}

trait GenerateOperation<OpType, Op> where Op: ElementaryPageOperations {
    fn generate_operation(op: OpType) -> Op;
}

impl<Op> GenerateOperation<PageOps, Op> for Op where Op: ElementaryPageOperations {
    fn generate_operation(op: PageOps) -> Op {
        match op {
            PageOps::Identify(file, path)     => Op::identify(file, path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Option<Vec<PageOps>>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {

        if ops.is_empty() {
            return CompoundPageOperation {
                page_name: page_name,
                page_path: page_path,
                ops: None,
            };
        }

        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: Some(vec),
        }
    }

    fn make_noop() -> CompoundPageOperation {
        CompoundPageOperation {
            page_name: String::from(""),
            page_path: String::from(""),
            ops: None,
        }
    }

    fn is_noop(&self) -> bool {
        match self.ops {
            Some(ref vec) => vec.is_empty(),
            None          => true,
        }
    }

    fn run_operation<Op>(&self) -> OperationResults
        where Op: ElementaryPageOperations + RunOperation + GenerateOperation<PageOps, Op> {

        match self.ops {
            None      => {
                let mut res = Vec::new();
                res.push(Ok(String::from("No Operation")));
                OperationResults::from_vec(&mut res)
            }
            Some(ref vec) => {
                if self.is_noop() {
                    // Should not happen.
                    unreachable!();
                    //return Ok(String::from("No Operation"));
                }

                let mut result = Ok(String::from(""));
                for op in vec.iter() {
                    let op_results = Op::run_operation(Op::generate_operation(op.clone()));
                    for res in op_results.results {
                        match res {
                            Ok(s) => {
                                continue;
                            }
                            Err(e) => {
                                result = Err(e);
                                break;
                            }
                        }
                    }
                }
                let mut results = Vec::new();
                results.push(result);
                OperationResults::from_vec(&mut results)
            }
        }
    }

}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
            file_name: FileName,
            file_extension: ImageFileFormat,
            file_path: FilePath,
            dimensions: ImageDimensions,
            resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H) where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

type OperationResult = IoResult<String>;

struct OperationResults {
    results: Vec<OperationResult>,
}

impl OperationResults {
    fn new() -> OperationResults {
        OperationResults {
            results: Vec::new(),
        }
    }

    fn from_vec(vec: &mut Vec<OperationResult>) -> OperationResults {
        let mut results = Vec::new();
        results.append(vec);

        OperationResults {
            results: results,
        }
    }

    fn push(&mut self, result: OperationResult) {
        self.results.push(result);
    }

    fn append(&mut self, other: &mut OperationResults) {
        self.results.append(&mut other.results);
    }

}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

#[derive(Clone, Eq, PartialEq)]
enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> OperationSchedule {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_operation(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_operation(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn iter(&self) -> OpSchedIter {
        OpSchedIter {
            inner: self.schedule.iter()
        }
    }

    fn run_operation<Op>(&self) -> OperationResults
        where Op: ElementaryPageOperations + RunOperation + GenerateOperation<PageOps, Op> {

        let mut results = OperationResults::new();

        for (page, op) in self {

            match op.ops {
                None => {
                    continue;
                }
                Some(ref operations) => {
                    for elem_op in operations {
                        let compiled_op = Op::generate_operation(elem_op.clone());
                        let mut result = Op::run_operation(compiled_op);
                        results.append(&mut result);
                    }
                }
            //let compiled_op = Op::generate_operation(op);
            //let result = compiled_op.run_operation::<Op>();
            //results.append(result);
            }

        }

        results
    }
}

struct OpSchedIter<'a> {
    inner:  hash_map::Iter<'a, Page, CompoundPageOperation>
}

impl<'a> Iterator for OpSchedIter<'a> {
    type Item = (&'a Page, &'a CompoundPageOperation);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

}

impl<'a> IntoIterator for &'a OperationSchedule {
    type Item = (&'a Page, &'a CompoundPageOperation);
    type IntoIter = OpSchedIter<'a>;

    fn into_iter(self) -> OpSchedIter<'a> {
        self.iter()
    }
}

struct OpSchedIntoIter {
    inner: hash_map::IntoIter<Page, CompoundPageOperation>,
}

impl IntoIterator for OperationSchedule {
    type Item = (Page, CompoundPageOperation);
    type IntoIter = OpSchedIntoIter;

    fn into_iter(self) -> OpSchedIntoIter {
        OpSchedIntoIter {
            inner: self.schedule.into_iter()
        }
    }
}

impl Iterator for OpSchedIntoIter {
    type Item = (Page, CompoundPageOperation);

    fn next(&mut self) -> Option<(Page, CompoundPageOperation)> {
        self.inner.next()
    }
}
<commit_msg>Rename the trait GenerateOperation to CompileOperation.<commit_after>#![allow(dead_code)]
use std::io::Result as IoResult;
use std::result::Result;
use std::iter::{Iterator, IntoIterator};
use std::collections::HashMap;
use std::collections::hash_map;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

pub type Pixels = usize;
pub type FileName = String;
pub type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
pub enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

#[derive(Clone, Eq, PartialEq, Hash)]
pub enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Clone, Eq, PartialEq, Hash)]
pub enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    x_pixels: Pixels,
    y_pixels: Pixels,
}

impl ImageDimensions {
    fn new(x: Pixels, y: Pixels) -> ImageDimensions {
        ImageDimensions {
            x_pixels: x,
            y_pixels: y,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash)]
pub struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
pub enum PageOps {
    Identify(FileName, FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

pub trait ElementaryPageOperations {
    fn identify(file_name: FileName, path: FilePath) -> Self;
    fn rescale(amount: Pixels, dir: Direction)       -> Self;
    fn expand_left_edge(amount: Pixels)              -> Self;
    fn expand_right_edge(amount: Pixels)             -> Self;
    fn expand_top_edge(amount: Pixels)               -> Self;
    fn expand_bottom_edge(amount: Pixels)            -> Self;
    fn trim_left_edge(amount: Pixels)                -> Self;
    fn trim_right_edge(amount: Pixels)               -> Self;
    fn trim_top_edge(amount: Pixels)                 -> Self;
    fn trim_bottom_edge(amount: Pixels)              -> Self;
    fn set_resolution(res: ImageResolution)          -> Self;
}

trait RunOperation {
    fn run_operation(op: Self) -> OperationResults;
}

trait CompileOperation<OpType, Op> where Op: ElementaryPageOperations {
    fn compile_operation(op: OpType) -> Op;
}

impl<Op> CompileOperation<PageOps, Op> for Op where Op: ElementaryPageOperations {
    fn compile_operation(op: PageOps) -> Op {
        match op {
            PageOps::Identify(file, path)     => Op::identify(file, path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Option<Vec<PageOps>>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {

        if ops.is_empty() {
            return CompoundPageOperation {
                page_name: page_name,
                page_path: page_path,
                ops: None,
            };
        }

        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: Some(vec),
        }
    }

    fn make_noop() -> CompoundPageOperation {
        CompoundPageOperation {
            page_name: String::from(""),
            page_path: String::from(""),
            ops: None,
        }
    }

    fn is_noop(&self) -> bool {
        match self.ops {
            Some(ref vec) => vec.is_empty(),
            None          => true,
        }
    }

    fn run_operation<Op>(&self) -> OperationResults
        where Op: ElementaryPageOperations + RunOperation + CompileOperation<PageOps, Op> {

        match self.ops {
            None      => {
                let mut res = Vec::new();
                res.push(Ok(String::from("No Operation")));
                OperationResults::from_vec(&mut res)
            }
            Some(ref vec) => {
                if self.is_noop() {
                    // Should not happen.
                    unreachable!();
                    //return Ok(String::from("No Operation"));
                }

                let mut result = Ok(String::from(""));
                for op in vec.iter() {
                    let op_results = Op::run_operation(Op::compile_operation(op.clone()));
                    for res in op_results.results {
                        match res {
                            Ok(s) => {
                                continue;
                            }
                            Err(e) => {
                                // Fail fast if there is an error.
                                result = Err(e);
                                break;
                            }
                        }
                    }
                }
                let mut results = Vec::new();
                results.push(result);
                OperationResults::from_vec(&mut results)
            }
        }
    }

}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
            file_name: FileName,
            file_extension: ImageFileFormat,
            file_path: FilePath,
            dimensions: ImageDimensions,
            resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H) where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

type OperationResult = IoResult<String>;

struct OperationResults {
    results: Vec<OperationResult>,
}

impl OperationResults {
    fn new() -> OperationResults {
        OperationResults {
            results: Vec::new(),
        }
    }

    fn from_vec(vec: &mut Vec<OperationResult>) -> OperationResults {
        let mut results = Vec::new();
        results.append(vec);

        OperationResults {
            results: results,
        }
    }

    fn push(&mut self, result: OperationResult) {
        self.results.push(result);
    }

    fn append(&mut self, other: &mut OperationResults) {
        self.results.append(&mut other.results);
    }

}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

#[derive(Clone, Eq, PartialEq)]
enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> OperationSchedule {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_operation(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_operation(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn iter(&self) -> OpSchedIter {
        OpSchedIter {
            inner: self.schedule.iter()
        }
    }

    fn run_operation<Op>(&self) -> OperationResults
        where Op: ElementaryPageOperations + RunOperation + CompileOperation<PageOps, Op> {

        let mut results = OperationResults::new();

        for (page, op) in self {

            match op.ops {
                None => {
                    continue;
                }
                Some(ref operations) => {
                    for elem_op in operations {
                        let compiled_op = Op::compile_operation(elem_op.clone());
                        let mut result = Op::run_operation(compiled_op);
                        results.append(&mut result);
                    }
                }
            }

        }

        results
    }
}

struct OpSchedIter<'a> {
    inner:  hash_map::Iter<'a, Page, CompoundPageOperation>
}

impl<'a> Iterator for OpSchedIter<'a> {
    type Item = (&'a Page, &'a CompoundPageOperation);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

}

impl<'a> IntoIterator for &'a OperationSchedule {
    type Item = (&'a Page, &'a CompoundPageOperation);
    type IntoIter = OpSchedIter<'a>;

    fn into_iter(self) -> OpSchedIter<'a> {
        self.iter()
    }
}

struct OpSchedIntoIter {
    inner: hash_map::IntoIter<Page, CompoundPageOperation>,
}

impl IntoIterator for OperationSchedule {
    type Item = (Page, CompoundPageOperation);
    type IntoIter = OpSchedIntoIter;

    fn into_iter(self) -> OpSchedIntoIter {
        OpSchedIntoIter {
            inner: self.schedule.into_iter()
        }
    }
}

impl Iterator for OpSchedIntoIter {
    type Item = (Page, CompoundPageOperation);

    fn next(&mut self) -> Option<(Page, CompoundPageOperation)> {
        self.inner.next()
    }
}
<|endoftext|>


<commit_before><commit_msg>chore: Remove leftover dbg!<commit_after><|endoftext|>


<commit_before><commit_msg>Documented function of `delete_line` is more like `clr_eol`<commit_after><|endoftext|>


<commit_before><commit_msg>du: refactor pattern building in du<commit_after><|endoftext|>


<commit_before><commit_msg>Added basic test to v1::animation<commit_after><|endoftext|>


<commit_before>/*
Module: rand

Random number generation
*/
#[abi = "cdecl"]
native mod rustrt {
    type rctx;
    fn rand_new() -> rctx;
    fn rand_next(c: rctx) -> u32;
    fn rand_free(c: rctx);
}

/* Section: Types */

/*
Obj: rng

A random number generator
*/
type rng = obj {
    /*
    Method: next

    Return the next random integer
    */
    fn next() -> u32;

    /*
    Method: next_float

    Return the next random float
    */
    fn next_float() -> float;

    /*
    Method: gen_str

    Return a random string composed of A-Z, a-z, 0-9.
    */
    fn gen_str(len: uint) -> str;

    /*
    Method: gen_bytes

    Return a random byte string.
    */
    fn gen_bytes(len: uint) -> [u8];
};

resource rand_res(c: rustrt::rctx) { rustrt::rand_free(c); }

/* Section: Operations */

/*
Function: mk_rng

Create a random number generator
*/
fn mk_rng() -> rng {
    obj rt_rng(c: @rand_res) {
        fn next() -> u32 { ret rustrt::rand_next(**c); }
        fn next_float() -> float {
          let u1 = rustrt::rand_next(**c) as float;
          let u2 = rustrt::rand_next(**c) as float;
          let u3 = rustrt::rand_next(**c) as float;
          let scale = u32::max_value as float;
          ret ((u1 / scale + u2) / scale + u3) / scale;
        }
        fn gen_str(len: uint) -> str {
            let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                          "abcdefghijklmnopqrstuvwxyz" +
                          "0123456789";
            let s = "";
            let i = 0u;
            while (i < len) {
                let n = rustrt::rand_next(**c) as uint %
                    str::char_len(charset);
                s = s + str::from_char(str::char_at(charset, n));
                i += 1u;
            }
            s
        }
        fn gen_bytes(len: uint) -> [u8] {
            let v = [];
            let i = 0u;
            while i < len {
                let n = rustrt::rand_next(**c) as uint;
                v += [(n % (u8::max_value as uint)) as u8];
                i += 1u;
            }
            v
        }
    }
    ret rt_rng(@rand_res(rustrt::rand_new()));
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Convert std::sha1 and std::rand over to ifaces<commit_after>/*
Module: rand

Random number generation
*/
#[abi = "cdecl"]
native mod rustrt {
    type rctx;
    fn rand_new() -> rctx;
    fn rand_next(c: rctx) -> u32;
    fn rand_free(c: rctx);
}

/* Section: Types */

/*
Obj: rng

A random number generator
*/
iface rng {
    /*
    Method: next

    Return the next random integer
    */
    fn next() -> u32;

    /*
    Method: next_float

    Return the next random float
    */
    fn next_float() -> float;

    /*
    Method: gen_str

    Return a random string composed of A-Z, a-z, 0-9.
    */
    fn gen_str(len: uint) -> str;

    /*
    Method: gen_bytes

    Return a random byte string.
    */
    fn gen_bytes(len: uint) -> [u8];
}

resource rand_res(c: rustrt::rctx) { rustrt::rand_free(c); }

/* Section: Operations */

/*
Function: mk_rng

Create a random number generator
*/
fn mk_rng() -> rng {
    impl of rng for @rand_res {
        fn next() -> u32 { ret rustrt::rand_next(**self); }
        fn next_float() -> float {
          let u1 = rustrt::rand_next(**self) as float;
          let u2 = rustrt::rand_next(**self) as float;
          let u3 = rustrt::rand_next(**self) as float;
          let scale = u32::max_value as float;
          ret ((u1 / scale + u2) / scale + u3) / scale;
        }
        fn gen_str(len: uint) -> str {
            let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
                          "abcdefghijklmnopqrstuvwxyz" +
                          "0123456789";
            let s = "";
            let i = 0u;
            while (i < len) {
                let n = rustrt::rand_next(**self) as uint %
                    str::char_len(charset);
                s = s + str::from_char(str::char_at(charset, n));
                i += 1u;
            }
            s
        }
        fn gen_bytes(len: uint) -> [u8] {
            let v = [];
            let i = 0u;
            while i < len {
                let n = rustrt::rand_next(**self) as uint;
                v += [(n % (u8::max_value as uint)) as u8];
                i += 1u;
            }
            v
        }
    }
    @rand_res(rustrt::rand_new()) as rng
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>#[cfg(test)]
pub mod mocks;

#[cfg(test)]
mod spec_tests;

use crate::{
    cart::Cart,
    ppu::{control_register::IncrementAmount, write_latch::LatchState},
};
use std::cell::Cell;

pub trait IVram: Default {
    fn write_ppu_addr(&self, latch_state: LatchState);
    fn write_ppu_data<C: Cart>(&mut self, val: u8, inc_amount: IncrementAmount, cart: &mut C);
    fn read_ppu_data<C: Cart>(&self, inc_amount: IncrementAmount, cart: &C) -> u8;
    fn ppu_data<C: Cart>(&self, cart: &C) -> u8;
    fn read<C: Cart>(&self, addr: u16, cart: &C) -> u8;
    fn read_palette(&self, addr: u16) -> u8;
    fn addr(&self) -> u16;
    fn scroll_write(&self, latch_state: LatchState);
    fn control_write(&self, val: u8);
    fn coarse_x_increment(&self);
    fn fine_y_increment(&self);
    fn copy_horizontal_pos_to_addr(&self);
    fn copy_vertical_pos_to_addr(&self);
    fn fine_x(&self) -> u8;
}

pub struct Vram {
    address: Cell<u16>,
    name_tables: [u8; 0x1000],
    palette: [u8; 0x20],
    ppu_data_buffer: Cell<u8>,
    t: Cell<u16>,
    fine_x: Cell<u8>,
}

impl Default for Vram {
    fn default() -> Self {
        Vram {
            address: Cell::new(0),
            name_tables: [0; 0x1000],
            palette: [0; 0x20],
            ppu_data_buffer: Cell::new(0),
            t: Cell::new(0),
            fine_x: Cell::new(0),
        }
    }
}

impl IVram for Vram {
    fn write_ppu_addr(&self, latch_state: LatchState) {
        // Addresses greater than 0x3fff are mirrored down
        match latch_state {
            LatchState::FirstWrite(val) => {
                // t: ..FEDCBA ........ = d: ..FEDCBA
                // t: .X...... ........ = 0
                let t = self.t.get() & 0b1000_0000_1111_1111;
                self.t.set(((u16::from(val) & 0b0011_1111) << 8) | t)
            }
            LatchState::SecondWrite(val) => {
                // t: ....... HGFEDCBA = d: HGFEDCBA
                // v                   = t
                let t = u16::from(val) | (self.t.get() & 0b0111_1111_0000_0000);
                self.t.set(t);
                self.address.set(t);
            }
        }
    }

    fn write_ppu_data<C: Cart>(&mut self, val: u8, inc_amount: IncrementAmount, cart: &mut C) {
        let addr = self.address.get();

        if addr < 0x2000 {
            cart.write_chr(addr, val);
        } else if addr < 0x3f00 {
            unsafe {
                // Safe unchecked access since the AND will normalize to a valid index
                *self.name_tables.get_unchecked_mut(addr as usize & 0x0fff) = val;
            }
        } else if addr < 0x4000 {
            let addr = addr as usize & 0x1f;
            // Certain sprite addresses are mirrored back into background addresses
            let addr = match addr & 0xf {
                0x0 => 0x0,
                0x4 => 0x4,
                0x8 => 0x8,
                0xc => 0xc,
                _ => addr,
            };
            unsafe {
                // Safe unchecked access since the AND will normalize to a valid index
                *self.palette.get_unchecked_mut(addr) = val;
            }
        }

        match inc_amount {
            IncrementAmount::One => self.address.set(self.address.get() + 1),
            IncrementAmount::ThirtyTwo => self.address.set(self.address.get() + 32),
        }
    }

    fn read_ppu_data<C: Cart>(&self, inc_amount: IncrementAmount, cart: &C) -> u8 {
        let val = self.ppu_data(cart);
        match inc_amount {
            IncrementAmount::One => self.address.set(self.address.get() + 1),
            IncrementAmount::ThirtyTwo => self.address.set(self.address.get() + 32),
        }
        val
    }

    fn ppu_data<C: Cart>(&self, cart: &C) -> u8 {
        let addr = self.address.get();
        let val = self.read(addr, cart);

        // When reading while the VRAM address is in the range 0-$3EFF (i.e., before the palettes),
        // the read will return the contents of an internal read buffer. This internal buffer is
        // updated only when reading PPUDATA, and so is preserved across frames. After the CPU reads
        // and gets the contents of the internal buffer, the PPU will immediately update the
        // internal buffer with the byte at the current VRAM address
        if addr < 0x3f00 {
            let buffered_val = self.ppu_data_buffer.get();
            self.ppu_data_buffer.set(val);
            buffered_val
        } else {
            val
        }
    }

    fn read<C: Cart>(&self, addr: u16, cart: &C) -> u8 {
        if addr < 0x2000 {
            cart.read_chr(addr)
        } else if addr < 0x3f00 {
            unsafe {
                // Safe unchecked access since the AND will normalize to a valid index
                *self.name_tables.get_unchecked(addr as usize & 0x0fff)
            }
        } else if addr < 0x4000 {
            let addr = addr & 0x1f;
            self.read_palette(addr)
        } else {
            panic!("Invalid vram read")
        }
    }

    fn read_palette(&self, addr: u16) -> u8 {
        // Certain sprite addresses are mirrored back into background addresses
        let addr = match addr & 0xf {
            0x0 => 0x0,
            0x4 => 0x4,
            0x8 => 0x8,
            0xc => 0xc,
            _ => addr,
        };
        unsafe {
            // Safe unchecked access since the AND will normalize to a valid index
            *self.palette.get_unchecked(addr as usize)
        }
    }

    fn addr(&self) -> u16 {
        self.address.get()
    }

    fn scroll_write(&self, latch_state: LatchState) {
        match latch_state {
            LatchState::FirstWrite(val) => {
                // t: ....... ...HGFED = d: HGFED...
                let t = self.t.get() & 0b_1111_1111_1110_0000;
                self.t.set(((u16::from(val) & 0b_1111_1000) >> 3) | t);

                //x:              CBA = d: .....CBA
                self.fine_x.set(val & 0b_0000_0111);
            }
            LatchState::SecondWrite(val) => {
                // t: CBA..HG FED..... = d: HGFEDCBA
                let t = self.t.get() & 0b_0000_1100_0001_1111;
                let cba_mask = (u16::from(val) & 0b_0000_0111) << 12;
                let hgfed_mask = (u16::from(val) & 0b_1111_1000) << 2;
                self.t.set((cba_mask | hgfed_mask) | t);
            }
        }
    }

    fn control_write(&self, val: u8) {
        // t: ...BA.. ........ = d: ......BA
        let t = self.t.get() & 0b0111_0011_1111_1111;
        let new_t = ((u16::from(val) & 0b0011) << 10) | t;
        self.t.set(new_t);
    }

    fn coarse_x_increment(&self) {
        let v = self.address.get();

        // The coarse X component of v needs to be incremented when the next tile is reached. Bits
        // 0-4 are incremented, with overflow toggling bit 10. This means that bits 0-4 count from 0
        // to 31 across a single nametable, and bit 10 selects the current nametable horizontally.
        let v = if v & 0x001F == 31 {
            // set coarse X = 0 and switch horizontal nametable
            (v & !0x001F) ^ 0x0400
        } else {
            // increment coarse X
            v + 1
        };

        self.address.set(v);
    }

    fn fine_y_increment(&self) {
        let v = self.address.get();

        let v = if v & 0x7000 != 0x7000 {
            // if fine Y < 7, increment fine Y
            v + 0x1000
        } else {
            // if fine Y = 0...
            let v = v & !0x7000;

            // let y = coarse Y
            let mut y = (v & 0x03E0) >> 5;
            let v = if y == 29 {
                // set coarse Y to 0
                y = 0;

                // switch vertical nametable
                v ^ 0x0800
            } else if y == 31 {
                // set coarse Y = 0, nametable not switched
                y = 0;
                v
            } else {
                // increment coarse Y
                y += 1;
                v
            };

            // put coarse Y back into v
            (v & !0x03E0) | (y << 5)
        };

        self.address.set(v);
    }

    fn copy_horizontal_pos_to_addr(&self) {
        // At dot 257 of each scanline, if rendering is enabled, the PPU copies all bits related to
        // horizontal position from t to v:
        // v: ....F.. ...EDCBA = t: ....F.. ...EDCBA
        let v = self.address.get() & 0b0111_1011_1110_0000;
        self.address.set((self.t.get() & 0b0000_0100_0001_1111) | v)
    }

    fn copy_vertical_pos_to_addr(&self) {
        // During dots 280 to 304 of the pre-render scanline (end of vblank), if rendering is
        // enabled, at the end of vblank, shortly after the horizontal bits are copied from t to v
        // at dot 257, the PPU will repeatedly copy the vertical bits from t to v from dots 280 to
        // 304, completing the full initialization of v from t:
        // v: IHGF.ED CBA..... = t: IHGF.ED CBA.....
        let v = self.address.get() & 0b0000_0100_0001_1111;
        self.address.set((self.t.get() & 0b0111_1011_1110_0000) | v)
    }

    fn fine_x(&self) -> u8 {
        self.fine_x.get()
    }
}
<commit_msg>Get rid of some unsafe code<commit_after>#[cfg(test)]
pub mod mocks;

#[cfg(test)]
mod spec_tests;

use crate::{
    cart::Cart,
    ppu::{control_register::IncrementAmount, write_latch::LatchState},
};
use std::cell::Cell;

pub trait IVram: Default {
    fn write_ppu_addr(&self, latch_state: LatchState);
    fn write_ppu_data<C: Cart>(&mut self, val: u8, inc_amount: IncrementAmount, cart: &mut C);
    fn read_ppu_data<C: Cart>(&self, inc_amount: IncrementAmount, cart: &C) -> u8;
    fn ppu_data<C: Cart>(&self, cart: &C) -> u8;
    fn read<C: Cart>(&self, addr: u16, cart: &C) -> u8;
    fn read_palette(&self, addr: u16) -> u8;
    fn addr(&self) -> u16;
    fn scroll_write(&self, latch_state: LatchState);
    fn control_write(&self, val: u8);
    fn coarse_x_increment(&self);
    fn fine_y_increment(&self);
    fn copy_horizontal_pos_to_addr(&self);
    fn copy_vertical_pos_to_addr(&self);
    fn fine_x(&self) -> u8;
}

pub struct Vram {
    address: Cell<u16>,
    name_tables: [u8; 0x1000],
    palette: [u8; 0x20],
    ppu_data_buffer: Cell<u8>,
    t: Cell<u16>,
    fine_x: Cell<u8>,
}

impl Default for Vram {
    fn default() -> Self {
        Vram {
            address: Cell::new(0),
            name_tables: [0; 0x1000],
            palette: [0; 0x20],
            ppu_data_buffer: Cell::new(0),
            t: Cell::new(0),
            fine_x: Cell::new(0),
        }
    }
}

impl IVram for Vram {
    fn write_ppu_addr(&self, latch_state: LatchState) {
        // Addresses greater than 0x3fff are mirrored down
        match latch_state {
            LatchState::FirstWrite(val) => {
                // t: ..FEDCBA ........ = d: ..FEDCBA
                // t: .X...... ........ = 0
                let t = self.t.get() & 0b1000_0000_1111_1111;
                self.t.set(((u16::from(val) & 0b0011_1111) << 8) | t)
            }
            LatchState::SecondWrite(val) => {
                // t: ....... HGFEDCBA = d: HGFEDCBA
                // v                   = t
                let t = u16::from(val) | (self.t.get() & 0b0111_1111_0000_0000);
                self.t.set(t);
                self.address.set(t);
            }
        }
    }

    fn write_ppu_data<C: Cart>(&mut self, val: u8, inc_amount: IncrementAmount, cart: &mut C) {
        let addr = self.address.get();

        if addr < 0x2000 {
            cart.write_chr(addr, val);
        } else if addr < 0x3f00 {
            self.name_tables[addr as usize & 0x0fff] = val;
        } else if addr < 0x4000 {
            let addr = addr as usize & 0x1f;
            // Certain sprite addresses are mirrored back into background addresses
            let addr = match addr & 0xf {
                0x0 => 0x0,
                0x4 => 0x4,
                0x8 => 0x8,
                0xc => 0xc,
                _ => addr,
            };
            self.palette[addr] = val;
        }

        match inc_amount {
            IncrementAmount::One => self.address.set(self.address.get() + 1),
            IncrementAmount::ThirtyTwo => self.address.set(self.address.get() + 32),
        }
    }

    fn read_ppu_data<C: Cart>(&self, inc_amount: IncrementAmount, cart: &C) -> u8 {
        let val = self.ppu_data(cart);
        match inc_amount {
            IncrementAmount::One => self.address.set(self.address.get() + 1),
            IncrementAmount::ThirtyTwo => self.address.set(self.address.get() + 32),
        }
        val
    }

    fn ppu_data<C: Cart>(&self, cart: &C) -> u8 {
        let addr = self.address.get();
        let val = self.read(addr, cart);

        // When reading while the VRAM address is in the range 0-$3EFF (i.e., before the palettes),
        // the read will return the contents of an internal read buffer. This internal buffer is
        // updated only when reading PPUDATA, and so is preserved across frames. After the CPU reads
        // and gets the contents of the internal buffer, the PPU will immediately update the
        // internal buffer with the byte at the current VRAM address
        if addr < 0x3f00 {
            let buffered_val = self.ppu_data_buffer.get();
            self.ppu_data_buffer.set(val);
            buffered_val
        } else {
            val
        }
    }

    fn read<C: Cart>(&self, addr: u16, cart: &C) -> u8 {
        if addr < 0x2000 {
            cart.read_chr(addr)
        } else if addr < 0x3f00 {
            unsafe {
                // Safe unchecked access since the AND will normalize to a valid index
                *self.name_tables.get_unchecked(addr as usize & 0x0fff)
            }
        } else if addr < 0x4000 {
            let addr = addr & 0x1f;
            self.read_palette(addr)
        } else {
            panic!("Invalid vram read")
        }
    }

    fn read_palette(&self, addr: u16) -> u8 {
        // Certain sprite addresses are mirrored back into background addresses
        let addr = match addr & 0xf {
            0x0 => 0x0,
            0x4 => 0x4,
            0x8 => 0x8,
            0xc => 0xc,
            _ => addr,
        };
        unsafe {
            // Safe unchecked access since the AND will normalize to a valid index
            *self.palette.get_unchecked(addr as usize)
        }
    }

    fn addr(&self) -> u16 {
        self.address.get()
    }

    fn scroll_write(&self, latch_state: LatchState) {
        match latch_state {
            LatchState::FirstWrite(val) => {
                // t: ....... ...HGFED = d: HGFED...
                let t = self.t.get() & 0b_1111_1111_1110_0000;
                self.t.set(((u16::from(val) & 0b_1111_1000) >> 3) | t);

                //x:              CBA = d: .....CBA
                self.fine_x.set(val & 0b_0000_0111);
            }
            LatchState::SecondWrite(val) => {
                // t: CBA..HG FED..... = d: HGFEDCBA
                let t = self.t.get() & 0b_0000_1100_0001_1111;
                let cba_mask = (u16::from(val) & 0b_0000_0111) << 12;
                let hgfed_mask = (u16::from(val) & 0b_1111_1000) << 2;
                self.t.set((cba_mask | hgfed_mask) | t);
            }
        }
    }

    fn control_write(&self, val: u8) {
        // t: ...BA.. ........ = d: ......BA
        let t = self.t.get() & 0b0111_0011_1111_1111;
        let new_t = ((u16::from(val) & 0b0011) << 10) | t;
        self.t.set(new_t);
    }

    fn coarse_x_increment(&self) {
        let v = self.address.get();

        // The coarse X component of v needs to be incremented when the next tile is reached. Bits
        // 0-4 are incremented, with overflow toggling bit 10. This means that bits 0-4 count from 0
        // to 31 across a single nametable, and bit 10 selects the current nametable horizontally.
        let v = if v & 0x001F == 31 {
            // set coarse X = 0 and switch horizontal nametable
            (v & !0x001F) ^ 0x0400
        } else {
            // increment coarse X
            v + 1
        };

        self.address.set(v);
    }

    fn fine_y_increment(&self) {
        let v = self.address.get();

        let v = if v & 0x7000 != 0x7000 {
            // if fine Y < 7, increment fine Y
            v + 0x1000
        } else {
            // if fine Y = 0...
            let v = v & !0x7000;

            // let y = coarse Y
            let mut y = (v & 0x03E0) >> 5;
            let v = if y == 29 {
                // set coarse Y to 0
                y = 0;

                // switch vertical nametable
                v ^ 0x0800
            } else if y == 31 {
                // set coarse Y = 0, nametable not switched
                y = 0;
                v
            } else {
                // increment coarse Y
                y += 1;
                v
            };

            // put coarse Y back into v
            (v & !0x03E0) | (y << 5)
        };

        self.address.set(v);
    }

    fn copy_horizontal_pos_to_addr(&self) {
        // At dot 257 of each scanline, if rendering is enabled, the PPU copies all bits related to
        // horizontal position from t to v:
        // v: ....F.. ...EDCBA = t: ....F.. ...EDCBA
        let v = self.address.get() & 0b0111_1011_1110_0000;
        self.address.set((self.t.get() & 0b0000_0100_0001_1111) | v)
    }

    fn copy_vertical_pos_to_addr(&self) {
        // During dots 280 to 304 of the pre-render scanline (end of vblank), if rendering is
        // enabled, at the end of vblank, shortly after the horizontal bits are copied from t to v
        // at dot 257, the PPU will repeatedly copy the vertical bits from t to v from dots 280 to
        // 304, completing the full initialization of v from t:
        // v: IHGF.ED CBA..... = t: IHGF.ED CBA.....
        let v = self.address.get() & 0b0000_0100_0001_1111;
        self.address.set((self.t.get() & 0b0111_1011_1110_0000) | v)
    }

    fn fine_x(&self) -> u8 {
        self.fine_x.get()
    }
}
<|endoftext|>