<commit_before>//! POET - the Performance with Optimal Energy Toolkit. Used to meet timing constraints while
//! minimizing energy consumption.

use libc::{self, c_void, c_char, c_int, c_uint, c_double};
use std::ffi::CString;
use std::ptr;
use std::mem;

/// Typedef for an "apply" function - used to manipulate system or application settings.
pub type POETApplyFn = unsafe extern fn(states: *mut c_void,
                                        num_states: c_uint,
                                        id: c_uint,
                                        last_id: c_uint);

/// Typedef for "current state" function - used to try and determine the current system or
/// application state.
pub type POETCurrentStateFn = unsafe extern fn(states: *const c_void,
                                               num_states: c_uint,
                                               curr_state_id: *mut c_uint) -> i32;

#[link(name = "poet")]
extern {
    /*
     * Core functions - poet.h
     */

    fn poet_init(perf_goal: c_double,
                 num_system_states: c_uint,
                 control_states: *mut POETControlState,
                 apply_states: *mut POETCpuState,
                 apply_func: POETApplyFn,
                 curr_state_func: POETCurrentStateFn,
                 period: c_uint,
                 buffer_depth: c_uint,
                 log_filename: *const c_char) -> *mut c_void;

    fn poet_apply_control(state: *mut c_void,
                          id: u64,
                          perf: c_double,
                          pwr: c_double);

    fn poet_destroy(state: *mut c_void);

    /*
     * Configuration/utility functions - poet_config.h
     */

    fn apply_cpu_config(states: *mut c_void,
                        num_states: c_uint,
                        id: c_uint,
                        last_id: c_uint);

    fn get_control_states(path: *const c_char,
                          states: *mut *mut POETControlState,
                          num_states: *mut c_uint) -> c_int;

    fn get_cpu_states(path: *const c_char,
                      states: *mut *mut POETCpuState,
                      num_states: *mut c_uint) -> c_int;

    fn get_current_cpu_state(states: *const c_void,
                             num_states: c_uint,
                             curr_state_id: *mut c_uint) -> c_int;

}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_control_state_t`.
pub struct POETControlState {
    pub id: c_uint,
    pub speedup: c_double,
    pub cost: c_double,
}

impl POETControlState {
    /// Attempt to load control states from a file.
    pub fn new(filename: Option<&CString>) -> Result<Vec<POETControlState>, &'static str> {
        let name_ptr = match filename {
            Some(f) => f.as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETControlState = ptr::null_mut::<POETControlState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_control_states(name_ptr,
                                         &mut states,
                                         &mut nstates);
            if res != 0 {
                return Err("Failed to load control states");
            }
            let csvec: Vec<POETControlState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETControlState {
    fn default() -> POETControlState {
        POETControlState {
            id: 0,
            speedup: 1.0,
            cost: 1.0,
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_cpu_state_t`.
pub struct POETCpuState {
    id: c_uint,
    freq: c_uint,
    cores: c_uint,
}

impl POETCpuState {
    /// Attempt to load cpu states from a file.
    pub fn new(filename: Option<&CString>) -> Result<Vec<POETCpuState>, &'static str> {
        let name_ptr = match filename {
            Some(f) => f.as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETCpuState = ptr::null_mut::<POETCpuState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_cpu_states(name_ptr,
                                     &mut states,
                                     &mut nstates);
            if res != 0 {
                return Err("Failed to load cpu states");
            }
            let csvec: Vec<POETCpuState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETCpuState {
    fn default() -> POETCpuState {
        POETCpuState {
            id: 0,
            freq: 0,
            cores: 0,
        }
    }
}

/// The `POET` struct wraps an underyling C struct.
pub struct POET {
    /// The underlying C struct `poet_state`.
    pub poet: *mut c_void,
}

impl POET {
    /// Attempt to initialize POET and allocate resources in the underlying C struct.
    pub fn new(perf_goal: f64,
               control_states: &mut Vec<POETControlState>,
               cpu_states: &mut Vec<POETCpuState>,
               apply_func: Option<POETApplyFn>,
               curr_state_func: Option<POETCurrentStateFn>,
               period: u32,
               buffer_depth: u32,
               log_filename: Option<&CString>) -> Result<POET, &'static str> {
        if control_states.len() != cpu_states.len() {
            return Err("Number of control and cpu states don't match");
        }
        let apply_func = match apply_func {
            Some(p) => p,
            None => apply_cpu_config,
        };
        let curr_state_func = match curr_state_func {
            Some(p) => p,
            None => get_current_cpu_state,
        };
        let log_ptr = match log_filename {
            Some(l) => l.as_ptr(),
            None => ptr::null(),
        };
        let poet = unsafe {
            let num_states = control_states.len() as u32;
            poet_init(perf_goal,
                      num_states, control_states.as_mut_ptr(), cpu_states.as_mut_ptr(),
                      apply_func, curr_state_func,
                      period, buffer_depth, log_ptr)
        };
        if poet.is_null() {
            return Err("Failed to instantiate POET object");
        }
        Ok(POET { poet: poet, })
    }

    /// Call at every iteration - at specified periods this function will (potentially) order
    /// changes to system or application state to try and meet timing constraints.
    pub fn apply_control(&mut self, tag: u64, window_rate: f64, window_power: f64) {
        unsafe {
            poet_apply_control(self.poet, tag, window_rate, window_power);
        }
    }
}

impl Drop for POET {
    /// Cleanup POET and deallocate resources in the underlying C struct.
    fn drop(&mut self) {
        unsafe {
            poet_destroy(self.poet);
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libc::{c_void, c_uint};
    use std::ffi::CString;

    #[test]
    fn test_basic() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, None).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_control_cpu_files_with_log() {
        let mut control_states = POETControlState::new(Some(&CString::new("test/control_config").unwrap())).unwrap();
        let mut cpu_states = POETCpuState::new(Some(&CString::new("test/cpu_config").unwrap())).unwrap();
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, Some(&CString::new("poet.log").unwrap())).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_rust_callbacks() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 Some(dummy_apply), Some(dummy_curr_state),
                                 20u32, 1u32, None).unwrap();
        for i in 0..50 {
            poet.apply_control(i, 1.0, 1.0);
        }
    }

    unsafe extern fn dummy_apply(_states: *mut c_void,
                                 _num_states: c_uint,
                                 _id: c_uint,
                                 _last_id: c_uint) {
        // do nothing
        println!("Received apply call");
    }

    unsafe extern fn dummy_curr_state (_states: *const c_void,
                                       _num_states: c_uint,
                                       _curr_state_id: *mut c_uint) -> i32 {
        println!("Received curr state call");
        // this is actually an invalid value, but forces the apply function to be called
        *_curr_state_id = _num_states;
        0
    }

}
<commit_msg>Reduce amount of unsafe<commit_after>//! POET - the Performance with Optimal Energy Toolkit. Used to meet timing constraints while
//! minimizing energy consumption.

use libc::{self, c_void, c_char, c_int, c_uint, c_double};
use std::ffi::CString;
use std::ptr;
use std::mem;

/// Typedef for an "apply" function - used to manipulate system or application settings.
pub type POETApplyFn = extern fn(states: *mut c_void,
                                 num_states: c_uint,
                                 id: c_uint,
                                 last_id: c_uint);

/// Typedef for "current state" function - used to try and determine the current system or
/// application state.
pub type POETCurrentStateFn = extern fn(states: *const c_void,
                                        num_states: c_uint,
                                        curr_state_id: *mut c_uint) -> i32;

#[link(name = "poet")]
extern {
    /*
     * Core functions - poet.h
     */

    fn poet_init(perf_goal: c_double,
                 num_system_states: c_uint,
                 control_states: *mut POETControlState,
                 apply_states: *mut POETCpuState,
                 apply_func: POETApplyFn,
                 curr_state_func: POETCurrentStateFn,
                 period: c_uint,
                 buffer_depth: c_uint,
                 log_filename: *const c_char) -> *mut c_void;

    fn poet_apply_control(state: *mut c_void,
                          id: u64,
                          perf: c_double,
                          pwr: c_double);

    fn poet_destroy(state: *mut c_void);

    /*
     * Configuration/utility functions - poet_config.h
     */

    fn apply_cpu_config(states: *mut c_void,
                        num_states: c_uint,
                        id: c_uint,
                        last_id: c_uint);

    fn get_control_states(path: *const c_char,
                          states: *mut *mut POETControlState,
                          num_states: *mut c_uint) -> c_int;

    fn get_cpu_states(path: *const c_char,
                      states: *mut *mut POETCpuState,
                      num_states: *mut c_uint) -> c_int;

    fn get_current_cpu_state(states: *const c_void,
                             num_states: c_uint,
                             curr_state_id: *mut c_uint) -> c_int;

}

extern fn apply_cpu_config_wrapper(states: *mut c_void,
                                   num_states: c_uint,
                                   id: c_uint,
                                   last_id: c_uint) {
    unsafe {
        apply_cpu_config(states, num_states, id, last_id)
    }
}

