<commit_before>extern crate regex;
extern crate toml;

use constant::*;
use self::regex::Error as RegexError;
use std::num::ParseIntError;
use std::error;
use std::fmt::{self, Display};
use std::io::Error as IoError;

#[derive(Debug)]
pub enum CliError {
    CannotHappen(CannotHappenError),
    Config(ConfigError),
    Io(IoError),
    ParseInt(ParseIntError),
    ParseToml(toml::ParserError),
    Regex(RegexError),
    RunningPlace(RunningPlaceError),
}
impl Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CliError::CannotHappen(ref e) => e.fmt(f),
            CliError::Config(ref e)       => e.fmt(f),
            CliError::Io(ref e)           => e.fmt(f),
            CliError::ParseInt(ref e)     => e.fmt(f),
            CliError::ParseToml(ref e)    => e.fmt(f),
            CliError::Regex(ref e)        => e.fmt(f),
            CliError::RunningPlace(ref e) => e.fmt(f),
        }
    }
}
impl error::Error for CliError {
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CliError::CannotHappen(ref e) => Some(e),
            CliError::Config(ref e)       => Some(e),
            CliError::Io(ref e)           => Some(e),
            CliError::ParseInt(ref e)     => Some(e),
            CliError::ParseToml(ref e)    => Some(e),
            CliError::Regex(ref e)        => Some(e),
            CliError::RunningPlace(ref e) => Some(e),
        }
    }

    fn description(&self) -> &str {
        match *self {
            CliError::CannotHappen(ref e) => e.description(),
            CliError::Config(ref e)       => e.description(),
            CliError::Io(ref e)           => e.description(),
            CliError::ParseInt(ref e)     => e.description(),
            CliError::ParseToml(ref e)    => e.description(),
            CliError::Regex(ref e)        => e.description(),
            CliError::RunningPlace(ref e) => e.description(),
        }
    }
}
impl From<CannotHappenError> for CliError {
    fn from(e: CannotHappenError) -> CliError {
        CliError::CannotHappen(e)
    }
}
impl From<ConfigError> for CliError {
    fn from(e: ConfigError) -> CliError {
        CliError::Config(e)
    }
}
impl From<IoError> for CliError {
    fn from(e: IoError) -> CliError {
        CliError::Io(e)
    }
}
impl From<ParseIntError> for CliError {
    fn from(e: ParseIntError) -> CliError {
        CliError::ParseInt(e)
    }
}
impl From<toml::ParserError> for CliError {
    fn from(e: toml::ParserError) -> CliError {
        CliError::ParseToml(e)
    }
}
impl From<RegexError> for CliError {
    fn from(e: RegexError) -> CliError {
        CliError::Regex(e)
    }
}
impl From<RunningPlaceError> for CliError {
    fn from(e: RunningPlaceError) -> CliError {
        CliError::RunningPlace(e)
    }
}

#[derive(Debug)]
pub struct RunningPlaceError {
    pub dir: String,
}
impl Display for RunningPlaceError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cannot run in \"{}\" directory", self.dir)
    }
}
impl error::Error for RunningPlaceError {
    fn cause(&self) -> Option<&error::Error> { None }

    fn description(&self) -> &str { "cannot run in banned directories" }
}

#[derive(Debug)]
pub enum EssentialKind {
    WorkingDir,
    StorageDir,
    ConfigFile,
    IgnoreFile,
}
impl Display for EssentialKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            EssentialKind::WorkingDir => format!("{} directory", WORKING_DIR_NAME),
            EssentialKind::StorageDir => format!("{} directory", STORAGE_DIR_NAME),
            EssentialKind::ConfigFile => format!("{} file"     , CONFIG_FILE_NAME),
            EssentialKind::IgnoreFile => format!("{} file"     , IGNORE_FILE_NAME),
        })
    }
}

#[derive(Debug)]
pub struct EssentialLack {
    pub what: EssentialKind,
}
impl Display for EssentialLack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} does not exist. Please use \"init\" command", self.what)
    }
}
impl error::Error for EssentialLack {
    fn cause(&self) -> Option<&error::Error> { None }

    fn description(&self) -> &str { "essential file does not exist" }
}

#[derive(Debug)]
pub enum UsageKind {
    Nothing,
    Init,
    Config,
    Ignore,
    Sweep,
    Burn,
    Start,
    End,
    Destroy,
}
impl Display for UsageKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            UsageKind::Nothing => format!(
r#"Usage:
    {} <command> [--help|-h]

Command:
    init    # Register current directory.
    config  # Set config's parameters.
    ignore  # Set ignored files.
    sweep   # Sweep files in current directory.
    burn    # Burn sweeped files.
    start   # Start "{}".
    end     # End "{}".
    destroy # Destroy "{}"."#,
                ME,
                ME,
                ME,
                ME,
            ),
            UsageKind::Init    => "init!"   .to_string(),
            UsageKind::Config  => "config!" .to_string(),
            UsageKind::Ignore  => "ignore!" .to_string(),
            UsageKind::Sweep   => "sweep!"  .to_string(),
            UsageKind::Burn    => "burn!"   .to_string(),
            UsageKind::Start   => "start!"  .to_string(),
            UsageKind::End     => "end!"    .to_string(),
            UsageKind::Destroy => "destroy!".to_string(),
        })
    }
}

#[derive(Debug)]
pub struct Usage {
    pub kind: UsageKind,
}
impl Display for Usage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}
impl error::Error for Usage {
    fn cause(&self) -> Option<&error::Error> { None }

    fn description(&self) -> &str { "show usage" }
}

#[derive(Debug)]
pub struct CannotHappenError;
impl Display for CannotHappenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cannot happen")
    }
}
impl error::Error for CannotHappenError {
    fn cause(&self) -> Option<&error::Error> { None }

    fn description(&self) -> &str { "cannot happen" }
}

#[derive(Debug)]
pub enum ConfigErrorKind {
    NotFoundBurnAfter,
    BurnAfter,
    NumOfBurnAfter,
    UnitOfBurnAfter,
}
impl Display for ConfigErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            ConfigErrorKind::NotFoundBurnAfter => r#"Please set [burn]after param"#,
            ConfigErrorKind::BurnAfter         => r#"Invalid "[burn]after" param"#,
            ConfigErrorKind::NumOfBurnAfter    => r#"Please set positive number as "[burn]after""#,
            ConfigErrorKind::UnitOfBurnAfter   => r#"Please set "day" or "week" as "[burn]after""#,
        })
    }
}

#[derive(Debug)]
pub struct ConfigError {
    pub kind: ConfigErrorKind,
}
impl Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}
impl error::Error for ConfigError {
    fn cause(&self) -> Option<&error::Error> { None }

    fn description(&self) -> &str { "invalid params" }
}
<commit_msg>[Refactoring] Use directly "std::error::Error"<commit_after>extern crate regex;
extern crate toml;

use constant::*;
use self::regex::Error as RegexError;
use std::num::ParseIntError;
use std::error::Error;
use std::fmt::{self, Display};
use std::io::Error as IoError;

#[derive(Debug)]
pub enum CliError {
    CannotHappen(CannotHappenError),
    Config(ConfigError),
    Io(IoError),
    ParseInt(ParseIntError),
    ParseToml(toml::ParserError),
    Regex(RegexError),
    RunningPlace(RunningPlaceError),
}
impl Display for CliError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CliError::CannotHappen(ref e) => e.fmt(f),
            CliError::Config(ref e)       => e.fmt(f),
            CliError::Io(ref e)           => e.fmt(f),
            CliError::ParseInt(ref e)     => e.fmt(f),
            CliError::ParseToml(ref e)    => e.fmt(f),
            CliError::Regex(ref e)        => e.fmt(f),
            CliError::RunningPlace(ref e) => e.fmt(f),
        }
    }
}
impl Error for CliError {
    fn cause(&self) -> Option<&Error> {
        match *self {
            CliError::CannotHappen(ref e) => Some(e),
            CliError::Config(ref e)       => Some(e),
            CliError::Io(ref e)           => Some(e),
            CliError::ParseInt(ref e)     => Some(e),
            CliError::ParseToml(ref e)    => Some(e),
            CliError::Regex(ref e)        => Some(e),
            CliError::RunningPlace(ref e) => Some(e),
        }
    }

    fn description(&self) -> &str {
        match *self {
            CliError::CannotHappen(ref e) => e.description(),
            CliError::Config(ref e)       => e.description(),
            CliError::Io(ref e)           => e.description(),
            CliError::ParseInt(ref e)     => e.description(),
            CliError::ParseToml(ref e)    => e.description(),
            CliError::Regex(ref e)        => e.description(),
            CliError::RunningPlace(ref e) => e.description(),
        }
    }
}
impl From<CannotHappenError> for CliError {
    fn from(e: CannotHappenError) -> CliError {
        CliError::CannotHappen(e)
    }
}
impl From<ConfigError> for CliError {
    fn from(e: ConfigError) -> CliError {
        CliError::Config(e)
    }
}
impl From<IoError> for CliError {
    fn from(e: IoError) -> CliError {
        CliError::Io(e)
    }
}
impl From<ParseIntError> for CliError {
    fn from(e: ParseIntError) -> CliError {
        CliError::ParseInt(e)
    }
}
impl From<toml::ParserError> for CliError {
    fn from(e: toml::ParserError) -> CliError {
        CliError::ParseToml(e)
    }
}
impl From<RegexError> for CliError {
    fn from(e: RegexError) -> CliError {
        CliError::Regex(e)
    }
}
impl From<RunningPlaceError> for CliError {
    fn from(e: RunningPlaceError) -> CliError {
        CliError::RunningPlace(e)
    }
}

#[derive(Debug)]
pub struct RunningPlaceError {
    pub dir: String,
}
impl Display for RunningPlaceError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cannot run in \"{}\" directory", self.dir)
    }
}
impl Error for RunningPlaceError {
    fn cause(&self) -> Option<&Error> { None }

    fn description(&self) -> &str { "cannot run in banned directories" }
}

#[derive(Debug)]
pub enum EssentialKind {
    WorkingDir,
    StorageDir,
    ConfigFile,
    IgnoreFile,
}
impl Display for EssentialKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            EssentialKind::WorkingDir => format!("{} directory", WORKING_DIR_NAME),
            EssentialKind::StorageDir => format!("{} directory", STORAGE_DIR_NAME),
            EssentialKind::ConfigFile => format!("{} file"     , CONFIG_FILE_NAME),
            EssentialKind::IgnoreFile => format!("{} file"     , IGNORE_FILE_NAME),
        })
    }
}

#[derive(Debug)]
pub struct EssentialLack {
    pub what: EssentialKind,
}
impl Display for EssentialLack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} does not exist. Please use \"init\" command", self.what)
    }
}
impl Error for EssentialLack {
    fn cause(&self) -> Option<&Error> { None }

    fn description(&self) -> &str { "essential file does not exist" }
}

#[derive(Debug)]
pub enum UsageKind {
    Nothing,
    Init,
    Config,
    Ignore,
    Sweep,
    Burn,
    Start,
    End,
    Destroy,
}
impl Display for UsageKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            UsageKind::Nothing => format!(
r#"Usage:
    {} <command> [--help|-h]

Command:
    init    # Register current directory.
    config  # Set config's parameters.
    ignore  # Set ignored files.
    sweep   # Sweep files in current directory.
    burn    # Burn sweeped files.
    start   # Start "{}".
    end     # End "{}".
    destroy # Destroy "{}"."#,
                ME,
                ME,
                ME,
                ME,
            ),
            UsageKind::Init    => "init!"   .to_string(),
            UsageKind::Config  => "config!" .to_string(),
            UsageKind::Ignore  => "ignore!" .to_string(),
            UsageKind::Sweep   => "sweep!"  .to_string(),
            UsageKind::Burn    => "burn!"   .to_string(),
            UsageKind::Start   => "start!"  .to_string(),
            UsageKind::End     => "end!"    .to_string(),
            UsageKind::Destroy => "destroy!".to_string(),
        })
    }
}

#[derive(Debug)]
pub struct Usage {
    pub kind: UsageKind,
}
impl Display for Usage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}
impl Error for Usage {
    fn cause(&self) -> Option<&Error> { None }

    fn description(&self) -> &str { "show usage" }
}

#[derive(Debug)]
pub struct CannotHappenError;
impl Display for CannotHappenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cannot happen")
    }
}
impl Error for CannotHappenError {
    fn cause(&self) -> Option<&Error> { None }

    fn description(&self) -> &str { "cannot happen" }
}

#[derive(Debug)]
pub enum ConfigErrorKind {
    NotFoundBurnAfter,
    BurnAfter,
    NumOfBurnAfter,
    UnitOfBurnAfter,
}
impl Display for ConfigErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            ConfigErrorKind::NotFoundBurnAfter => r#"Please set [burn]after param"#,
            ConfigErrorKind::BurnAfter         => r#"Invalid "[burn]after" param"#,
            ConfigErrorKind::NumOfBurnAfter    => r#"Please set positive number as "[burn]after""#,
            ConfigErrorKind::UnitOfBurnAfter   => r#"Please set "day" or "week" as "[burn]after""#,
        })
    }
}

#[derive(Debug)]
pub struct ConfigError {
    pub kind: ConfigErrorKind,
}
impl Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}
impl Error for ConfigError {
    fn cause(&self) -> Option<&Error> { None }

    fn description(&self) -> &str { "invalid params" }
}
<|endoftext|>


<commit_before>//! # Flash drivers for the LM4F120H5QR
//!
//! The LM4F120 has a flash memory controller which supports:
//!
//! * 1 KiB page erase
//! * 32-bit word write
//! * 32 word (128 byte) block write

// ****************************************************************************
//
// Imports
//
// ****************************************************************************

use cortex_m::asm::nop;
use tm4c123x;

// ****************************************************************************
//
// Public Types
//
// ****************************************************************************

/// Represents an address in Flash memory. As flash memory starts at
/// 0x0000_0000, flash addresses are equal to CPU physical addresses.
pub struct FlashAddress(pub u32);

#[derive(Debug)]
pub enum Error {
    /// A hardware error occured. The value is the
    /// contents of the FCRIS register.
    HardwareError(u32),
    /// Given address did not have the alignment
    /// specified here (as a u32).
    BadAlignment(u32),
    /// Buffer given was too long, it should be the length given.
    BufferTooLong(usize),
    /// Buffer given was too short, it should be the length given.
    BufferTooShort(usize),
}

// ****************************************************************************
//
// Public Data
//
// ****************************************************************************

// None

// ****************************************************************************
//
// Private Types
//
// ****************************************************************************

// None

// ****************************************************************************
//
// Private Data
//
// ****************************************************************************

// const PAGE_LENGTH: usize = 128;
// const FLASH_KEY: u16 = 0x71D5;
const FLASH_KEY: u16 = 0xA442;

// ****************************************************************************
//
// Public Functions
//
// ****************************************************************************

/// Erase a 1 KiB page of flash. The given address must be on 1 KiB boundary
/// (i.e. a multiple of 1024).
pub fn erase_page(address: FlashAddress) -> Result<(), Error> {
    if (address.0 & 1023) != 0 {
        return Err(Error::BadAlignment(1024));
    }

    unsafe {
        let reg = get_registers();
        clear_bits(reg);
        // Write the page address to the FMA register
        reg.fma.write(|w| w.offset().bits(address.0));
        // Write the flash memory key and the erase bit
        reg.fmc.modify(
            |_, w| w.erase().bit(true).wrkey().bits(FLASH_KEY),
        );
        // Poll the FMC register until the ERASE bit is cleared
        while reg.fmc.read().erase().bit() {
            nop();
        }
        get_status(reg)
    }
}

/// Write a 32-bit value to flash at the given address. The address
/// must be on a 4-byte boundary (i.e. a multiple of 4).
pub fn write_word(address: FlashAddress, word: u32) -> Result<(), Error> {
    if (address.0 & 3) != 0 {
        return Err(Error::BadAlignment(4));
    }

    unsafe {
        let reg = get_registers();
        clear_bits(reg);
        // Write the target address to the FMA register
        reg.fma.write(|w| w.offset().bits(address.0));
        // Write the data to the FMD register
        reg.fmd.write(|w| w.data().bits(word));
        // Write the flash memory key and the write bit
        reg.fmc.modify(
            |_, w| w.write().bit(true).wrkey().bits(FLASH_KEY),
        );
        // Poll the FMC register until the WRITE bit is cleared
        while reg.fmc.read().write().bit() {
            nop();
        }
        get_status(reg)
    }
}

/// Write a 128 byte buffer to flash at the given address. The address
/// must be on a 128-byte boundary (i.e. a multiple of 128) and the
/// buffer must have a length equal to 128. Pad with 0xFF if
/// your data is short.
// pub fn write_page(address: FlashAddress, data: &[u8]) -> Result<(), Error> {
//     if (address.0 & 127) != 0 {
//         return Err(Error::BadAlignment(128));
//     } else if data.len() < PAGE_LENGTH {
//         return Err(Error::BufferTooShort(PAGE_LENGTH));
//     } else if data.len() > PAGE_LENGTH {
//         return Err(Error::BufferTooLong(PAGE_LENGTH));
//     }

//     unsafe {
//         let reg = get_registers();
//         // Write the data to the FMBx registers
//         // I'm not sure the tmc4c123x crate has these correct, as it only
//         // exposes a single fmbn, not 32 fmbx registers.
//         reg.fmd.write(|w| w.data().bits(word));
//         // Write the target address to the FMA register
//         reg.fma.write(|w| w.offset().bits(address.0));
//         // Write the flash memory key and the write bit
//         reg.fmc.modify(|_, w| w.write().bit(true).wrkey().bits(FLASH_KEY));
//         // Poll the FMC register until the WRITE bit is cleared
//         while reg.fmc.read().write().bit() {
//             nop();
//         }
//     }

//     Ok(())

// }

// ****************************************************************************
//
// Private Functions
//
// ****************************************************************************
/// Get a reference to the UART control register struct in the chip.
unsafe fn get_registers() -> &'static tm4c123x::flash_ctrl::RegisterBlock {
    &*tm4c123x::FLASH_CTRL.get()
}

/// Clear all the status bits
fn clear_bits(reg: &'static tm4c123x::flash_ctrl::RegisterBlock) {
    reg.fcmisc.write(|w| {
        w.amisc()
            .bit(true)
            .voltmisc()
            .bit(true)
            .invdmisc()
            .bit(true)
            .progmisc()
            .bit(true)
            .ermisc()
            .bit(true)
    });
}

/// Get pass/fail from the controller
fn get_status(reg: &'static tm4c123x::flash_ctrl::RegisterBlock) -> Result<(), Error> {
    let fcris = reg.fcris.read();
    if fcris.aris().bit() || fcris.voltris().bit() || fcris.invdris().bit() ||
        fcris.progris().bit() || fcris.erris().bit()
    {
        Err(Error::HardwareError(fcris.bits()))
    } else {
        Ok(())
    }
}

// ****************************************************************************
//
// End Of File
//
// ****************************************************************************
<commit_msg>Added function to check protection status of flash.<commit_after>//! # Flash drivers for the LM4F120H5QR
//!
//! The LM4F120 has a flash memory controller which supports:
//!
//! * 1 KiB page erase
//! * 32-bit word write
//! * 32 word (128 byte) block write

// ****************************************************************************
//
// Imports
//
// ****************************************************************************

use cortex_m::asm::nop;
use tm4c123x;

// ****************************************************************************
//
// Public Types
//
// ****************************************************************************

/// Represents an address in Flash memory. As flash memory starts at
/// 0x0000_0000, flash addresses are equal to CPU physical addresses.
pub struct FlashAddress(pub u32);