extern fn get_current_cpu_state_wrapper(states: *const c_void,
                                        num_states: c_uint,
                                        curr_state_id: *mut c_uint) -> c_int {
    unsafe {
        get_current_cpu_state(states, num_states, curr_state_id)
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_control_state_t`.
pub struct POETControlState {
    pub id: c_uint,
    pub speedup: c_double,
    pub cost: c_double,
}

impl POETControlState {
    /// Attempt to load control states from a file.
    pub fn new(filename: Option<&CString>) -> Result<Vec<POETControlState>, &'static str> {
        let name_ptr = match filename {
            Some(f) => f.as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETControlState = ptr::null_mut::<POETControlState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_control_states(name_ptr,
                                         &mut states,
                                         &mut nstates);
            if res != 0 {
                return Err("Failed to load control states");
            }
            let csvec: Vec<POETControlState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETControlState {
    fn default() -> POETControlState {
        POETControlState {
            id: 0,
            speedup: 1.0,
            cost: 1.0,
        }
    }
}

#[repr(C)]
#[derive(Copy, Clone)]
/// Representation of native struct `poet_cpu_state_t`.
pub struct POETCpuState {
    id: c_uint,
    freq: c_uint,
    cores: c_uint,
}

impl POETCpuState {
    /// Attempt to load cpu states from a file.
    pub fn new(filename: Option<&CString>) -> Result<Vec<POETCpuState>, &'static str> {
        let name_ptr = match filename {
            Some(f) => f.as_ptr(),
            None => ptr::null(),
        };
        let mut states: *mut POETCpuState = ptr::null_mut::<POETCpuState>();
        unsafe {
            let mut nstates: u32 = 0;
            let res = get_cpu_states(name_ptr,
                                     &mut states,
                                     &mut nstates);
            if res != 0 {
                return Err("Failed to load cpu states");
            }
            let csvec: Vec<POETCpuState> = Vec::from_raw_parts(states, nstates as usize, nstates as usize);
            // clone so we can free C-allocated memory (so user doesn't have to)
            let ret = csvec.clone();
            mem::forget(csvec);
            libc::free(states as *mut c_void);
            Ok(ret)
        }
    }
}

impl Default for POETCpuState {
    fn default() -> POETCpuState {
        POETCpuState {
            id: 0,
            freq: 0,
            cores: 0,
        }
    }
}

/// The `POET` struct wraps an underyling C struct.
pub struct POET {
    /// The underlying C struct `poet_state`.
    pub poet: *mut c_void,
}

impl POET {
    /// Attempt to initialize POET and allocate resources in the underlying C struct.
    pub fn new(perf_goal: f64,
               control_states: &mut Vec<POETControlState>,
               cpu_states: &mut Vec<POETCpuState>,
               apply_func: Option<POETApplyFn>,
               curr_state_func: Option<POETCurrentStateFn>,
               period: u32,
               buffer_depth: u32,
               log_filename: Option<&CString>) -> Result<POET, &'static str> {
        if control_states.len() != cpu_states.len() {
            return Err("Number of control and cpu states don't match");
        }
        // the following necessary cast for None seem to be a bug in Rust coercion
        let apply_func = match apply_func {
            Some(p) => p,
            None => apply_cpu_config_wrapper
                    as extern "C" fn(*mut libc::types::common::c95::c_void, u32, u32, u32),
        };
        let curr_state_func = match curr_state_func {
            Some(p) => p,
            None => get_current_cpu_state_wrapper
                    as extern "C" fn(*const libc::types::common::c95::c_void, u32, *mut u32) -> i32,
        };
        let log_ptr = match log_filename {
            Some(l) => l.as_ptr(),
            None => ptr::null(),
        };
        let poet = unsafe {
            let num_states = control_states.len() as u32;
            poet_init(perf_goal,
                      num_states, control_states.as_mut_ptr(), cpu_states.as_mut_ptr(),
                      apply_func, curr_state_func,
                      period, buffer_depth, log_ptr)
        };
        if poet.is_null() {
            return Err("Failed to instantiate POET object");
        }
        Ok(POET { poet: poet, })
    }

    /// Call at every iteration - at specified periods this function will (potentially) order
    /// changes to system or application state to try and meet timing constraints.
    pub fn apply_control(&mut self, tag: u64, window_rate: f64, window_power: f64) {
        unsafe {
            poet_apply_control(self.poet, tag, window_rate, window_power);
        }
    }
}

impl Drop for POET {
    /// Cleanup POET and deallocate resources in the underlying C struct.
    fn drop(&mut self) {
        unsafe {
            poet_destroy(self.poet);
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libc::{c_void, c_uint};
    use std::ffi::CString;

    #[test]
    fn test_basic() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, None).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_control_cpu_files_with_log() {
        let mut control_states = POETControlState::new(Some(&CString::new("test/control_config").unwrap())).unwrap();
        let mut cpu_states = POETCpuState::new(Some(&CString::new("test/cpu_config").unwrap())).unwrap();
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 None, None,
                                 20u32, 1u32, Some(&CString::new("poet.log").unwrap())).unwrap();
        poet.apply_control(0, 1.0, 1.0);
    }

    #[test]
    fn test_rust_callbacks() {
        let mut control_states = vec![POETControlState::default()];
        let mut cpu_states = vec![POETCpuState::default()];
        let mut poet = POET::new(100.0,
                                 &mut control_states, &mut cpu_states,
                                 Some(dummy_apply), Some(dummy_curr_state),
                                 20u32, 1u32, None).unwrap();
        for i in 0..50 {
            poet.apply_control(i, 1.0, 1.0);
        }
    }

    extern fn dummy_apply(_states: *mut c_void,
                          _num_states: c_uint,
                          _id: c_uint,
                          _last_id: c_uint) {
        // do nothing
        println!("Received apply call");
    }

    extern fn dummy_curr_state(_states: *const c_void,
                               _num_states: c_uint,
                               _curr_state_id: *mut c_uint) -> i32 {
        println!("Received curr state call");
        unsafe {
            // this is actually an invalid value, but forces the apply function to be called
            *_curr_state_id = _num_states;
        }
        0
    }

}
<|endoftext|>


<commit_before>use crate::direction::Direction;

pub struct Ship {
    state: ShipState,
    position: Vec<[u8; 2]>,
    dir: Direction,
}

impl Ship {
    pub fn new(pos: Vec<[u8; 2]>) -> Ship {
        let dir = Direction::from_positions(&pos[1], &pos[0]).unwrap();

        Ship {
            state: ShipState::Placement,
            position: pos,
            dir: dir,
        }
    }

    pub fn pos(&self) -> &[[u8; 2]] {
        &self.position
    }

    pub fn set_pos(&mut self, pos: Vec<[u8; 2]>) -> Result<(), &'static str> {
        let dir = Direction::from_positions(&pos[1], &pos[0])?;

        self.position = pos;
        self.dir = dir;

        Ok(())
    }

    pub fn dir(&self) -> Direction {
        self.dir
    }

    pub fn len(&self) -> usize {
        self.position.len()
    }

    pub fn is_active(&self) -> bool {
        self.state == ShipState::Active
    }

    /// Sets the ship as active.
    ///
    /// # Errors
    ///
    /// Returns an error if the ship's state is not `ShipState::Placement`.
    pub fn set_active(&mut self) -> Result<(), &'static str> {
        if self.state != ShipState::Placement {
            Err("tried to set a ship as active that wasn't in placement state")
        } else {
            self.state = ShipState::Active;

            Ok(())
        }
    }

    pub fn is_sunk(&self) -> bool {
        self.state == ShipState::Sunk
    }

    /// Sets the ship as having been sunk.
    ///
    /// # Errors
    ///
    /// Returns an error if the ship's state is not `ShipState::Active`.
    pub fn set_sunk(&mut self) -> Result<(), &'static str> {
        if self.state != ShipState::Active {
            Err("tried to sink a ship that was not active")
        } else {
            self.state = ShipState::Sunk;

            Ok(())
        }
    }
}

#[derive(PartialEq)]
enum ShipState {
    Placement,
    Active,
    Sunk
}

<commit_msg>Add missing doc comments to Ship methods<commit_after>use crate::direction::Direction;

pub struct Ship {
    state: ShipState,
    position: Vec<[u8; 2]>,
    dir: Direction,
}

impl Ship {
    /// Creates a new `Ship` with the given position.
    pub fn new(pos: Vec<[u8; 2]>) -> Ship {
        let dir = Direction::from_positions(&pos[1], &pos[0]).unwrap();

        Ship {
            state: ShipState::Placement,
            position: pos,
            dir: dir,
        }
    }

    /// Returns the ship's position.
    pub fn pos(&self) -> &[[u8; 2]] {
        &self.position
    }

    /// Sets the ship's position.
    ///
    /// # Errors
    ///
    /// Returns an error if `pos` does not form a vertical or horizontal line.
    pub fn set_pos(&mut self, pos: Vec<[u8; 2]>) -> Result<(), &'static str> {
        let dir = Direction::from_positions(&pos[1], &pos[0])?;

        self.position = pos;
        self.dir = dir;

        Ok(())
    }

    /// Returns the ship's direction.
    pub fn dir(&self) -> Direction {
        self.dir
    }

    /// Returns the ship's length.
    pub fn len(&self) -> usize {
        self.position.len()
    }

    /// Returns whether the ship is active.
    pub fn is_active(&self) -> bool {
        self.state == ShipState::Active
    }

    /// Sets the ship as active.
    ///
    /// # Errors
    ///
    /// Returns an error if the ship's state is not `ShipState::Placement`.
    pub fn set_active(&mut self) -> Result<(), &'static str> {
        if self.state != ShipState::Placement {
            Err("tried to set a ship as active that wasn't in placement state")
        } else {
            self.state = ShipState::Active;

            Ok(())
        }
    }

    /// Returns whether the ship has sunk.
    pub fn is_sunk(&self) -> bool {
        self.state == ShipState::Sunk
    }

    /// Sets the ship as having been sunk.
    ///
    /// # Errors
    ///
    /// Returns an error if the ship's state is not `ShipState::Active`.
    pub fn set_sunk(&mut self) -> Result<(), &'static str> {
        if self.state != ShipState::Active {
            Err("tried to sink a ship that was not active")
        } else {
            self.state = ShipState::Sunk;

            Ok(())
        }
    }
}

#[derive(PartialEq)]
enum ShipState {
    Placement,
    Active,
    Sunk
}

<|endoftext|>


<commit_before>use std::ascii;
use std::ascii::Ascii;
use std::vec::Vec;

/// Member b is a vector of std::ascii::Ascii holding a word to be stemmed.
/// The letters are in b[0], b[1] ... ending at b[z->k]. Member k is readjusted
/// downwards as the stemming progresses. Zero termination is not in fact used
/// in the algorithm.
///
/// Note that only lower case sequences are stemmed. Forcing to lower case
/// should be done before get(...) is called.
///
///
/// Typical usage is:
///
///     let b = "pencils";
///     let res = stem::get(b);
///     assert_eq!(res, Ok("pencil".to_string()));
///
pub struct Stemmer {
    b: Vec<ascii::Ascii>,
    k: uint,
    j: uint,
}

impl Stemmer {
    pub fn new(word: &str) -> Result<Stemmer, &str> {
        if !word.is_ascii() {
            Err("Only support English words with ASCII characters")
        } else {
            let b = unsafe { word.to_ascii_nocheck().to_lowercase() };
            let k = b.len();
            Ok(Stemmer {
                b: b,
                k: k,
                j: 0,
            })
        }
    }

    /// stem.is_consonant(i) is true <=> stem[i] is a consonant
    pub fn is_consonant(&self, i: uint) -> bool {
        match self.b[i].to_char() {
            'a' | 'e' | 'i' | 'o' | 'u' => false,
            'y' => if i == 0 {
                true
            } else {
                !self.is_consonant(i - 1)
            },
            _ => true,
        }
    }

    /// stem.measure() measures the number of consonant sequences in [0, j).
    /// if c is a consonant sequence and v a vowel sequence, and <..> indicates
    /// arbitrary presence,
    ///
    /// ~~~notrust
    ///    <c><v>       gives 0
    ///    <c>vc<v>     gives 1
    ///    <c>vcvc<v>   gives 2
    ///    <c>vcvcvc<v> gives 3
    ///    ....
    /// ~~~
    pub fn measure(&self) -> uint {
        let mut n = 0u;
        let mut i = 0u;
        let j = self.j;
        loop {
            if i >= j { return n }
            if !self.is_consonant(i) { break }
            i += 1;
        }
        i += 1;
        loop {
            loop {
                if i >= j { return n }
                if self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
            n += 1;
            loop {
                if i >= j { return n }
                if !self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
        }
    }

    /// stem.has_vowel() is TRUE <=> [0, j-1) contains a vowel
    pub fn has_vowel(&self) -> bool {
        for i in range(0, self.j) {
            if !self.is_consonant(i) {
                return true;
            }
        }
        return false;
    }

    /// stem.double_consonant(i) is TRUE <=> i,(i-1) contain a double consonant.
    pub fn double_consonant(&self, i: uint) -> bool {
        if i < 1 {
            false
        } else if self.b[i] != self.b[i - 1] {
            false
        } else {
            self.is_consonant(i)
        }
    }

    /// cvc(z, i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant
    /// and also if the second c is not w,x or y. this is used when trying to
    /// restore an e at the end of a short word. e.g.
    ///
    /// ~~~notrust
    ///    cav(e), lov(e), hop(e), crim(e), but
    ///    snow, box, tray.
    /// ~~~
    pub fn cvc(&self, i: uint) -> bool {
        if i < 2 || !self.is_consonant(i) || self.is_consonant(i - 1)
            || !self.is_consonant(i - 2) { return false }
        match self.b[i].to_char() {
            'w' | 'x' | 'y' => false,
            _ => true,
        }
    }

    /// stem.ends(s) is true <=> [0, k) ends with the string s.
    pub fn ends(&mut self, s: &str) -> bool {
        let s = s.as_bytes();
        let len = s.len();
        let k = self.k;
        if s[len - 1] != self.b[k-1].to_byte() { return false } /* tiny speed-up */
        if len > k { return false }
        let mut iter = s.iter();
        for ac in self.b.slice(k - len, k).iter() {
            if ac.to_byte() != *iter.next().unwrap() { return false }
        }
        self.j = k - len;
        return true;
    }

    /// stem.setto(s) sets [j,k) to the characters in the string s,
    /// readjusting k.
    fn set_to(&mut self, s: &str) {
        let s = s.as_bytes();
        let length = s.len();
        let j = self.j;
        for i in range(0, length) {
            self.b.as_mut_slice()[j + i] = s[i].to_ascii();
        }
        self.k = j + length;
    }

    /// self.replace(s) is used further down.
    fn r(&mut self, s: &str) {
        if self.measure() > 0 {
            self.set_to(s);
        }
    }

    /// stem.step1ab() gets rid of plurals and -ed or -ing. e.g.
    ///
    /// ~~~~notrust
    ///     caresses  ->  caress
    ///     ponies    ->  poni
    ///     ties      ->  ti
    ///     caress    ->  caress
    ///     cats      ->  cat
    ///
    ///     feed      ->  feed
    ///     agreed    ->  agree
    ///     disabled  ->  disable
    ///
    ///     matting   ->  mat
    ///     mating    ->  mate
    ///     meeting   ->  meet
    ///     milling   ->  mill
    ///     messing   ->  mess
    ///
    ///     meetings  ->  meet
    /// ~~~~
    pub fn step1ab(&mut self) {
        if self.b[self.k - 1].to_char() == 's' {
            if self.ends("sses") {
                self.k -= 2;
            } else if self.ends("ies") {
                self.set_to("i");
            } else if self.b[self.k - 2].to_char() != 's' {
                self.k -= 1;
            }
        }
        if self.ends("eed") {
            if self.measure() > 0 { self.k -= 1 }
        } else if (self.ends("ed") || self.ends("ing")) && self.has_vowel() {
            self.k = self.j;
            if self.ends("at") {
                self.set_to("ate");
            } else if self.ends("bl") {
                self.set_to("ble");
            } else if self.ends("iz") {
                self.set_to("ize");
            } else if self.double_consonant(self.k - 1) {
                self.k -= 1;
                match self.b[self.k - 1].to_char() {
                    'l' | 's' | 'z' => self.k += 1,
                    _ => (),
                }
            } else if self.measure() == 1 && self.cvc(self.k - 1) {
                self.set_to("e");
            }
        }
    }

    /// stem.step1c() turns terminal y to i when there is another vowel in the stem.
    pub fn step1c(&mut self) {
       if self.ends("y") && self.has_vowel() {
           self.b.as_mut_slice()[self.k-1] = 'i'.to_ascii();
        }
    }

    /// stem.step2() maps double suffices to single ones. so -ization ( = -ize
    /// plus -ation) maps to -ize etc. note that the string before the suffix
    /// must give m(z) > 0.
    pub fn step2(&mut self) {
        match self.b[self.k-2].to_char() {
            'a' => {
                if self.ends("ational") { self.r("ate"); return }
                if self.ends("tional") { self.r("tion"); return }
            },
            'c' => {
                if self.ends("enci") { self.r("ence"); return }
                if self.ends("anci") { self.r("ance"); return }
            },
            'e' => if self.ends("izer") { self.r("ize"); return },
            'l' => {
                if self.ends("bli") { self.r("ble"); return } /*-DEPARTURE-*/

             /* To match the published algorithm, replace this line with
                'l' => {
                    if self.ends("abli") { self.r("able"); return } */

                 if self.ends("alli") { self.r("al"); return }
                 if self.ends("entli") { self.r("ent"); return }
                 if self.ends("eli") { self.r("e"); return }
                 if self.ends("ousli") { self.r("ous"); return }
            },
            'o' => {
                if self.ends("ization") { self.r("ize"); return }
                if self.ends("ation") { self.r("ate"); return }
                if self.ends("ator") { self.r("ate"); return }
            },
            's' => {
                if self.ends("alism") { self.r("al"); return }
                if self.ends("iveness") { self.r("ive"); return }
                if self.ends("fulness") { self.r("ful"); return }
                if self.ends("ousness") { self.r("ous"); return }
            },
            't' => {
                if self.ends("aliti") { self.r("al"); return }
                if self.ends("iviti") { self.r("ive"); return }
                if self.ends("biliti") { self.r("ble"); return }
            },
            'g' => if self.ends("logi") { self.r("log"); return }, /*-DEPARTURE-*/
             /* To match the published algorithm, delete this line */
            _ => (),
        }
    }

    /// stem.step3() deals with -ic-, -full, -ness etc. similar strategy to step2.
    pub fn step3(&mut self) {
        match self.b[self.k-1].to_char() {
            'e' => {
                if self.ends("icate") { self.r("ic"); return }
                if self.ends("ative") { self.r(""); return }
                if self.ends("alize") { self.r("al"); return }
            },
            'i' => if self.ends("iciti") { self.r("ic"); return },
            'l' => {
                if self.ends("ical") { self.r("ic"); return }
                if self.ends("ful") { self.r(""); return }
            },
            's' => if self.ends("ness") { self.r(""); return },
            _ => (),
        }
    }

    /// stem.step4() takes off -ant, -ence etc., in context <c>vcvc<v>.
    pub fn step4(&mut self) {
        match self.b[self.k-2].to_char() {
            'a' => {
                if self.ends("al") {}
                else { return }
            }
            'c' => {
                if self.ends("ance") {}
                else if self.ends("ence") {}
                else { return }
            },
            'e' => {
                if self.ends("er") {}
                else { return }
            }
            'i' => {
                if self.ends("ic") {}
                else { return }
            },
            'l' => {
                if self.ends("able") {}
                else if self.ends("ible") {}
                else { return }
            },
            'n' => {
                if self.ends("ant") {}
                else if self.ends("ement") {}
                else if self.ends("ment") {}
                else if self.ends("ent") {}
                else { return }
            },
            'o' => {
                if self.ends("ion")
                    && (self.b[self.j-1].to_char() == 's' || self.b[self.j-1].to_char() == 't') {}
                else if self.ends("ou") {}
                else { return }
                /* takes care of -ous */
            },
            's' => {
                if self.ends("ism") {}
                else { return }
            },
            't' => {
                if self.ends("ate") {}
                else if self.ends("iti") {}
                else { return }
            },
            'u' => {
                if self.ends("ous") {}
                else { return }
            },
            'v' => {
                if self.ends("ive") {}
                else { return }
            },
            'z' => {
                if self.ends("ize") {}
                else { return }
            },
            _ => return,
        }
        if self.measure() > 1 { self.k = self.j }
    }

    /// stem.step5() removes a final -e if self.measure() > 1, and changes -ll
    /// to -l if self.measure() > 1.
    pub fn step5(&mut self) {
       self.j = self.k;
       if self.b[self.k - 1].to_char() == 'e' {
           let a = self.measure();
           if a > 1 || a == 1 && !self.cvc(self.k - 2) { self.k -= 1 }
       }
       if self.b[self.k-1].to_char() == 'l'
           && self.double_consonant(self.k-1) && self.measure() > 1 {
           self.k-=1;
       }
    }

    pub fn get(&self) -> String {
        let borrowed = self.b.slice_to(self.k);
        borrowed.as_str_ascii().into_string()
    }
}

pub fn get(word: &str) -> Result<String, &str> {
    if word.len() > 2 {
        match Stemmer::new(word) {
            Ok(w) => {
                let mut mw = w;
                mw.step1ab();
                mw.step1c();
                mw.step2();
                mw.step3();
                mw.step4();
                mw.step5();
                Ok(mw.get())
            }
            Err(e) => Err(e),
        }
    } else {
        Ok(word.into_string())
    }
}

#[cfg(test)]
mod test {
    use std::io::File;
    use std::io::BufferedReader;
    use std::path;

    use super::get;

    #[test]
    fn lexicon() {
        let input = File::open(&path::Path::new("test-data/voc.txt")).unwrap();
        let result = File::open(&path::Path::new("test-data/output.txt")).unwrap();
        let mut input_reader = BufferedReader::new(input);
        let mut result_reader = BufferedReader::new(result);
        loop {
            match input_reader.read_line() {
                Ok(word) => match get(word.as_slice().trim()) {
                    Ok(stem) => {
                        match result_reader.read_line() {
                            Ok(answer) => if answer.as_slice().trim() != stem.as_slice() {
                                panic!("\n[FAILED] '{:s}' != '{:s}'", stem, answer);
                            } else {
                                print!(".");
                            },
                            Err(_) => break,
                        }
                    },
                    Err(e) => panic!("\n[FAILED] Cannot get stem for '{:s}': {:s}", word, e),
                },
                Err(_) => break,
            }
        }
        println!("");
    }
}
<commit_msg>got rid of warnings, updated tests and added benchmarks. removed underlying impl as a vector of asciis and instead just use a vector of bytes<commit_after>#[cfg(test)] extern crate test;

use std::borrow::ToOwned;

/// Member b is a vector of bytes holding a word to be stemmed.
/// The letters are in b[0], b[1] ... ending at b[z->k]. Member k is readjusted
/// downwards as the stemming progresses. Zero termination is not in fact used
/// in the algorithm.
///
/// Note that only lower case sequences are stemmed. Forcing to lower case
/// should be done before get(...) is called.
///
///
/// Typical usage is:
///
///     let b = "pencils";
///     let res = stem::get(b);
///     assert_eq!(res, Ok("pencil".to_string()));
///
struct Stemmer {
    b: Vec<u8>,
    k: uint,
    j: uint
}

impl Stemmer {
    fn new(word: &str) -> Result<Stemmer, &str> {
        if !word.is_ascii() {
            Err("Only support English words with ASCII characters")
        } else {
            let b: Vec<u8> = unsafe {
                word.to_ascii_nocheck()
                    .iter()
                    .map(|c| c.to_lowercase().as_byte())
                    .collect()
            };
            let k = b.len();
            Ok(Stemmer {
                b: b,
                k: k,
                j: 0
            })
        }
    }

    /// stem.is_consonant(i) is true <=> stem[i] is a consonant
    #[inline]
    fn is_consonant(&self, i: uint) -> bool {
        match self.b[i] {
            b'a' | b'e' | b'i' | b'o' | b'u' => false,
            b'y' => if i == 0 {
                true
            } else {
                !self.is_consonant(i - 1)
            },
            _ => true
        }
    }

    /// stem.measure() measures the number of consonant sequences in [0, j).
    /// if c is a consonant sequence and v a vowel sequence, and <..> indicates
    /// arbitrary presence,
    ///
    /// ~~~notrust
    ///    <c><v>       gives 0
    ///    <c>vc<v>     gives 1
    ///    <c>vcvc<v>   gives 2
    ///    <c>vcvcvc<v> gives 3
    ///    ....
    /// ~~~
    fn measure(&self) -> uint {
        let mut n = 0u;
        let mut i = 0u;
        let j = self.j;
        loop {
            if i >= j { return n }
            if !self.is_consonant(i) { break }
            i += 1;
        }
        i += 1;
        loop {
            loop {
                if i >= j { return n }
                if self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
            n += 1;
            loop {
                if i >= j { return n }
                if !self.is_consonant(i) { break }
                i += 1;
            }
            i += 1;
        }
    }

    /// stem.has_vowel() is TRUE <=> [0, j-1) contains a vowel
    fn has_vowel(&self) -> bool {
        for i in range(0, self.j) {
            if !self.is_consonant(i) {
                return true;
            }
        }
        false
    }

    /// stem.double_consonant(i) is TRUE <=> i,(i-1) contain a double consonant.
    #[inline]
    fn double_consonant(&self, i: uint) -> bool {
        if i < 1 {
            false
        } else if self.b[i] != self.b[i - 1] {
            false
        } else {
            self.is_consonant(i)
        }
    }

    /// cvc(z, i) is TRUE <=> i-2,i-1,i has the form consonant - vowel - consonant
    /// and also if the second c is not w,x or y. this is used when trying to
    /// restore an e at the end of a short word. e.g.
    ///
    /// ~~~notrust
    ///    cav(e), lov(e), hop(e), crim(e), but
    ///    snow, box, tray.
    /// ~~~
    fn cvc(&self, i: uint) -> bool {
        if i < 2
            || !self.is_consonant(i)
            || self.is_consonant(i - 1)
            || !self.is_consonant(i - 2)
        {
            false
        } else {
            match self.b[i] {
                b'w' | b'x' | b'y' => false,
                _ => true
            }
        }
    }

    /// stem.ends(s) is true <=> [0, k) ends with the string s.
    fn ends(&mut self, _s: &str) -> bool {
        let s = _s.as_bytes();
        let len = s.len();
        if len > self.k {
            false
        } else {
            if self.b.slice(self.k - len, self.k) == s {
                self.j = self.k - len;
                true
            } else {
                false
            }
        }
    }

    /// stem.setto(s) sets [j,k) to the characters in the string s,
    /// readjusting k.
    fn set_to(&mut self, s: &str) {
        let s = s.as_bytes();
        let len = s.len();
        for i in range(0, len) {
            self.b.as_mut_slice()[self.j + i] = s[i];
        }
        self.k = self.j + len;
    }

    /// self.replace(s) is used further down.
    #[inline]
    fn r(&mut self, s: &str) {
        if self.measure() > 0 {
            self.set_to(s);
        }
    }

    /// stem.step1ab() gets rid of plurals and -ed or -ing. e.g.
    ///
    /// ~~~~notrust
    ///     caresses  ->  caress
    ///     ponies    ->  poni
    ///     ties      ->  ti
    ///     caress    ->  caress
    ///     cats      ->  cat
    ///
    ///     feed      ->  feed
    ///     agreed    ->  agree
    ///     disabled  ->  disable
    ///
    ///     matting   ->  mat
    ///     mating    ->  mate
    ///     meeting   ->  meet
    ///     milling   ->  mill
    ///     messing   ->  mess
    ///
    ///     meetings  ->  meet
    /// ~~~~
    fn step1ab(&mut self) {
        if self.b[self.k - 1] == b's' {
            if self.ends("sses") {
                self.k -= 2;
            } else if self.ends("ies") {
                self.set_to("i");
            } else if self.b[self.k - 2] != b's' {
                self.k -= 1;
            }
        }
        if self.ends("eed") {
            if self.measure() > 0 { self.k -= 1 }
        } else if (self.ends("ed") || self.ends("ing")) && self.has_vowel() {
            self.k = self.j;
            if self.ends("at") {
                self.set_to("ate");
            } else if self.ends("bl") {
                self.set_to("ble");
            } else if self.ends("iz") {
                self.set_to("ize");
            } else if self.double_consonant(self.k - 1) {
                self.k -= 1;
                match self.b[self.k - 1] {
                    b'l' | b's' | b'z' => self.k += 1,
                    _ => ()
                }
            } else if self.measure() == 1 && self.cvc(self.k - 1) {
                self.set_to("e");
            }
        }
    }

    /// stem.step1c() turns terminal y to i when there is another vowel in the stem.
    fn step1c(&mut self) {
        if self.ends("y") && self.has_vowel() {
           self.b.as_mut_slice()[self.k - 1] = b'i';
        }
    }

    /// stem.step2() maps double suffices to single ones. so -ization ( = -ize
    /// plus -ation) maps to -ize etc. note that the string before the suffix
    /// must give m(z) > 0.
    fn step2(&mut self) {
        match self.b[self.k - 2] {
            b'a' => {
                if self.ends("ational") { self.r("ate"); return }
                if self.ends("tional") { self.r("tion"); return }
            }
            b'c' => {
                if self.ends("enci") { self.r("ence"); return }
                if self.ends("anci") { self.r("ance"); return }
            }
            b'e' => if self.ends("izer") { self.r("ize"); return },
            b'l' => {
                if self.ends("bli") { self.r("ble"); return } /*-DEPARTURE-*/

             /* To match the published algorithm, replace this line with
                'l' => {
                    if self.ends("abli") { self.r("able"); return } */

                if self.ends("alli") { self.r("al"); return }
                if self.ends("entli") { self.r("ent"); return }
                if self.ends("eli") { self.r("e"); return }
                if self.ends("ousli") { self.r("ous"); return }
            }
            b'o' => {
                if self.ends("ization") { self.r("ize"); return }
                if self.ends("ation") { self.r("ate"); return }
                if self.ends("ator") { self.r("ate"); return }
            }
            b's' => {
                if self.ends("alism") { self.r("al"); return }
                if self.ends("iveness") { self.r("ive"); return }
                if self.ends("fulness") { self.r("ful"); return }
                if self.ends("ousness") { self.r("ous"); return }
            }
            b't' => {
                if self.ends("aliti") { self.r("al"); return }
                if self.ends("iviti") { self.r("ive"); return }
                if self.ends("biliti") { self.r("ble"); return }
            }
            b'g' => if self.ends("logi") { self.r("log"); return }, /*-DEPARTURE-*/
             /* To match the published algorithm, delete this line */
            _ => ()
        }
    }

    /// stem.step3() deals with -ic-, -full, -ness etc. similar strategy to step2.
    fn step3(&mut self) {
        match self.b[self.k - 1] {
            b'e' => {
                if self.ends("icate") { self.r("ic"); return }
                if self.ends("ative") { self.r(""); return }
                if self.ends("alize") { self.r("al"); return }
            }
            b'i' => if self.ends("iciti") { self.r("ic"); return },
            b'l' => {
                if self.ends("ical") { self.r("ic"); return }
                if self.ends("ful") { self.r(""); return }
            }
            b's' => if self.ends("ness") { self.r(""); return },
            _ => ()
        }
    }

    /// stem.step4() takes off -ant, -ence etc., in context <c>vcvc<v>.
    fn step4(&mut self) {
        match self.b[self.k - 2] {
            b'a' => {
                if self.ends("al") {}
                else { return }
            }
            b'c' => {
                if self.ends("ance") {}
                else if self.ends("ence") {}
                else { return }
            }
            b'e' => {
                if self.ends("er") {}
                else { return }
            }
            b'i' => {
                if self.ends("ic") {}
                else { return }
            }
            b'l' => {
                if self.ends("able") {}
                else if self.ends("ible") {}
                else { return }
            }
            b'n' => {
                if self.ends("ant") {}
                else if self.ends("ement") {}
                else if self.ends("ment") {}
                else if self.ends("ent") {}
                else { return }
            }
            b'o' => {
                if self.ends("ion") &&
                    (self.b[self.j - 1] == b's' || self.b[self.j - 1] == b't') {}
                else if self.ends("ou") {}
                else { return }
                /* takes care of -ous */
            }
            b's' => {
                if self.ends("ism") {}
                else { return }
            }
            b't' => {
                if self.ends("ate") {}
                else if self.ends("iti") {}
                else { return }
            }
            b'u' => {
                if self.ends("ous") {}
                else { return }
            }
            b'v' => {
                if self.ends("ive") {}
                else { return }
            }
            b'z' => {
                if self.ends("ize") {}
                else { return }
            }
            _ => return
        }
        if self.measure() > 1 { self.k = self.j }
    }

    /// stem.step5() removes a final -e if self.measure() > 0, and changes -ll
    /// to -l if self.measure() > 1.
    fn step5(&mut self) {
        self.j = self.k;
        if self.b[self.k - 1] == b'e' {
            let a = self.measure();
            if a > 1 || a == 1 && !self.cvc(self.k - 2) { self.k -= 1 }
        }
        if self.b[self.k - 1] == b'l' &&
            self.double_consonant(self.k - 1) &&
            self.measure() > 1
        {
            self.k -= 1;
        }
    }

    #[inline]
    fn get(&self) -> String {
        unsafe { String::from_raw_buf_len(self.b.slice_to(self.k).as_ptr(), self.k) }
    }
}

pub fn get(word: &str) -> Result<String, &str> {
    if word.len() > 2 {
        match Stemmer::new(word) {
            Ok(w) => {
                let mut mw = w;
                mw.step1ab();
                mw.step1c();
                mw.step2();
                mw.step3();
                mw.step4();
                mw.step5();
                Ok(mw.get())
            }
            Err(e) => Err(e),
        }
    } else {
        Ok(word.to_owned())
    }
}

#[cfg(test)]
mod test_stem {
    use super::get;
    use test::Bencher;

    pub static INPUT: &'static str  = include_str!("../test-data/voc.txt");
    pub static RESULT: &'static str = include_str!("../test-data/output.txt");

    fn test_loop<
        's,
        I0: Iterator<T>,
        I1: Iterator<T>,
        T: Deref<str>>(
        tests: I0,
        results: I1
    ) {
        for (test, expect) in tests.zip(results) {
            let stemmed = get(test.trim_right_chars('\r'));

            assert!(stemmed.is_ok(), "[FAILED] Expected stem for '{}'", test.trim_right_chars('\r'));
            assert_eq!(stemmed.unwrap().trim_right_chars('\r'), expect.trim_right_chars('\r'));
        }
    }

    #[test]
    fn lexicon() {
        let input_s = INPUT.split('\n');
        let result_s = RESULT.split('\n');

        test_loop(input_s, result_s);
    }

    #[bench]
    fn lexicon_bench(b: &mut Bencher) {
        let input_v: Vec<&str> = INPUT.split('\n').collect();

        b.iter(|| {
            for t in input_v.iter() {
                let stemmed = get(t.trim_right_chars('\r'));

                assert!(stemmed.is_ok());
            }
        });
    }

    #[bench]
    fn single_bench(b: &mut Bencher) {
        b.iter(|| {
            let stemmed = get("testing");

            assert!(stemmed.is_ok());
        });
    }
}
<|endoftext|>


<commit_before>macro_rules! grammar {
    { for $grammar:ty { $($grammar_defn:tt)* } } => {
        impl $crate::Grammar for $grammar { }
        declare_nonterminals! { $grammar, $($grammar_defn)* }
    }
}

macro_rules! declare_nonterminals {
    ( $grammar:ty, $nonterminal:ident: $ty:ty = $defn:tt => $body:expr ;
      $($remainder:tt)* ) => {
        declare_map_nonterminal! { $grammar, $nonterminal, $ty, $defn, $body }
        declare_nonterminals! { $grammar, $($remainder)* }
    };
    ( $grammar:ty, $nonterminal:ident: $ty:ty = $defn:tt ;
      $($remainder:tt)* ) => {
        declare_identity_nonterminal! { $grammar, $nonterminal, $ty, $defn }
        declare_nonterminals! { $grammar, $($remainder)* }
    };
    ( $grammar:ty, ) => {
    };
}

macro_rules! declare_map_nonterminal {
    ($grammar:ty, $nonterminal:ident, $ty:ty, $defn:tt, $body:expr) => {
        #[allow(non_camel_case_types)]
        #[derive(Debug)]
        pub struct $nonterminal;

        impl $crate::Parser<$grammar> for $nonterminal {
            type Output = $ty;

            fn pretty_print(&self) -> String {
                format!("{:?}", self)
            }

            fn parse<'a>(&self,
                         grammar: &'a $grammar,
                         start: $crate::Input<'a>)
                         -> $crate::ParseResult<'a,$ty>
            {
                let parser = named_item!($defn);
                let (end, named_item_pat!($defn)) =
                    try!($crate::Parser::parse(&parser, grammar, start));
                Ok((end,$body))
            }
        }
    }
}

macro_rules! declare_identity_nonterminal {
    ($grammar:ty, $nonterminal:ident, $ty:ty, $defn:tt) => {
        #[allow(non_camel_case_types)]
        #[derive(Debug)]
        pub struct $nonterminal;

        impl $crate::Parser<$grammar> for $nonterminal {
            type Output = $ty;

            fn pretty_print(&self) -> String {
                format!("{:?}", self)
            }

            fn parse<'a>(&self,
                         grammar: &'a $grammar,
                         start: $crate::Input<'a>)
                         -> $crate::ParseResult<'a,$ty> {
                             let parser = item!($defn);
                             $crate::Parser::parse(&parser, grammar, start)
                         }
        }
    }
}

macro_rules! named_item {
    ( ( $($a:tt)* ) ) => {
        named_items!($($a)*)
    };
    ( $a:tt ) => {
        item!($a)
    }
}

macro_rules! named_items {
    ( < $name:ident : $a:tt > , $($bs:tt)* ) => {
        {
            let bs = named_items!($($bs)*);
            items!($a, bs)
        }
    };
    ( < $name:ident : $a:tt > ) => {
        item!($a)
    };
    ( $a:tt, $($bs:tt)* ) => {
        {
            let bs = named_items!($($bs)*);
            items!($a, bs)
        }
    };
    ( $a:tt ) => {
        item!($a)
    };
    ( ) => {
        Empty
    };
}

macro_rules! named_item_pat {
    ( ( $($a:tt)* ) ) => {
        named_items_pat!($($a)*)
    };
    ( $a:tt ) => {
        _
    }
}

macro_rules! named_items_pat {
    ( < $name:ident : $a:tt > , $($bs:tt)* ) => {
        ($name, named_items_pat!($($bs)*))
    };
    ( < $name:ident : $a:tt > ) => {
        $name
    };
    ( $a:tt, $($bs:tt)* ) => {
        (_, named_items_pat!($($bs)*))
    };
    ( $a:tt ) => {
        _
    };
    ( ) => {
        ()
    };
}

macro_rules! items {
    ( $a:tt, $($bs:tt)* ) => {
        $crate::util::Join { first: item!($a), second: items!($($bs)*), }
    };
    ( $a:tt | $($bs:tt)* ) => {
        $crate::util::Or { a: item!($a), b: items!($($bs)*) }
    };
    ( $a:tt ) => {
        item!($a)
    };
    ( ) => {
        Empty
    }
}

macro_rules! item {
    { ( ) } => {
        Empty
    };

    { ( $tt:tt ) } => {
        item!($tt)
    };

    { ( $($tt:tt)* ) } => {
        items!($($tt)*)
    };

    { [ $($tt:tt)* ] } => {
        $crate::util::Optional { parser: items!($($tt)*) }
    };

    { { + $($tt:tt)* } } => {
        $crate::util::Repeat { parser: items!($($tt)*), min: 1,
                               separator: $crate::util::Whitespace }
    };

    { { * $($tt:tt)* } } => {
        $crate::util::Repeat { parser: items!($($tt)*), min: 0,
                               separator: $crate::util::Whitespace }
    };

    { { $($tt:tt)* } } => {
        $crate::util::Repeat { parser: items!($($tt)*), min: 0,
                               separator: $crate::util::Whitespace }
    };

    { $name:expr } => {
        $name
    };
}

mod silly_grammar {
    pub struct Foo;

    grammar! {
        for Foo {
            Hi: u32 = ("Hi") => 1;
            Ho: u32 = "Ho" => 2;

            HiOrHo: u32 = (Hi|Ho);

            Sum: u32 = (Sum1 | HiOrHo);
            Sum1: u32 = (<x:HiOrHo>, "+", <y:Sum>) => {x + y*10};

            HiHo: () = (Hi, Ho) => ();

            Rep: Vec<u32> = {HiOrHo};
        }
    }

    fn should_parse_prefix<G,P:?Sized>(grammar: &G,
                                       parser: &P,
                                       text: &str)
                                       -> P::Output
        where G: ::Grammar, P: ::Parser<G>
    {
        parser.parse_prefix(grammar, text).unwrap().1
    }

    #[test]
    fn parse_hi_from_hi() {
        assert_eq!(1, should_parse_prefix(&Foo, &Hi, "Hi"));
    }

    #[test]
    #[should_panic]
    fn parse_hi_from_ho() {
        assert_eq!(2, should_parse_prefix(&Foo, &Hi, "Ho"));
    }

    #[test]
    fn parse_hiorho_from_hi() {
        assert_eq!(1, should_parse_prefix(&Foo, &HiOrHo, "Hi"));
    }

    #[test]
    fn parse_hiorho_from_ho() {
        assert_eq!(2, should_parse_prefix(&Foo, &HiOrHo, "Ho"));
    }

    #[test]
    fn parse_hiho_from_ho() {
        assert_eq!((), should_parse_prefix(&Foo, &HiHo, "Hi Ho"));
    }

    #[test]
    fn parse_sum_from_ho() {
        assert_eq!(1221, should_parse_prefix(&Foo, &Sum, "Hi + Ho + Ho + Hi"));
    }

    #[test]
    fn parse_repeat() {
        assert_eq!(vec![1, 2, 2, 1, 2], should_parse_prefix(&Foo, &Rep, "Hi Ho Ho Hi Ho"));
    }
}

mod classy {
    use Parser;

    #[derive(Debug)]
    pub struct ClassDefn {
        name: &'static str,
        members: Vec<MemberDefn>
    }

    #[derive(Debug)]
    pub enum MemberDefn {
        Field(Box<FieldDefn>),
        Method(Box<MethodDefn>),
    }

    #[derive(Debug)]
    pub struct FieldDefn {
        name: &'static str,
        ty: TypeRef
    }

    #[derive(Debug)]
    pub struct MethodDefn {
        name: &'static str,
        arg_tys: Vec<TypeRef>,
        ret_ty: TypeRef
    }

    #[derive(Debug)]
    pub struct TypeRef {
        id: &'static str
    }

    #[derive(Debug)]
    pub struct Classy;

    grammar! {
        for Classy {
            ID: &'static str =
                ("a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "x" | "y" | "z" |
                 "u32" | "i32"); // goofy

            CLASS: ClassDefn =
                ("class", <name:ID>, "{", <members:{MEMBER}>, "}") => {
                    ClassDefn { name: name, members: members }
                };

            MEMBER: MemberDefn =
                (FIELD_DEFN | METHOD_DEFN);

            FIELD_DEFN: MemberDefn =
                (<name:ID>, ":", <ty:TYPE_REF>, ";") => {
                    MemberDefn::Field(Box::new(
                        FieldDefn { name: name, ty: ty }))
                };

            TYPE_REF: TypeRef =
                (<id:ID>) => {
                    TypeRef { id: id }
                };

            METHOD_DEFN: MemberDefn =
                (<name:ID>, "(", <args:{TYPE_REF}>, ")", "->", <ret:TYPE_REF>, ";") => {
                    MemberDefn::Method(Box::new(
                        MethodDefn { name: name, arg_tys: args, ret_ty: ret }))
                };
        }
    }

    #[test]
    fn parse_a_class() {
        CLASS.parse_prefix(
            &Classy,
            "class x { f: u32; g: i32; h(i32) -> u32; }").unwrap();
    }
}

<commit_msg>Integrate regex library<commit_after>macro_rules! grammar {
    { for $grammar:ty { $($grammar_defn:tt)* } } => {
        impl $crate::Grammar for $grammar { }
        declare_nonterminals! { $grammar, $($grammar_defn)* }
    }
}

macro_rules! declare_nonterminals {
    ( $grammar:ty, $nonterminal:ident: $ty:ty = $defn:tt => $body:expr ;
      $($remainder:tt)* ) => {
        declare_map_nonterminal! { $grammar, $nonterminal, $ty, $defn, $body }
        declare_nonterminals! { $grammar, $($remainder)* }
    };
    ( $grammar:ty, $nonterminal:ident: $ty:ty = $defn:tt ;
      $($remainder:tt)* ) => {
        declare_identity_nonterminal! { $grammar, $nonterminal, $ty, $defn }
        declare_nonterminals! { $grammar, $($remainder)* }
    };
    ( $grammar:ty, ) => {
    };
}

macro_rules! declare_map_nonterminal {
    ($grammar:ty, $nonterminal:ident, $ty:ty, $defn:tt, $body:expr) => {
        #[allow(non_camel_case_types)]
        #[derive(Debug)]
        pub struct $nonterminal;

        impl $crate::Parser<$grammar> for $nonterminal {
            type Output = $ty;

            fn pretty_print(&self) -> String {
                format!("{:?}", self)
            }

            fn parse<'a>(&self,
                         grammar: &'a $grammar,
                         start: $crate::Input<'a>)
                         -> $crate::ParseResult<'a,$ty>
            {
                let parser = named_item!($defn);
                let (end, named_item_pat!($defn)) =
                    try!($crate::Parser::parse(&parser, grammar, start));
                Ok((end,$body))
            }
        }
    }
}

macro_rules! declare_identity_nonterminal {
    ($grammar:ty, $nonterminal:ident, $ty:ty, $defn:tt) => {
        #[allow(non_camel_case_types)]
        #[derive(Debug)]
        pub struct $nonterminal;

        impl $crate::Parser<$grammar> for $nonterminal {
            type Output = $ty;

            fn pretty_print(&self) -> String {
                format!("{:?}", self)
            }

            fn parse<'a>(&self,
                         grammar: &'a $grammar,
                         start: $crate::Input<'a>)
                         -> $crate::ParseResult<'a,$ty> {
                             let parser = item!($defn);
                             $crate::Parser::parse(&parser, grammar, start)
                         }
        }
    }
}

macro_rules! named_item {
    ( ( $($a:tt)* ) ) => {
        named_items!($($a)*)
    };
    ( $a:tt ) => {
        item!($a)
    }
}

macro_rules! named_items {
    ( < $name:ident : $a:tt > , $($bs:tt)* ) => {
        {
            let bs = named_items!($($bs)*);
            items!($a, bs)
        }
    };
    ( < $name:ident : $a:tt > ) => {
        item!($a)
    };
    ( $a:tt, $($bs:tt)* ) => {
        {
            let bs = named_items!($($bs)*);
            items!($a, bs)
        }
    };
    ( $a:tt ) => {
        item!($a)
    };
    ( ) => {
        Empty
    };
}

macro_rules! named_item_pat {
    ( ( $($a:tt)* ) ) => {
        named_items_pat!($($a)*)
    };
    ( $a:tt ) => {
        _
    }
}

macro_rules! named_items_pat {
    ( < $name:ident : $a:tt > , $($bs:tt)* ) => {
        ($name, named_items_pat!($($bs)*))
    };
    ( < $name:ident : $a:tt > ) => {
        $name
    };
    ( $a:tt, $($bs:tt)* ) => {
        (_, named_items_pat!($($bs)*))
    };
    ( $a:tt ) => {
        _
    };
    ( ) => {
        ()
    };
}

macro_rules! items {
    ( $a:tt, $($bs:tt)* ) => {
        $crate::util::Join { first: item!($a), second: items!($($bs)*), }
    };
    ( $a:tt | $($bs:tt)* ) => {
        $crate::util::Or { a: item!($a), b: items!($($bs)*) }
    };
    ( $a:tt ) => {
        item!($a)
    };
    ( ) => {
        Empty
    }
}

macro_rules! item {
    { ( ) } => {
        Empty
    };

    { ( $tt:tt ) } => {
        item!($tt)
    };

    { ( $($tt:tt)* ) } => {
        items!($($tt)*)
    };

    { [ $($tt:tt)* ] } => {
        $crate::util::Optional { parser: items!($($tt)*) }
    };

    { { + $($tt:tt)* } } => {
        $crate::util::Repeat { parser: items!($($tt)*), min: 1,
                               separator: $crate::util::Whitespace }
    };

    { { * $($tt:tt)* } } => {
        $crate::util::Repeat { parser: items!($($tt)*), min: 0,
                               separator: $crate::util::Whitespace }
    };

    { { $($tt:tt)* } } => {
        $crate::util::Repeat { parser: items!($($tt)*), min: 0,
                               separator: $crate::util::Whitespace }
    };

    { $name:expr } => {
        $name
    };
}

mod silly_grammar {
    pub struct Foo;

    grammar! {
        for Foo {
            Hi: u32 = ("Hi") => 1;
            Ho: u32 = "Ho" => 2;

            HiOrHo: u32 = (Hi|Ho);

            Sum: u32 = (Sum1 | HiOrHo);
            Sum1: u32 = (<x:HiOrHo>, "+", <y:Sum>) => {x + y*10};

            HiHo: () = (Hi, Ho) => ();

            Rep: Vec<u32> = {HiOrHo};
        }
    }

    fn should_parse_prefix<G,P:?Sized>(grammar: &G,
                                       parser: &P,
                                       text: &str)
                                       -> P::Output
        where G: ::Grammar, P: ::Parser<G>
    {
        parser.parse_prefix(grammar, text).unwrap().1
    }

    #[test]
    fn parse_hi_from_hi() {
        assert_eq!(1, should_parse_prefix(&Foo, &Hi, "Hi"));
    }

    #[test]
    #[should_panic]
    fn parse_hi_from_ho() {
        assert_eq!(2, should_parse_prefix(&Foo, &Hi, "Ho"));
    }

    #[test]
    fn parse_hiorho_from_hi() {
        assert_eq!(1, should_parse_prefix(&Foo, &HiOrHo, "Hi"));
    }

    #[test]
    fn parse_hiorho_from_ho() {
        assert_eq!(2, should_parse_prefix(&Foo, &HiOrHo, "Ho"));
    }

    #[test]
    fn parse_hiho_from_ho() {
        assert_eq!((), should_parse_prefix(&Foo, &HiHo, "Hi Ho"));
    }

    #[test]
    fn parse_sum_from_ho() {
        assert_eq!(1221, should_parse_prefix(&Foo, &Sum, "Hi + Ho + Ho + Hi"));
    }

    #[test]
    fn parse_repeat() {
        assert_eq!(vec![1, 2, 2, 1, 2], should_parse_prefix(&Foo, &Rep, "Hi Ho Ho Hi Ho"));
    }
}

mod classy {
    use regex::Regex;
    use std::collections::HashSet;
    use Parser;

    #[derive(Debug)]
    pub struct ClassDefn {
        name: String,
        members: Vec<MemberDefn>
    }

    #[derive(Debug)]
    pub enum MemberDefn {
        Field(Box<FieldDefn>),
        Method(Box<MethodDefn>),
    }

    #[derive(Debug)]
    pub struct FieldDefn {
        name: String,
        ty: TypeRef
    }

    #[derive(Debug)]
    pub struct MethodDefn {
        name: String,
        arg_tys: Vec<TypeRef>,
        ret_ty: TypeRef
    }

    #[derive(Debug)]
    pub struct TypeRef {
        id: String
    }

    #[derive(Debug)]
    pub struct Classy {
        identifier: Regex,
        keywords: HashSet<String>,
    }

    impl Classy {
        fn new() -> Classy {
            Classy {
                identifier: Regex::new("^[a-zA-Z_][a-zA-Z_0-9]*").unwrap(),
                keywords: vec!["class"].into_iter().map(|x| x.to_string()).collect(),
            }
        }
    }

    #[allow(non_camel_case_types)]
    #[derive(Debug)]
    struct ID;

    impl Parser<Classy> for ID {
        type Output = String;

        fn pretty_print(&self) -> String {
            format!("{:?}", self)
        }

        fn parse<'a>(&self,
                     grammar: &'a Classy,
                     start: ::Input<'a>)
                     -> ::ParseResult<'a,String>
        {
            match grammar.identifier.find(&start.text[start.offset..]) {
                Some((_, offset)) => {
                    let end = start.offset_by(offset);
                    let matched = &start.text[start.offset..end.offset];
                    if !grammar.keywords.contains(matched) {
                        return Ok((end, matched.to_string()));
                    }
                }
                None => { }
            }

            Err(::Error { expected: "identifier", offset: start.offset })
        }
    }

    grammar! {
        for Classy {
            CLASS: ClassDefn =
                ("class", <name:ID>, "{", <members:{MEMBER}>, "}") => {
                    ClassDefn { name: name, members: members }
                };

            MEMBER: MemberDefn =
                (FIELD_DEFN | METHOD_DEFN);

            FIELD_DEFN: MemberDefn =
                (<name:ID>, ":", <ty:TYPE_REF>, ";") => {
                    MemberDefn::Field(Box::new(
                        FieldDefn { name: name, ty: ty }))
                };

            TYPE_REF: TypeRef =
                (<id:ID>) => {
                    TypeRef { id: id }
                };

            METHOD_DEFN: MemberDefn =
                (<name:ID>, "(", <args:{TYPE_REF}>, ")", "->", <ret:TYPE_REF>, ";") => {
                    MemberDefn::Method(Box::new(
                        MethodDefn { name: name, arg_tys: args, ret_ty: ret }))
                };
        }
    }

    #[test]
    fn parse_a_class() {
        let classy = Classy::new();
        CLASS.parse_prefix(
            &classy,
            "class x { f: u32; g: i32; h(i32) -> u32; }").unwrap();
    }
}

<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>

<% data.new_style_struct("Border", inherited=False,
                   additional_methods=[Method("border_" + side + "_has_nonzero_width",
                                              "bool") for side in ["top", "right", "bottom", "left"]]) %>
<%
    def maybe_logical_spec(side, kind):
        if side[1]: # if it is logical
            return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
        else:
            return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
%>
% for side in ALL_SIDES:
    <%
        side_name = side[0]
        is_logical = side[1]
    %>
    ${helpers.predefined_type(
        "border-%s-color" % side_name, "Color",
        "computed_value::T::currentcolor()",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
        spec=maybe_logical_spec(side, "color"),
        animation_value_type="AnimatedColor",
        logical=is_logical,
        logical_group="border-color",
        allow_quirks=not is_logical,
        flags="APPLIES_TO_FIRST_LETTER",
        ignored_when_colors_disabled=True,
    )}

    ${helpers.predefined_type(
        "border-%s-style" % side_name, "BorderStyle",
        "specified::BorderStyle::None",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
        spec=maybe_logical_spec(side, "style"),
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="discrete" if not is_logical else "none",
        logical=is_logical,
        logical_group="border-style",
        needs_context=False,
    )}

    ${helpers.predefined_type(
        "border-%s-width" % side_name,
        "BorderSideWidth",
        "crate::values::computed::NonNegativeLength::new(3.)",
        computed_type="crate::values::computed::NonNegativeLength",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
        spec=maybe_logical_spec(side, "width"),
        animation_value_type="NonNegativeLength",
        logical=is_logical,
        logical_group="border-width",
        flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
        allow_quirks=not is_logical,
        servo_restyle_damage="reflow rebuild_and_reflow_inline"
    )}
% endfor

${helpers.gecko_keyword_conversion(
    Keyword('border-style',
    "none solid double dotted dashed hidden groove ridge inset outset",
    gecko_enum_prefix="StyleBorderStyle",
    gecko_inexhaustive=True),
    type="crate::values::specified::BorderStyle",
)}

// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
    ${helpers.predefined_type(
        "border-" + corner + "-radius",
        "BorderCornerRadius",
        "computed::BorderCornerRadius::zero()",
        "parse",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
        boxed=True,
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="BorderCornerRadius",
    )}
% endfor

${helpers.single_keyword(
    "box-decoration-break",
    "slice clone",
    gecko_enum_prefix="StyleBoxDecorationBreak",
    gecko_pref="layout.css.box-decoration-break.enabled",
    spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
    products="gecko",
    animation_value_type="discrete",
)}

${helpers.single_keyword(
    "-moz-float-edge",
    "content-box margin-box",
    gecko_ffi_name="mFloatEdge",
    gecko_enum_prefix="StyleFloatEdge",
    products="gecko",
    spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
    animation_value_type="discrete",
)}

${helpers.predefined_type(
    "border-image-source",
    "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
    vector=False,
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=product == "servo",
    ignored_when_colors_disabled=True
)}

${helpers.predefined_type(
    "border-image-outset",
    "LengthOrNumberRect",
    parse_method="parse_non_negative",
    initial_value="computed::LengthOrNumberRect::all(computed::LengthOrNumber::zero())",
    initial_specified_value="specified::LengthOrNumberRect::all(specified::LengthOrNumber::zero())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-repeat",
    "BorderImageRepeat",
    "computed::BorderImageRepeat::stretch()",
    initial_specified_value="specified::BorderImageRepeat::stretch()",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
    flags="APPLIES_TO_FIRST_LETTER",
)}

${helpers.predefined_type(
    "border-image-width",
    "BorderImageWidth",
    initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
    initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-slice",
    "BorderImageSlice",
    initial_value="computed::BorderImageSlice::hundred_percent()",
    initial_specified_value="specified::BorderImageSlice::hundred_percent()",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

// FIXME(emilio): Why does this live here? ;_;
#[cfg(feature = "gecko")]
impl crate::values::computed::BorderImageWidth {
    pub fn to_gecko_rect(&self, sides: &mut crate::gecko_bindings::structs::nsStyleSides) {
        use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
        use crate::gecko::values::GeckoStyleCoordConvertible;
        use crate::values::generics::border::BorderImageSideWidth;

        % for i in range(0, 4):
        match self.${i} {
            BorderImageSideWidth::Auto => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Auto)
            },
            BorderImageSideWidth::Length(l) => {
                l.to_gecko_style_coord(&mut sides.data_at_mut(${i}))
            },
            BorderImageSideWidth::Number(n) => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Factor(n.0))
            },
        }
        % endfor
    }

    pub fn from_gecko_rect(
        sides: &crate::gecko_bindings::structs::nsStyleSides,
    ) -> Option<crate::values::computed::BorderImageWidth> {
        use crate::gecko_bindings::structs::nsStyleUnit::{eStyleUnit_Factor, eStyleUnit_Auto};
        use crate::gecko_bindings::sugar::ns_style_coord::CoordData;
        use crate::gecko::values::GeckoStyleCoordConvertible;
        use crate::values::computed::{LengthOrPercentage, Number};
        use crate::values::generics::border::BorderImageSideWidth;
        use crate::values::generics::NonNegative;

        Some(
            crate::values::computed::BorderImageWidth::new(
                % for i in range(0, 4):
                match sides.data_at(${i}).unit() {
                    eStyleUnit_Auto => {
                        BorderImageSideWidth::Auto
                    },
                    eStyleUnit_Factor => {
                        BorderImageSideWidth::Number(
                            NonNegative(Number::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to Number")))
                    },
                    _ => {
                        BorderImageSideWidth::Length(
                            NonNegative(LengthOrPercentage::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to LengthOrPercentage")))
                    },
                },
                % endfor
            )
        )
    }
}
<commit_msg>style: Drop layout.css.box-decoration-break.enabled pref.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>

<% data.new_style_struct("Border", inherited=False,
                   additional_methods=[Method("border_" + side + "_has_nonzero_width",
                                              "bool") for side in ["top", "right", "bottom", "left"]]) %>
<%
    def maybe_logical_spec(side, kind):
        if side[1]: # if it is logical
            return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
        else:
            return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
%>
% for side in ALL_SIDES:
    <%
        side_name = side[0]
        is_logical = side[1]
    %>
    ${helpers.predefined_type(
        "border-%s-color" % side_name, "Color",
        "computed_value::T::currentcolor()",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
        spec=maybe_logical_spec(side, "color"),
        animation_value_type="AnimatedColor",
        logical=is_logical,
        logical_group="border-color",
        allow_quirks=not is_logical,
        flags="APPLIES_TO_FIRST_LETTER",
        ignored_when_colors_disabled=True,
    )}

    ${helpers.predefined_type(
        "border-%s-style" % side_name, "BorderStyle",
        "specified::BorderStyle::None",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
        spec=maybe_logical_spec(side, "style"),
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="discrete" if not is_logical else "none",
        logical=is_logical,
        logical_group="border-style",
        needs_context=False,
    )}

    ${helpers.predefined_type(
        "border-%s-width" % side_name,
        "BorderSideWidth",
        "crate::values::computed::NonNegativeLength::new(3.)",
        computed_type="crate::values::computed::NonNegativeLength",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
        spec=maybe_logical_spec(side, "width"),
        animation_value_type="NonNegativeLength",
        logical=is_logical,
        logical_group="border-width",
        flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
        allow_quirks=not is_logical,
        servo_restyle_damage="reflow rebuild_and_reflow_inline"
    )}
% endfor

${helpers.gecko_keyword_conversion(
    Keyword('border-style',
    "none solid double dotted dashed hidden groove ridge inset outset",
    gecko_enum_prefix="StyleBorderStyle",
    gecko_inexhaustive=True),
    type="crate::values::specified::BorderStyle",
)}

// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
    ${helpers.predefined_type(
        "border-" + corner + "-radius",
        "BorderCornerRadius",
        "computed::BorderCornerRadius::zero()",
        "parse",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
        boxed=True,
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="BorderCornerRadius",
    )}
% endfor

${helpers.single_keyword(
    "box-decoration-break",
    "slice clone",
    gecko_enum_prefix="StyleBoxDecorationBreak",
    spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
    products="gecko",
    animation_value_type="discrete",
)}

${helpers.single_keyword(
    "-moz-float-edge",
    "content-box margin-box",
    gecko_ffi_name="mFloatEdge",
    gecko_enum_prefix="StyleFloatEdge",
    products="gecko",
    spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
    animation_value_type="discrete",
)}

${helpers.predefined_type(
    "border-image-source",
    "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
    vector=False,
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=product == "servo",
    ignored_when_colors_disabled=True
)}

${helpers.predefined_type(
    "border-image-outset",
    "LengthOrNumberRect",
    parse_method="parse_non_negative",
    initial_value="computed::LengthOrNumberRect::all(computed::LengthOrNumber::zero())",
    initial_specified_value="specified::LengthOrNumberRect::all(specified::LengthOrNumber::zero())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-repeat",
    "BorderImageRepeat",
    "computed::BorderImageRepeat::stretch()",
    initial_specified_value="specified::BorderImageRepeat::stretch()",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
    flags="APPLIES_TO_FIRST_LETTER",
)}

${helpers.predefined_type(
    "border-image-width",
    "BorderImageWidth",
    initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
    initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-slice",
    "BorderImageSlice",
    initial_value="computed::BorderImageSlice::hundred_percent()",
    initial_specified_value="specified::BorderImageSlice::hundred_percent()",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

// FIXME(emilio): Why does this live here? ;_;
#[cfg(feature = "gecko")]
impl crate::values::computed::BorderImageWidth {
    pub fn to_gecko_rect(&self, sides: &mut crate::gecko_bindings::structs::nsStyleSides) {
        use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
        use crate::gecko::values::GeckoStyleCoordConvertible;
        use crate::values::generics::border::BorderImageSideWidth;

        % for i in range(0, 4):
        match self.${i} {
            BorderImageSideWidth::Auto => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Auto)
            },
            BorderImageSideWidth::Length(l) => {
                l.to_gecko_style_coord(&mut sides.data_at_mut(${i}))
            },
            BorderImageSideWidth::Number(n) => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Factor(n.0))
            },
        }
        % endfor
    }

    pub fn from_gecko_rect(
        sides: &crate::gecko_bindings::structs::nsStyleSides,
    ) -> Option<crate::values::computed::BorderImageWidth> {
        use crate::gecko_bindings::structs::nsStyleUnit::{eStyleUnit_Factor, eStyleUnit_Auto};
        use crate::gecko_bindings::sugar::ns_style_coord::CoordData;
        use crate::gecko::values::GeckoStyleCoordConvertible;
        use crate::values::computed::{LengthOrPercentage, Number};
        use crate::values::generics::border::BorderImageSideWidth;
        use crate::values::generics::NonNegative;

        Some(
            crate::values::computed::BorderImageWidth::new(
                % for i in range(0, 4):
                match sides.data_at(${i}).unit() {
                    eStyleUnit_Auto => {
                        BorderImageSideWidth::Auto
                    },
                    eStyleUnit_Factor => {
                        BorderImageSideWidth::Number(
                            NonNegative(Number::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to Number")))
                    },
                    _ => {
                        BorderImageSideWidth::Length(
                            NonNegative(LengthOrPercentage::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to LengthOrPercentage")))
                    },
                },
                % endfor
            )
        )
    }
}
<|endoftext|>