#[derive(Debug)]
pub enum Error {
    /// A hardware error occured. The value is the
    /// contents of the FCRIS register.
    HardwareError(u32),
    /// Given address did not have the alignment
    /// specified here (as a u32).
    BadAlignment(u32),
    /// Buffer given was too long, it should be the length given.
    BufferTooLong(usize),
    /// Buffer given was too short, it should be the length given.
    BufferTooShort(usize),
}

#[derive(Debug)]
pub enum ProtectMode {
    ExecuteOnly,
    ReadOnly,
    ReadWrite
}

// ****************************************************************************
//
// Public Data
//
// ****************************************************************************

// None

// ****************************************************************************
//
// Private Types
//
// ****************************************************************************

// None

// ****************************************************************************
//
// Private Data
//
// ****************************************************************************

// const PAGE_LENGTH: usize = 128;
// const FLASH_KEY: u16 = 0x71D5;
const FLASH_KEY: u16 = 0xA442;

const FLASH_PROTECT_BLOCK_SIZE: u32 = 2048;
const FLASH_PROTECT_NUM_BLOCKS: u32 = 32;
const FLASH_PROTECT_BANK_SIZE: u32 = FLASH_PROTECT_BLOCK_SIZE * FLASH_PROTECT_NUM_BLOCKS;
const FLASH_PROTECT_NUM_BANKS: u32 = 4;

// ****************************************************************************
//
// Public Functions
//
// ****************************************************************************

/// Erase a 1 KiB page of flash. The given address must be on 1 KiB boundary
/// (i.e. a multiple of 1024).
pub fn erase_page(address: FlashAddress) -> Result<(), Error> {
    if (address.0 & 1023) != 0 {
        return Err(Error::BadAlignment(1024));
    }

    unsafe {
        let reg = get_registers();
        clear_bits(reg);
        // Write the page address to the FMA register
        reg.fma.write(|w| w.offset().bits(address.0));
        // Write the flash memory key and the erase bit
        reg.fmc.modify(
            |_, w| w.erase().bit(true).wrkey().bits(FLASH_KEY),
        );
        // Poll the FMC register until the ERASE bit is cleared
        while reg.fmc.read().erase().bit() {
            nop();
        }
        get_status(reg)
    }
}

/// Write a 32-bit value to flash at the given address. The address
/// must be on a 4-byte boundary (i.e. a multiple of 4).
pub fn write_word(address: FlashAddress, word: u32) -> Result<(), Error> {
    if (address.0 & 3) != 0 {
        return Err(Error::BadAlignment(4));
    }

    unsafe {
        let reg = get_registers();
        clear_bits(reg);
        // Write the target address to the FMA register
        reg.fma.write(|w| w.offset().bits(address.0));
        // Write the data to the FMD register
        reg.fmd.write(|w| w.data().bits(word));
        // Write the flash memory key and the write bit
        reg.fmc.modify(
            |_, w| w.write().bit(true).wrkey().bits(FLASH_KEY),
        );
        // Poll the FMC register until the WRITE bit is cleared
        while reg.fmc.read().write().bit() {
            nop();
        }
        get_status(reg)
    }
}

/// Get the protection status of the block containing the given address.
pub fn get_protection(address: FlashAddress) -> ProtectMode {
    let bank = (address.0 / FLASH_PROTECT_BANK_SIZE) % FLASH_PROTECT_NUM_BANKS;
    let bank_offset = address.0 & (FLASH_PROTECT_BANK_SIZE - 1);
    let block = bank_offset / FLASH_PROTECT_BLOCK_SIZE;
    let reg = unsafe {
        get_registers()
    };

    let read_bits = match bank {
        0 => reg.fmpre0.read().bits(),
        1 => reg.fmpre1.read().bits(),
        2 => reg.fmpre2.read().bits(),
        3 => reg.fmpre3.read().bits(),
        _ => unreachable!(),
    };
    let exec_bits = match bank {
        0 => reg.fmppe0.read().bits(),
        1 => reg.fmppe1.read().bits(),
        2 => reg.fmppe2.read().bits(),
        3 => reg.fmppe3.read().bits(),
        _ => unreachable!(),
    };
    let read_enabled = (read_bits & (1 << block)) == 1;
    let exec_enabled = (exec_bits & (1 << block)) == 1;

    if read_enabled {
        if exec_enabled {
            ProtectMode::ReadWrite
        } else {
            ProtectMode::ReadOnly
        }
    } else {
        ProtectMode::ExecuteOnly
    }

}

/// Write a 128 byte buffer to flash at the given address. The address
/// must be on a 128-byte boundary (i.e. a multiple of 128) and the
/// buffer must have a length equal to 128. Pad with 0xFF if
/// your data is short.
// pub fn write_page(address: FlashAddress, data: &[u8]) -> Result<(), Error> {
//     if (address.0 & 127) != 0 {
//         return Err(Error::BadAlignment(128));
//     } else if data.len() < PAGE_LENGTH {
//         return Err(Error::BufferTooShort(PAGE_LENGTH));
//     } else if data.len() > PAGE_LENGTH {
//         return Err(Error::BufferTooLong(PAGE_LENGTH));
//     }

//     unsafe {
//         let reg = get_registers();
//         // Write the data to the FMBx registers
//         // I'm not sure the tmc4c123x crate has these correct, as it only
//         // exposes a single fmbn, not 32 fmbx registers.
//         reg.fmd.write(|w| w.data().bits(word));
//         // Write the target address to the FMA register
//         reg.fma.write(|w| w.offset().bits(address.0));
//         // Write the flash memory key and the write bit
//         reg.fmc.modify(|_, w| w.write().bit(true).wrkey().bits(FLASH_KEY));
//         // Poll the FMC register until the WRITE bit is cleared
//         while reg.fmc.read().write().bit() {
//             nop();
//         }
//     }

//     Ok(())

// }

// ****************************************************************************
//
// Private Functions
//
// ****************************************************************************
/// Get a reference to the UART control register struct in the chip.
unsafe fn get_registers() -> &'static tm4c123x::flash_ctrl::RegisterBlock {
    &*tm4c123x::FLASH_CTRL.get()
}

/// Clear all the status bits
fn clear_bits(reg: &'static tm4c123x::flash_ctrl::RegisterBlock) {
    reg.fcmisc.write(|w| {
        w.amisc()
            .bit(true)
            .voltmisc()
            .bit(true)
            .invdmisc()
            .bit(true)
            .progmisc()
            .bit(true)
            .ermisc()
            .bit(true)
    });
}

/// Get pass/fail from the controller
fn get_status(reg: &'static tm4c123x::flash_ctrl::RegisterBlock) -> Result<(), Error> {
    let fcris = reg.fcris.read();
    if fcris.aris().bit() || fcris.voltris().bit() || fcris.invdris().bit() ||
        fcris.progris().bit() || fcris.erris().bit()
    {
        Err(Error::HardwareError(fcris.bits()))
    } else {
        Ok(())
    }
}

// ****************************************************************************
//
// End Of File
//
// ****************************************************************************
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{self, Write};

use config::Config;
use dep::{Dep, DepKind};
use error::CliResult;

pub type Nd = usize;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ed(Nd, Nd);

impl Ed {
    pub fn label<W: Write>(&self, w: &mut W, dg: &DepGraph) -> io::Result<()> {
        use dep::DepKind::{Optional, Dev, Build};
        let parent = dg.get(self.0).unwrap().kind;
        let child = dg.get(self.1).unwrap().kind;

        match (parent, child) {
            (Build, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.build_lines),
            (Build, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Build, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Dev, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            _               => writeln!(w, "[label=\"\"];")
        }
    }
}

impl fmt::Display for Ed {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let &Ed(il, ir) = self;
        write!(f, "N{} -> N{}", il, ir)
    }
}

#[derive(Debug)]
pub struct DepGraph<'c, 'o>
    where 'o: 'c
{
    pub nodes: Vec<Dep>,
    pub edges: Vec<Ed>,
    pub root: String,
    cfg: &'c Config<'o>,
}

impl<'c, 'o> DepGraph<'c, 'o> {
    pub fn with_root(root: Dep, cfg: &'c Config<'o>) -> Self {
        DepGraph {
            root: root.name.clone(),
            nodes: vec![root],
            edges: vec![],
            cfg: cfg,
        }
    }

    pub fn add_child(&mut self, parent: usize, d: &str, k: Option<DepKind>) -> usize {
        let idr = self.find_or_add(d, k.unwrap_or(DepKind::Unk));
        self.edges.push(Ed(parent, idr));
        idr
    }

    pub fn get(&self, id: usize) -> Option<&Dep> {
        if id < self.nodes.len() {
            return Some(&self.nodes[id]);
        }
        None
    }

    pub fn update_style(&mut self, name: &str, kind: DepKind) {
        if let Some(id) = self.find(name) {
            if let Some(dep) = self.get_mut(id) {
                dep.kind = kind;
            }
        }
    }

    pub fn update_ver<S: Into<String>>(&mut self, name: &str, ver: S) {
        if let Some(id) = self.find(name) {
            if let Some(dep) = self.get_mut(id) {
                dep.ver(ver);
            }
        }
    }

    pub fn remove(&mut self, name: &str) {
        if let Some(id) = self.find(name) {
            debugln!("remove; name={}; index={}", name, id);
            self.nodes.remove(id);
            self.shift_edges_after_node(id);
        }
    }

    fn shift_edges_after_node(&mut self, id: usize) {
        enum Side {
            Left,
            Right,
        }
        let mut to_upd = vec![];
        for c in id..self.nodes.len() {
            for (eid, &Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == c { to_upd.push((eid, Side::Left, c-1)); }
                if idr == c { to_upd.push((eid, Side::Right, c-1)); }
            }
        }
        for (eid, side, new) in to_upd {
            match side {
                Side::Left => self.edges[eid].0 = new,
                Side::Right => self.edges[eid].1 = new,
            }
        }
    }

    pub fn remove_orphans(&mut self) {
        let len = self.nodes.len();
        loop {
            let mut to_rem = None;
            for (eid, &Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl > len || idr > len {
                    to_rem = Some(eid);
                }
            }
            if let Some(id) = to_rem {
                self.edges.remove(id);
                continue;
            }
            break;
        }
        debugln!("remove_orphans; nodes={:?}", self.nodes);
        loop {
            let mut removed = false;
            let mut used = vec![false; self.nodes.len()];
            used[0] = true;
            for &Ed(_, idr) in &self.edges {
                debugln!("remove_orphans; idr={}", idr);
                used[idr] = true;
            }
            debugln!("remove_orphans; unsued_nodes={:?}", used);

            for (id, &u) in used.iter().enumerate() {
                if !u {
                    debugln!("remove_orphans; removing={}", id);
                    self.nodes.remove(id);

                    // Remove edges originating from the removed node
                    self.edges.retain(|&Ed(origin,_)| origin != id);
                    // Adjust edges to match the new node indexes
                    for edge in self.edges.iter_mut() {
                        if edge.0 > id {
                            edge.0 -= 1;
                        }
                        if edge.1 > id {
                            edge.1 -= 1;
                        }
                    }
                    removed = true;
                    break;
                }
            }
            if !removed {
                break;
            }
        }
    }

    fn remove_self_pointing(&mut self) {
        loop {
            let mut found = false;
            let mut self_p = vec![false; self.edges.len()];
            for (eid ,&Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == idr {
                    found = true;
                    self_p[eid] = true;
                    break;
                }
            }
            debugln!("remove_self_pointing; self_pointing={:?}", self_p);

            for (id, &u) in self_p.iter().enumerate() {
                if u {
                    debugln!("remove_self_pointing; removing={}", id);
                    self.edges.remove(id);
                    break;
                }
            }
            if !found {
                break;
            }
        }
    }

    pub fn get_mut(&mut self, id: usize) -> Option<&mut Dep> {
        if id < self.nodes.len() {
            return Some(&mut self.nodes[id]);
        }
        None
    }

    pub fn find(&self, name: &str) -> Option<usize> {
        for (i, d) in self.nodes.iter().enumerate() {
            if d.name == name {
                return Some(i);
            }
        }
        None
    }

    pub fn find_or_add(&mut self, new: &str, k: DepKind) -> usize {
        for (i, d) in self.nodes.iter().enumerate() {
            if d.name == new {
                return i;
            }
        }
        self.nodes.push(Dep::with_kind(new.to_owned(), k));
        self.nodes.len() - 1
    }

    pub fn render_to<W: Write>(mut self, output: &mut W) -> CliResult<()> {
        debugln!("exec=render_to;");
        self.edges.sort();
        self.edges.dedup();
        self.remove_orphans();
        self.remove_self_pointing();
        debugln!("dg={:#?}", self);
        try!(writeln!(output, "{}", "digraph dependencies {"));
        for (i, dep) in self.nodes.iter().enumerate() {
            try!(write!(output, "\tN{}", i));
            try!(dep.label(output, self.cfg));
        }
        for ed in &self.edges {
            try!(write!(output, "\t{}", ed));
            try!(ed.label(output, &self));
        }
        try!(writeln!(output, "{}", "}"));
        Ok(())
    }

}
<commit_msg>remove_orphans: fixed bound condition for removing an edge<commit_after>use std::fmt;
use std::io::{self, Write};

use config::Config;
use dep::{Dep, DepKind};
use error::CliResult;

pub type Nd = usize;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ed(Nd, Nd);

impl Ed {
    pub fn label<W: Write>(&self, w: &mut W, dg: &DepGraph) -> io::Result<()> {
        use dep::DepKind::{Optional, Dev, Build};
        let parent = dg.get(self.0).unwrap().kind;
        let child = dg.get(self.1).unwrap().kind;

        match (parent, child) {
            (Build, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.build_lines),
            (Build, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Build, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Dev, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            _               => writeln!(w, "[label=\"\"];")
        }
    }
}

impl fmt::Display for Ed {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let &Ed(il, ir) = self;
        write!(f, "N{} -> N{}", il, ir)
    }
}

#[derive(Debug)]
pub struct DepGraph<'c, 'o>
    where 'o: 'c
{
    pub nodes: Vec<Dep>,
    pub edges: Vec<Ed>,
    pub root: String,
    cfg: &'c Config<'o>,
}

impl<'c, 'o> DepGraph<'c, 'o> {
    pub fn with_root(root: Dep, cfg: &'c Config<'o>) -> Self {
        DepGraph {
            root: root.name.clone(),
            nodes: vec![root],
            edges: vec![],
            cfg: cfg,
        }
    }

    pub fn add_child(&mut self, parent: usize, d: &str, k: Option<DepKind>) -> usize {
        let idr = self.find_or_add(d, k.unwrap_or(DepKind::Unk));
        self.edges.push(Ed(parent, idr));
        idr
    }

    pub fn get(&self, id: usize) -> Option<&Dep> {
        if id < self.nodes.len() {
            return Some(&self.nodes[id]);
        }
        None
    }

    pub fn update_style(&mut self, name: &str, kind: DepKind) {
        if let Some(id) = self.find(name) {
            if let Some(dep) = self.get_mut(id) {
                dep.kind = kind;
            }
        }
    }

    pub fn update_ver<S: Into<String>>(&mut self, name: &str, ver: S) {
        if let Some(id) = self.find(name) {
            if let Some(dep) = self.get_mut(id) {
                dep.ver(ver);
            }
        }
    }

    pub fn remove(&mut self, name: &str) {
        if let Some(id) = self.find(name) {
            debugln!("remove; name={}; index={}", name, id);
            self.nodes.remove(id);
            self.shift_edges_after_node(id);
        }
    }

    fn shift_edges_after_node(&mut self, id: usize) {
        enum Side {
            Left,
            Right,
        }
        let mut to_upd = vec![];
        for c in id..self.nodes.len() {
            for (eid, &Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == c { to_upd.push((eid, Side::Left, c-1)); }
                if idr == c { to_upd.push((eid, Side::Right, c-1)); }
            }
        }
        for (eid, side, new) in to_upd {
            match side {
                Side::Left => self.edges[eid].0 = new,
                Side::Right => self.edges[eid].1 = new,
            }
        }
    }

    pub fn remove_orphans(&mut self) {
        let len = self.nodes.len();
        self.edges.retain(|&Ed(idl,idr)| idl < len && idr < len);
        debugln!("remove_orphans; nodes={:?}", self.nodes);
        loop {
            let mut removed = false;
            let mut used = vec![false; self.nodes.len()];
            used[0] = true;
            for &Ed(_, idr) in &self.edges {
                debugln!("remove_orphans; idr={}", idr);
                used[idr] = true;
            }
            debugln!("remove_orphans; unsued_nodes={:?}", used);

            for (id, &u) in used.iter().enumerate() {
                if !u {
                    debugln!("remove_orphans; removing={}", id);
                    self.nodes.remove(id);

                    // Remove edges originating from the removed node
                    self.edges.retain(|&Ed(origin,_)| origin != id);
                    // Adjust edges to match the new node indexes
                    for edge in self.edges.iter_mut() {
                        if edge.0 > id {
                            edge.0 -= 1;
                        }
                        if edge.1 > id {
                            edge.1 -= 1;
                        }
                    }
                    removed = true;
                    break;
                }
            }
            if !removed {
                break;
            }
        }
    }

    fn remove_self_pointing(&mut self) {
        loop {
            let mut found = false;
            let mut self_p = vec![false; self.edges.len()];
            for (eid ,&Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == idr {
                    found = true;
                    self_p[eid] = true;
                    break;
                }
            }
            debugln!("remove_self_pointing; self_pointing={:?}", self_p);

            for (id, &u) in self_p.iter().enumerate() {
                if u {
                    debugln!("remove_self_pointing; removing={}", id);
                    self.edges.remove(id);
                    break;
                }
            }
            if !found {
                break;
            }
        }
    }

    pub fn get_mut(&mut self, id: usize) -> Option<&mut Dep> {
        if id < self.nodes.len() {
            return Some(&mut self.nodes[id]);
        }
        None
    }

    pub fn find(&self, name: &str) -> Option<usize> {
        for (i, d) in self.nodes.iter().enumerate() {
            if d.name == name {
                return Some(i);
            }
        }
        None
    }

    pub fn find_or_add(&mut self, new: &str, k: DepKind) -> usize {
        for (i, d) in self.nodes.iter().enumerate() {
            if d.name == new {
                return i;
            }
        }
        self.nodes.push(Dep::with_kind(new.to_owned(), k));
        self.nodes.len() - 1
    }

    pub fn render_to<W: Write>(mut self, output: &mut W) -> CliResult<()> {
        debugln!("exec=render_to;");
        self.edges.sort();
        self.edges.dedup();
        self.remove_orphans();
        self.remove_self_pointing();
        debugln!("dg={:#?}", self);
        try!(writeln!(output, "{}", "digraph dependencies {"));
        for (i, dep) in self.nodes.iter().enumerate() {
            try!(write!(output, "\tN{}", i));
            try!(dep.label(output, self.cfg));
        }
        for ed in &self.edges {
            try!(write!(output, "\t{}", ed));
            try!(ed.label(output, &self));
        }
        try!(writeln!(output, "{}", "}"));
        Ok(())
    }

}
<|endoftext|>


<commit_before>// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate glob;

use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command};

use glob::{MatchOptions};

/// `libclang` directory patterns for FreeBSD and Linux.
const DIRECTORIES_LINUX: &[&str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib",
];

/// `libclang` directory patterns for OS X.
const DIRECTORIES_OSX: &[&str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// `libclang` directory patterns for Windows.
const DIRECTORIES_WINDOWS: &[&str] = &[
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\lib",
    "C:\\MSYS*\\MinGW*\\lib",
];

/// Executes the supplied console command, returning the `stdout` output if the command was
/// successfully executed.
fn run_command(command: &str, arguments: &[&str]) -> Option<String> {
    let output = Command::new(command).args(arguments).output().ok()?;
    Some(String::from_utf8_lossy(&output.stdout).into_owned())
}

/// Executes `llvm-config`, returning the `stdout` output if the command was successfully executed.
pub fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
    let command = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into());
    match run_command(&command, arguments) {
        Some(output) => Ok(output),
        None => Err(format!(
            "couldn't execute `llvm-config {}`, set the LLVM_CONFIG_PATH environment variable to a \
            path to a valid `llvm-config` executable",
            arguments.join(" "),
        )),
    }
}

/// Returns the paths to and the filenames of the files matching the supplied filename patterns in
/// the supplied directory.
fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    // Join the directory to the filename patterns to obtain the path patterns.
    let paths = filenames.iter().filter_map(|f| directory.join(f).to_str().map(ToOwned::to_owned));

    // Prevent wildcards from matching path separators.
    let mut options = MatchOptions::new();
    options.require_literal_separator = true;

    paths.flat_map(|p| {
        if let Ok(paths) = glob::glob_with(&p, &options) {
            paths.filter_map(Result::ok).collect()
        } else {
            vec![]
        }
    }).filter_map(|p| {
        let filename = p.file_name().and_then(|f| f.to_str())?;
        Some((directory.to_owned(), filename.into()))
    }).collect::<Vec<_>>()
}

/// Returns the paths to and the filenames of the files matching the supplied filename patterns in
/// the supplied directory, checking any relevant sibling directories.
fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    let mut results = search_directory(directory, filenames);

    // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory while
    // `libclang.lib` is usually found in the LLVM `lib` directory. To keep things
    // consistent with other platforms, only LLVM `lib` directories are included in the
    // backup search directory globs so we need to search the LLVM `bin` directory here.
    if cfg!(target_os="windows") && directory.ends_with("lib") {
        let sibling = directory.parent().unwrap().join("bin");
        results.extend(search_directory(&sibling, filenames).into_iter());
    }

    results
}

/// Returns the paths to and the filenames of the `libclang` static or dynamic libraries matching
/// the supplied filename patterns.
pub fn search_libclang_directories(files: &[String], variable: &str) -> Vec<(PathBuf, String)> {
    // Search the directory provided by the relevant environment variable.
    if let Ok(directory) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) {
        return search_directories(&directory, files);
    }

    let mut found = vec![];

    // Search the `bin` and `lib` directories in directory provided by `llvm-config --prefix`.
    if let Ok(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        found.extend(search_directories(&directory.join("bin"), files));
        found.extend(search_directories(&directory.join("lib"), files));
    }

    // Search the toolchain directory in the directory provided by `xcode-select --print-path`.
    if cfg!(target_os="macos") {
        if let Some(output) = run_command("xcode-select", &["--print-path"]) {
            let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
            let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib");
            found.extend(search_directories(&directory, files));
        }
    }

    // Search the directories provided by the `LD_LIBRARY_PATH` environment variable.
    if let Ok(path) = env::var("LD_LIBRARY_PATH") {
        for directory in path.split(':').map(Path::new) {
            found.extend(search_directories(&directory, files));
        }
    }

    // Determine the `libclang` directory patterns.
    let directories = if cfg!(any(target_os="freebsd", target_os="linux")) {
        DIRECTORIES_LINUX
    } else if cfg!(target_os="macos") {
        DIRECTORIES_OSX
    } else if cfg!(target_os="windows") {
        DIRECTORIES_WINDOWS
    } else {
        &[]
    };

    // Search the directories provided by the `libclang` directory patterns.
    let mut options = MatchOptions::new();
    options.case_sensitive = false;
    options.require_literal_separator = true;
    for directory in directories.iter().rev() {
        if let Ok(directories) = glob::glob_with(directory, &options) {
            for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) {
                found.extend(search_directories(&directory, files));
            }
        }
    }

    found
}
<commit_msg>Add lib64 path search on Linux (#88)<commit_after>// Copyright 2018 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate glob;

use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command};

use glob::{MatchOptions};

/// `libclang` directory patterns for FreeBSD and Linux.
const DIRECTORIES_LINUX: &[&str] = &[
    "/usr/lib*",
    "/usr/lib*/*",
    "/usr/lib*/*/*",
    "/usr/local/lib*",
    "/usr/local/lib*/*",
    "/usr/local/lib*/*/*",
    "/usr/local/llvm*/lib*",
];

/// `libclang` directory patterns for OS X.
const DIRECTORIES_OSX: &[&str] = &[
    "/usr/local/opt/llvm*/lib",
    "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
    "/Library/Developer/CommandLineTools/usr/lib",
    "/usr/local/opt/llvm*/lib/llvm*/lib",
];

/// `libclang` directory patterns for Windows.
const DIRECTORIES_WINDOWS: &[&str] = &[
    "C:\\LLVM\\lib",
    "C:\\Program Files*\\LLVM\\lib",
    "C:\\MSYS*\\MinGW*\\lib",
];

/// Executes the supplied console command, returning the `stdout` output if the command was
/// successfully executed.
fn run_command(command: &str, arguments: &[&str]) -> Option<String> {
    let output = Command::new(command).args(arguments).output().ok()?;
    Some(String::from_utf8_lossy(&output.stdout).into_owned())
}

/// Executes `llvm-config`, returning the `stdout` output if the command was successfully executed.
pub fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
    let command = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into());
    match run_command(&command, arguments) {
        Some(output) => Ok(output),
        None => Err(format!(
            "couldn't execute `llvm-config {}`, set the LLVM_CONFIG_PATH environment variable to a \
            path to a valid `llvm-config` executable",
            arguments.join(" "),
        )),
    }
}

/// Returns the paths to and the filenames of the files matching the supplied filename patterns in
/// the supplied directory.
fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    // Join the directory to the filename patterns to obtain the path patterns.
    let paths = filenames.iter().filter_map(|f| directory.join(f).to_str().map(ToOwned::to_owned));

    // Prevent wildcards from matching path separators.
    let mut options = MatchOptions::new();
    options.require_literal_separator = true;

    paths.flat_map(|p| {
        if let Ok(paths) = glob::glob_with(&p, &options) {
            paths.filter_map(Result::ok).collect()
        } else {
            vec![]
        }
    }).filter_map(|p| {
        let filename = p.file_name().and_then(|f| f.to_str())?;
        Some((directory.to_owned(), filename.into()))
    }).collect::<Vec<_>>()
}

/// Returns the paths to and the filenames of the files matching the supplied filename patterns in
/// the supplied directory, checking any relevant sibling directories.
fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> {
    let mut results = search_directory(directory, filenames);

    // On Windows, `libclang.dll` is usually found in the LLVM `bin` directory while
    // `libclang.lib` is usually found in the LLVM `lib` directory. To keep things
    // consistent with other platforms, only LLVM `lib` directories are included in the
    // backup search directory globs so we need to search the LLVM `bin` directory here.
    if cfg!(target_os="windows") && directory.ends_with("lib") {
        let sibling = directory.parent().unwrap().join("bin");
        results.extend(search_directory(&sibling, filenames).into_iter());
    }

    results
}

/// Returns the paths to and the filenames of the `libclang` static or dynamic libraries matching
/// the supplied filename patterns.
pub fn search_libclang_directories(files: &[String], variable: &str) -> Vec<(PathBuf, String)> {
    // Search the directory provided by the relevant environment variable.
    if let Ok(directory) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) {
        return search_directories(&directory, files);
    }

    let mut found = vec![];

    // Search the `bin` and `lib` directories in directory provided by `llvm-config --prefix`.
    if let Ok(output) = run_llvm_config(&["--prefix"]) {
        let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
        found.extend(search_directories(&directory.join("bin"), files));
        found.extend(search_directories(&directory.join("lib"), files));
        found.extend(search_directories(&directory.join("lib64"), files));
    }

    // Search the toolchain directory in the directory provided by `xcode-select --print-path`.
    if cfg!(target_os="macos") {
        if let Some(output) = run_command("xcode-select", &["--print-path"]) {
            let directory = Path::new(output.lines().next().unwrap()).to_path_buf();
            let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib");
            found.extend(search_directories(&directory, files));
        }
    }

    // Search the directories provided by the `LD_LIBRARY_PATH` environment variable.
    if let Ok(path) = env::var("LD_LIBRARY_PATH") {
        for directory in path.split(':').map(Path::new) {
            found.extend(search_directories(&directory, files));
        }
    }

    // Determine the `libclang` directory patterns.
    let directories = if cfg!(any(target_os="freebsd", target_os="linux")) {
        DIRECTORIES_LINUX
    } else if cfg!(target_os="macos") {
        DIRECTORIES_OSX
    } else if cfg!(target_os="windows") {
        DIRECTORIES_WINDOWS
    } else {
        &[]
    };

    // Search the directories provided by the `libclang` directory patterns.
    let mut options = MatchOptions::new();
    options.case_sensitive = false;
    options.require_literal_separator = true;
    for directory in directories.iter().rev() {
        if let Ok(directories) = glob::glob_with(directory, &options) {
            for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) {
                found.extend(search_directories(&directory, files));
            }
        }
    }

    found
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use common::Atom;

#[derive(Debug, PartialEq)]
enum LexerErrorCode {
    InvalidSyntax,
    TrailingCharacters,
    EOFWhileReadingToken,
}

#[derive(Debug, PartialEq)]
enum LexerError {
    SyntaxError {
        code: LexerErrorCode,
        line: usize,
        col: usize,
    }
}

#[derive(Debug, PartialEq)]
enum ParseAtomError {
    IncorrectSymbolName
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            _ => {
                match s.chars().next() {
                    Some(c) if !c.is_numeric() => {
                        Ok(Atom::Symbol(s.to_string()))
                    },
                    _ => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

#[derive(Debug, PartialEq)]
enum Token {
    Nil,
    Number(f64),
    String(String),
    Error(LexerError),
}

enum LexerState {
    Start,
    BeforeFinish,
    Finish,
}

pub struct Lexer<T> {
    reader: T,
    cur_char: Option<char>,
    line: usize,
    col: usize,
    state: LexerState
}

impl<T: Iterator<Item=char>> Iterator for Lexer<T> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        match self.state {
            LexerState::Finish => {
                None
            },
            LexerState::BeforeFinish => {
                self.read_whitespaces();
                self.cur_char
                    .and(Some(self.error_token(LexerErrorCode::TrailingCharacters)))
                    .or_else(|| { self.state = LexerState::Finish; None })
            },
            _ => {
                Some(self.read())
            }
        }
    }
}

impl<T: Iterator<Item=char>> Lexer<T> {
    fn new(reader: T) -> Lexer<T> {
        let mut l = Lexer {
            reader: reader,
            cur_char: None,
            line: 1,
            col: 0,
            state: LexerState::Start
        };
        l.bump();
        l
    }

    fn read(&mut self) -> Token {
        loop {
            self.read_whitespaces();
            match self.state {
                LexerState::Start => {
                    return self.read_start()
                },
                _ => {
                    return self.error_token(LexerErrorCode::InvalidSyntax)
                }
            }
        }
    }

    fn read_start(&mut self) -> Token {
        let token = self.read_token();
        self.state = match token {
            Token::Error(_) => {
                LexerState::Finish
            },
            _ => LexerState::BeforeFinish,
        };
        token
    }

    fn read_token(&mut self) -> Token {
        if let Some(c) = self.cur_char {
            match c {
                'n' => {
                    self.read_ident("il", Token::Nil)
                },
                '0' ... '9' | '-' => {
                    self.read_number()
                },
                '"' => {
                    self.read_string()
                },
                _ => {
                    self.error_token(LexerErrorCode::InvalidSyntax)
                }
            }
        } else {
            self.error_token(LexerErrorCode::EOFWhileReadingToken)
        }
    }

    fn read_ident(&mut self, ident: &str, token: Token) -> Token {
        if ident.chars().all(|c| { self.bump(); Some(c) == self.cur_char }) {
            self.bump();
            token
        } else {
            self.error_token(LexerErrorCode::InvalidSyntax)
        }
    }

    fn read_number(&mut self) -> Token {
        let mut neg = false;

        if let Some('-') = self.cur_char {
            self.bump();
            neg = true;
        }

        let mut accum = 0_f64;

        while let Some(c) = self.cur_char {
            match c {
                '0' ... '9' => {
                    let c_as_usize = (c as usize) - ('0' as usize);
                    accum *= 10_f64;
                    accum += c_as_usize as f64;
                    self.bump()
                },
                _ => {
                    break
                }
            }
        }

        if neg {
            accum *= -1_f64;
        }

        Token::Number(accum)
    }

    fn read_string(&mut self) -> Token {
        let mut res = String::new();

        self.bump();

        while let Some(c) = self.cur_char {
            match c {
                '"' => {
                    break
                },
                _ => {
                    res.push(c);
                }
            }
            self.bump();
        }

        self.bump();

        Token::String(res)
    }

    fn read_whitespaces(&mut self) {
        while let Some(ch) = self.cur_char {
            if [' ', '\n', '\r', '\t'].contains(&ch) {
                self.bump()
            } else {
                break
            }
        }
    }

    fn bump(&mut self) {
        self.cur_char = self.reader.next();
        if Some('\n') == self.cur_char {
            self.line += 1;
            self.col = 1;
        } else {
            self.col += 1;
        }
    }

    fn error_token(&mut self, ec: LexerErrorCode) -> Token {
        self.state = LexerState::Finish;
        Token::Error(LexerError::SyntaxError {
            code: ec,
            line: self.line,
            col: self.col
        })
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter(|s| { !s.is_empty() })
        .map(|s| { s.to_string() })
        .collect()
}

#[cfg(test)]
mod tests {
    use common::Atom::{self, Symbol, Number};
    use super::{Lexer, Token};
    use super::ParseAtomError::IncorrectSymbolName;
    use super::tokenize;

    #[test]
    fn test_read_integer_as_f64() {
        let mut lexer = Lexer::new("64".chars());
        assert_eq!(Some(Token::Number(64_f64)), lexer.next());
        assert_eq!(None, lexer.next());
    }

    #[test]
    fn test_read_string() {
        let s = "rust is beautiful".to_string();
        let actual_input = format!(r#""{}""#, s);
        let mut lexer = Lexer::new(actual_input.chars());
        assert_eq!(Some(Token::String(s)), lexer.next());
        assert_eq!(None, lexer.next());
    }

    #[test]
    fn test_read_float() {
        assert_eq!(Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_read_symbol() {
        assert_eq!(Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_read_incorrect_symbol_starting_with_digit() {
        assert_eq!(IncorrectSymbolName, "6name".parse::<Atom>().err().unwrap())
    }

    #[test]
    fn test_read_dense_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn test_read_sparse_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }
}
<commit_msg>Add test to lexer module for nil parsing case<commit_after>use std::str::FromStr;
use common::Atom;

#[derive(Debug, PartialEq)]
enum LexerErrorCode {
    InvalidSyntax,
    TrailingCharacters,
    EOFWhileReadingToken,
}

#[derive(Debug, PartialEq)]
enum LexerError {
    SyntaxError {
        code: LexerErrorCode,
        line: usize,
        col: usize,
    }
}

#[derive(Debug, PartialEq)]
enum ParseAtomError {
    IncorrectSymbolName
}

impl FromStr for Atom {
    type Err = ParseAtomError;

    fn from_str(s: &str) -> Result<Atom, ParseAtomError> {
        match s.parse::<f64>() {
            Ok(f) => {
                Ok(Atom::Number(f))
            },
            _ => {
                match s.chars().next() {
                    Some(c) if !c.is_numeric() => {
                        Ok(Atom::Symbol(s.to_string()))
                    },
                    _ => {
                        Err(ParseAtomError::IncorrectSymbolName)
                    }
                }
            }
        }
    }
}

#[derive(Debug, PartialEq)]
enum Token {
    Nil,
    Number(f64),
    String(String),
    Error(LexerError),
}

enum LexerState {
    Start,
    BeforeFinish,
    Finish,
}

pub struct Lexer<T> {
    reader: T,
    cur_char: Option<char>,
    line: usize,
    col: usize,
    state: LexerState
}

impl<T: Iterator<Item=char>> Iterator for Lexer<T> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        match self.state {
            LexerState::Finish => {
                None
            },
            LexerState::BeforeFinish => {
                self.read_whitespaces();
                self.cur_char
                    .and(Some(self.error_token(LexerErrorCode::TrailingCharacters)))
                    .or_else(|| { self.state = LexerState::Finish; None })
            },
            _ => {
                Some(self.read())
            }
        }
    }
}

impl<T: Iterator<Item=char>> Lexer<T> {
    fn new(reader: T) -> Lexer<T> {
        let mut l = Lexer {
            reader: reader,
            cur_char: None,
            line: 1,
            col: 0,
            state: LexerState::Start
        };
        l.bump();
        l
    }

    fn read(&mut self) -> Token {
        loop {
            self.read_whitespaces();
            match self.state {
                LexerState::Start => {
                    return self.read_start()
                },
                _ => {
                    return self.error_token(LexerErrorCode::InvalidSyntax)
                }
            }
        }
    }

    fn read_start(&mut self) -> Token {
        let token = self.read_token();
        self.state = match token {
            Token::Error(_) => {
                LexerState::Finish
            },
            _ => LexerState::BeforeFinish,
        };
        token
    }

    fn read_token(&mut self) -> Token {
        if let Some(c) = self.cur_char {
            match c {
                'n' => {
                    self.read_ident("il", Token::Nil)
                },
                '0' ... '9' | '-' => {
                    self.read_number()
                },
                '"' => {
                    self.read_string()
                },
                _ => {
                    self.error_token(LexerErrorCode::InvalidSyntax)
                }
            }
        } else {
            self.error_token(LexerErrorCode::EOFWhileReadingToken)
        }
    }

    fn read_ident(&mut self, ident: &str, token: Token) -> Token {
        if ident.chars().all(|c| { self.bump(); Some(c) == self.cur_char }) {
            self.bump();
            token
        } else {
            self.error_token(LexerErrorCode::InvalidSyntax)
        }
    }

    fn read_number(&mut self) -> Token {
        let mut neg = false;

        if let Some('-') = self.cur_char {
            neg = true;
            self.bump();
        }

        let mut accum = 0_f64;

        while let Some(c) = self.cur_char {
            match c {
                '0' ... '9' => {
                    let c_as_usize = (c as usize) - ('0' as usize);
                    accum *= 10_f64;
                    accum += c_as_usize as f64;
                    self.bump()
                },
                _ => {
                    break
                }
            }
        }

        if neg {
            accum *= -1_f64;
        }

        Token::Number(accum)
    }

    fn read_string(&mut self) -> Token {
        let mut res = String::new();

        self.bump();

        while let Some(c) = self.cur_char {
            match c {
                '"' => {
                    break
                },
                _ => {
                    res.push(c);
                }
            }
            self.bump();
        }

        self.bump();

        Token::String(res)
    }

    fn read_whitespaces(&mut self) {
        while let Some(ch) = self.cur_char {
            if [' ', '\n', '\r', '\t'].contains(&ch) {
                self.bump()
            } else {
                break
            }
        }
    }

    fn bump(&mut self) {
        self.cur_char = self.reader.next();
        if Some('\n') == self.cur_char {
            self.line += 1;
            self.col = 1;
        } else {
            self.col += 1;
        }
    }

    fn error_token(&mut self, ec: LexerErrorCode) -> Token {
        self.state = LexerState::Finish;
        Token::Error(LexerError::SyntaxError {
            code: ec,
            line: self.line,
            col: self.col
        })
    }
}

fn tokenize(s: &str) -> Vec<String> {
    s.replace("("," ( ")
        .replace(")", " ) ")
        .replace("\n", " ")
        .split(' ')
        .filter(|s| { !s.is_empty() })
        .map(|s| { s.to_string() })
        .collect()
}

#[cfg(test)]
mod tests {
    use common::Atom::{self, Symbol, Number};
    use super::{Lexer, Token};
    use super::ParseAtomError::IncorrectSymbolName;
    use super::tokenize;

    #[test]
    fn test_read_nil() {
        let mut lexer = Lexer::new("nil".chars());
        assert_eq!(Some(Token::Nil), lexer.next());
        assert_eq!(None, lexer.next());
    }

    #[test]
    fn test_read_integer_as_f64() {
        let mut lexer = Lexer::new("64".chars());
        assert_eq!(Some(Token::Number(64_f64)), lexer.next());
        assert_eq!(None, lexer.next());
    }

    #[test]
    fn test_read_string() {
        let s = "rust is beautiful".to_string();
        let actual_input = format!(r#""{}""#, s);
        let mut lexer = Lexer::new(actual_input.chars());
        assert_eq!(Some(Token::String(s)), lexer.next());
        assert_eq!(None, lexer.next());
    }

    #[test]
    fn test_read_float() {
        assert_eq!(Number(64.5), "64.5".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_read_symbol() {
        assert_eq!(Symbol("name".to_string()), "name".parse::<Atom>().ok().unwrap())
    }

    #[test]
    fn test_read_incorrect_symbol_starting_with_digit() {
        assert_eq!(IncorrectSymbolName, "6name".parse::<Atom>().err().unwrap())
    }

    #[test]
    fn test_read_dense_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize("(def a 1)"))
    }

    #[test]
    fn test_read_sparse_expression() {
        let expected_result = ["(", "def", "a", "1", ")"]
                                .iter()
                                .map(|s| { s.to_string() })
                                .collect();
        assert_eq!(expected_result, tokenize(" ( \n def a\n1)   \n"))
    }
}
<|endoftext|>


<commit_before>// Copyright © 2015 Sebastian Thiel
// Copyright © 2015 Andy Grover
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// “Software”), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Base a lexer for LVM2's text format on the more complex (hah) json format.
// This code is based on https://github.com/Byron/json-tools.

use std::io;
use std::io::Error;
use std::io::ErrorKind::Other;

use std::collections::btree_map::BTreeMap;

#[derive(Debug, PartialEq, Clone)]
pub enum Token<'a> {
    /// `{`
    CurlyOpen,
    /// `}`
    CurlyClose,

    /// `[`
    BracketOpen,
    /// `]`
    BracketClose,

    /// `=`
    Equals,
    /// `,`
    Comma,

    /// A string , like `"foo"`
    String(&'a[u8]),

    Ident(&'a[u8]),

    /// An unsigned integer number
    Number(u64),

    Comment(&'a[u8]),

    /// The type of the token could not be identified.
    /// Should be removed if this lexer is ever to be feature complete
    Invalid(u8),
}

impl<'a> AsRef<str> for Token<'a> {
    fn as_ref(&self) -> &str {
        match *self {
            Token::CurlyOpen => "{",
            Token::CurlyClose => "}",
            Token::BracketOpen => "[",
            Token::BracketClose => "]",
            Token::Equals => "=",
            Token::Comma => ",",

            Token::Invalid(c) => panic!("Cannot convert invalid Token {}", c),
            _ => panic!("Cannot convert variant Tokens"),
        }
    }
}

pub struct Lexer<'a> {
    chars: &'a[u8],
    next_byte: Option<u8>,
    cursor: usize,
}

impl<'a> Lexer<'a> {
    /// Returns a new Lexer from a given byte iterator.
    pub fn new(chars: &'a[u8]) -> Lexer<'a> {
        Lexer {
            chars: chars,
            next_byte: None,
            cursor: 0,
        }
    }

    fn put_back(&mut self, c: u8) {
        debug_assert!(self.next_byte.is_none());
        self.next_byte = Some(c);
        self.cursor -= 1;
    }

    fn next_byte(&mut self) -> Option<u8> {
        match self.next_byte.take() {
            Some(c) => {
                self.cursor += 1;
                Some(c)
            },
            None => {
                if self.cursor >= self.chars.len() {
                    None
                }
                else {
                    let res = self.chars[self.cursor];
                    self.cursor += 1;
                    Some(res)
                }
            }
        }
    }
}

// Identifies the state of the lexer
enum Mode {
    Main,

    // tells position where these modes were started
    String(usize),
    Ident(usize),
    Number(usize),
    Comment(usize),
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token<'a>;

    /// Lex the underlying byte stream to generate tokens
    fn next(&mut self) -> Option<Token<'a>> {

        let mut state = Mode::Main;

        while let Some(c) = self.next_byte() {
            match state {
                Mode::Main => {
                    match c {
                        b'{' => {
                            return Some(Token::CurlyOpen);
                        },
                        b'}' => {
                            return Some(Token::CurlyClose);
                        },
                        b'"' => {
                            state = Mode::String(self.cursor - 1);
                        },
                        b'a' ... b'z' | b'A' ... b'Z' | b'_' | b'.' => {
                            state = Mode::Ident(self.cursor - 1);
                        },
                        b'0' ... b'9' => {
                            state = Mode::Number(self.cursor - 1);
                        },
                        b'#' => {
                            state = Mode::Comment(self.cursor - 1);
                        },
                        b'[' => {
                            return Some(Token::BracketOpen);
                        },
                        b']' => {
                            return Some(Token::BracketClose);
                        },
                        b'=' => {
                            return Some(Token::Equals);
                        },
                        b',' => {
                            return Some(Token::Comma);
                        },
                        b' ' | b'\n' | b'\t' | b'\0' => {
                            // ignore whitespace
                        }
                        _ => {
                            return Some(Token::Invalid(c));
                        },
                    }
                }
                Mode::String(first) => {
                    match c {
                        b'"' => {
                            return Some(Token::String(
                                &self.chars[first+1..self.cursor-1]));
                        },
                        _ => {
                            continue;
                        }
                    }
                },
                Mode::Ident(first) => {
                    match c {
                        b'a' ... b'z' | b'A' ... b'Z' | b'0' ... b'9'
                            | b'_' | b'.' | b'-' => {
                                continue;
                            }
                        _ => {
                            self.put_back(c);
                            return Some(Token::Ident(
                                &self.chars[first..self.cursor]));
                        }
                    }
                },
                Mode::Number(first) => {
                    match c {
                        b'0' ... b'9' => {
                            continue;
                        },
                        _ => {
                            self.put_back(c);
                            let s = String::from_utf8_lossy(
                                &self.chars[first..self.cursor]).into_owned();
                            return Some(Token::Number(s.parse().unwrap()));
                        }
                    }
                }
                Mode::Comment(first) => {
                    match c {
                        b'\n' => {
                            self.put_back(c);
                            return Some(Token::Comment(
                                &self.chars[first..self.cursor]));
                        }
                        _ => {
                            continue;
                        }
                    }
                },
            }
        }

        None
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Entry {
    Number(u64),
    String(String),
    Dict(Box<BTreeMap<String, Entry>>),
    List(Box<Vec<Entry>>),
}

fn find_matching_token<'a>(buf: &[Token<'a>], begin: &Token<'a>, end: &Token<'a>) -> io::Result<usize> {
    let mut brace_count = 0;

    for (i, x) in buf.iter().enumerate() {
        match x {
            x if x == begin => {
                brace_count += 1;
            },
            x if x == end => {
                brace_count -= 1;
                if brace_count == 0 {
                    return Ok(i+1);
                }
            },
            _ => {},
        }
    }
    Err(Error::new(Other, "token mismatch"))
}

// lists can only contain strings and numbers, yay
pub fn get_list<'a>(tokens: &[Token<'a>]) -> io::Result<Vec<Entry>> {
    let mut v = Vec::new();

    assert_eq!(*tokens.first().unwrap(), Token::BracketOpen);
    assert_eq!(*tokens.last().unwrap(), Token::BracketClose);

    // Omit enclosing brackets
    for tok in &tokens[1..tokens.len()-1] {
        match *tok {
            Token::Number(x) => v.push(Entry::Number(x)),
            Token::String(x) => v.push(Entry::String(String::from_utf8_lossy(x).into_owned())),
            Token::Comma => { },
            _ => return Err(Error::new(
                Other, format!("Unexpected {:?}", *tok)))
        }
    }

    Ok(v)
}

fn get_hash<'a>(tokens: &[Token<'a>]) -> io::Result<BTreeMap<String, Entry>> {
    let mut ret: BTreeMap<String, Entry> = BTreeMap::new();

    assert_eq!(*tokens.first().unwrap(), Token::CurlyOpen);
    assert_eq!(*tokens.last().unwrap(), Token::CurlyClose);

    let mut cur = 1;

    while tokens[cur] != Token::CurlyClose {

        let ident = match tokens[cur] {
            Token::Ident(x) => String::from_utf8_lossy(x).into_owned(),
            Token::Comment(_) => {
                cur += 1;
                continue
            },
            _ => return Err(Error::new(
                Other, format!("Unexpected {:?}", tokens[cur])))
        };

        cur += 1;
        match tokens[cur] {
            Token::Equals => {
                cur += 1;
                match tokens[cur] {
                    Token::Number(x) => {
                        cur += 1;
                        ret.insert(ident, Entry::Number(x));
                    },
                    Token::String(x) => {
                        cur += 1;
                        ret.insert(ident, Entry::String(
                            String::from_utf8_lossy(x).into_owned()));
                    },
                    Token::BracketOpen => {
                        let sz = try!(find_matching_token(
                            &tokens[cur..], &Token::BracketOpen, &Token::BracketClose));
                        ret.insert(ident, Entry::List(
                            Box::new(try!(get_list(&tokens[cur..cur+sz])))));
                        cur += sz;
                    }
                    _ => return Err(Error::new(
                        Other, format!("Unexpected {:?}", tokens[cur])))
                }
            },
            Token::CurlyOpen => {
                let sz = try!(find_matching_token(
                    &tokens[cur..], &Token::CurlyOpen, &Token::CurlyClose));
                ret.insert(ident, Entry::Dict(
                    Box::new(try!(get_hash(&tokens[cur..cur+sz])))));
                cur += sz;
            }
            _ => return Err(Error::new(
                Other, format!("Unexpected {:?}", tokens[cur])))
        };
    }

    Ok(ret)
}

pub fn lex_and_structize(buf: &[u8]) -> io::Result<BTreeMap<String, Entry>> {

    let mut tokens: Vec<Token> = Vec::new();

    // LVM vsn1 is implicitly a map at the top level, so add
    // the appropriate tokens
    tokens.push(Token::CurlyOpen);
    tokens.append(&mut Lexer::new(&buf).collect());
    tokens.push(Token::CurlyClose);

    get_hash(&tokens)
}
<commit_msg>change find_matching_token to return a slice<commit_after>// Copyright © 2015 Sebastian Thiel
// Copyright © 2015 Andy Grover
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// “Software”), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// Base a lexer for LVM2's text format on the more complex (hah) json format.
// This code is based on https://github.com/Byron/json-tools.

use std::io;
use std::io::Error;
use std::io::ErrorKind::Other;

use std::collections::btree_map::BTreeMap;

#[derive(Debug, PartialEq, Clone)]
pub enum Token<'a> {
    /// `{`
    CurlyOpen,
    /// `}`
    CurlyClose,

    /// `[`
    BracketOpen,
    /// `]`
    BracketClose,

    /// `=`
    Equals,
    /// `,`
    Comma,

    /// A string , like `"foo"`
    String(&'a[u8]),

    Ident(&'a[u8]),

    /// An unsigned integer number
    Number(u64),

    Comment(&'a[u8]),

    /// The type of the token could not be identified.
    /// Should be removed if this lexer is ever to be feature complete
    Invalid(u8),
}

impl<'a> AsRef<str> for Token<'a> {
    fn as_ref(&self) -> &str {
        match *self {
            Token::CurlyOpen => "{",
            Token::CurlyClose => "}",
            Token::BracketOpen => "[",
            Token::BracketClose => "]",
            Token::Equals => "=",
            Token::Comma => ",",

            Token::Invalid(c) => panic!("Cannot convert invalid Token {}", c),
            _ => panic!("Cannot convert variant Tokens"),
        }
    }
}

pub struct Lexer<'a> {
    chars: &'a[u8],
    next_byte: Option<u8>,
    cursor: usize,
}

impl<'a> Lexer<'a> {
    /// Returns a new Lexer from a given byte iterator.
    pub fn new(chars: &'a[u8]) -> Lexer<'a> {
        Lexer {
            chars: chars,
            next_byte: None,
            cursor: 0,
        }
    }

    fn put_back(&mut self, c: u8) {
        debug_assert!(self.next_byte.is_none());
        self.next_byte = Some(c);
        self.cursor -= 1;
    }

    fn next_byte(&mut self) -> Option<u8> {
        match self.next_byte.take() {
            Some(c) => {
                self.cursor += 1;
                Some(c)
            },
            None => {
                if self.cursor >= self.chars.len() {
                    None
                }
                else {
                    let res = self.chars[self.cursor];
                    self.cursor += 1;
                    Some(res)
                }
            }
        }
    }
}

// Identifies the state of the lexer
enum Mode {
    Main,

    // tells position where these modes were started
    String(usize),
    Ident(usize),
    Number(usize),
    Comment(usize),
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token<'a>;

    /// Lex the underlying byte stream to generate tokens
    fn next(&mut self) -> Option<Token<'a>> {

        let mut state = Mode::Main;

        while let Some(c) = self.next_byte() {
            match state {
                Mode::Main => {
                    match c {
                        b'{' => {
                            return Some(Token::CurlyOpen);
                        },
                        b'}' => {
                            return Some(Token::CurlyClose);
                        },
                        b'"' => {
                            state = Mode::String(self.cursor - 1);
                        },
                        b'a' ... b'z' | b'A' ... b'Z' | b'_' | b'.' => {
                            state = Mode::Ident(self.cursor - 1);
                        },
                        b'0' ... b'9' => {
                            state = Mode::Number(self.cursor - 1);
                        },
                        b'#' => {
                            state = Mode::Comment(self.cursor - 1);
                        },
                        b'[' => {
                            return Some(Token::BracketOpen);
                        },
                        b']' => {
                            return Some(Token::BracketClose);
                        },
                        b'=' => {
                            return Some(Token::Equals);
                        },
                        b',' => {
                            return Some(Token::Comma);
                        },
                        b' ' | b'\n' | b'\t' | b'\0' => {
                            // ignore whitespace
                        }
                        _ => {
                            return Some(Token::Invalid(c));
                        },
                    }
                }
                Mode::String(first) => {
                    match c {
                        b'"' => {
                            return Some(Token::String(
                                &self.chars[first+1..self.cursor-1]));
                        },
                        _ => {
                            continue;
                        }
                    }
                },
                Mode::Ident(first) => {
                    match c {
                        b'a' ... b'z' | b'A' ... b'Z' | b'0' ... b'9'
                            | b'_' | b'.' | b'-' => {
                                continue;
                            }
                        _ => {
                            self.put_back(c);
                            return Some(Token::Ident(
                                &self.chars[first..self.cursor]));
                        }
                    }
                },
                Mode::Number(first) => {
                    match c {
                        b'0' ... b'9' => {
                            continue;
                        },
                        _ => {
                            self.put_back(c);
                            let s = String::from_utf8_lossy(
                                &self.chars[first..self.cursor]).into_owned();
                            return Some(Token::Number(s.parse().unwrap()));
                        }
                    }
                }
                Mode::Comment(first) => {
                    match c {
                        b'\n' => {
                            self.put_back(c);
                            return Some(Token::Comment(
                                &self.chars[first..self.cursor]));
                        }
                        _ => {
                            continue;
                        }
                    }
                },
            }
        }

        None
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Entry {
    Number(u64),
    String(String),
    Dict(Box<BTreeMap<String, Entry>>),
    List(Box<Vec<Entry>>),
}

fn find_matching_token<'a, 'b>(tokens: &'b[Token<'a>], begin: &Token<'a>, end: &Token<'a>) -> io::Result<&'b[Token<'a>]> {
    let mut brace_count = 0;

    for (i, x) in tokens.iter().enumerate() {
        match x {
            x if x == begin => {
                brace_count += 1;
            },
            x if x == end => {
                brace_count -= 1;
                if brace_count == 0 {
                    return Ok(&tokens[..i+1]);
                }
            },
            _ => {},
        }
    }
    Err(Error::new(Other, "token mismatch"))
}

// lists can only contain strings and numbers, yay
pub fn get_list<'a>(tokens: &[Token<'a>]) -> io::Result<Vec<Entry>> {
    let mut v = Vec::new();

    assert_eq!(*tokens.first().unwrap(), Token::BracketOpen);
    assert_eq!(*tokens.last().unwrap(), Token::BracketClose);

    // Omit enclosing brackets
    for tok in &tokens[1..tokens.len()-1] {
        match *tok {
            Token::Number(x) => v.push(Entry::Number(x)),
            Token::String(x) => v.push(Entry::String(String::from_utf8_lossy(x).into_owned())),
            Token::Comma => { },
            _ => return Err(Error::new(
                Other, format!("Unexpected {:?}", *tok)))
        }
    }

    Ok(v)
}

fn get_hash<'a>(tokens: &[Token<'a>]) -> io::Result<BTreeMap<String, Entry>> {
    let mut ret: BTreeMap<String, Entry> = BTreeMap::new();

    assert_eq!(*tokens.first().unwrap(), Token::CurlyOpen);
    assert_eq!(*tokens.last().unwrap(), Token::CurlyClose);

    let mut cur = 1;

    while tokens[cur] != Token::CurlyClose {

        let ident = match tokens[cur] {
            Token::Ident(x) => String::from_utf8_lossy(x).into_owned(),
            Token::Comment(_) => {
                cur += 1;
                continue
            },
            _ => return Err(Error::new(
                Other, format!("Unexpected {:?}", tokens[cur])))
        };

        cur += 1;
        match tokens[cur] {
            Token::Equals => {
                cur += 1;
                match tokens[cur] {
                    Token::Number(x) => {
                        cur += 1;
                        ret.insert(ident, Entry::Number(x));
                    },
                    Token::String(x) => {
                        cur += 1;
                        ret.insert(ident, Entry::String(
                            String::from_utf8_lossy(x).into_owned()));
                    },
                    Token::BracketOpen => {
                        let slc = try!(find_matching_token(
                            &tokens[cur..], &Token::BracketOpen, &Token::BracketClose));
                        ret.insert(ident, Entry::List(
                            Box::new(try!(get_list(&slc)))));
                        cur += slc.len();
                    }
                    _ => return Err(Error::new(
                        Other, format!("Unexpected {:?}", tokens[cur])))
                }
            },
            Token::CurlyOpen => {
                let slc = try!(find_matching_token(
                    &tokens[cur..], &Token::CurlyOpen, &Token::CurlyClose));
                ret.insert(ident, Entry::Dict(
                    Box::new(try!(get_hash(&slc)))));
                cur += slc.len();
            }
            _ => return Err(Error::new(
                Other, format!("Unexpected {:?}", tokens[cur])))
        };
    }

    Ok(ret)
}

pub fn lex_and_structize(buf: &[u8]) -> io::Result<BTreeMap<String, Entry>> {

    let mut tokens: Vec<Token> = Vec::new();

    // LVM vsn1 is implicitly a map at the top level, so add
    // the appropriate tokens
    tokens.push(Token::CurlyOpen);
    tokens.append(&mut Lexer::new(&buf).collect());
    tokens.push(Token::CurlyClose);

    get_hash(&tokens)
}
<|endoftext|>