<commit_before>use collections::string::String;
use collections::btree_map::BTreeMap;
use collections::vec::Vec;

use spin::RwLockWriteGuard;

use super::namespace::{ AmlValue, ObjectReference };
use super::AmlError;

use acpi::ACPI_TABLE;

pub type ParseResult = Result<AmlParseType, AmlError>;
pub type AmlParseType = AmlParseTypeGeneric<AmlValue>;

pub struct AmlParseTypeGeneric<T> {
    pub val: T,
    pub len: usize
}

pub enum ExecutionState {
    EXECUTING,
    CONTINUE,
    BREAK,
    RETURN(AmlValue)
}

pub struct AmlExecutionContext {
    pub scope: String,
    pub local_vars: [AmlValue; 8],
    pub arg_vars: [AmlValue; 8],
    pub state: ExecutionState,
    pub namespace_delta: Vec<String>,
    pub ctx_id: u64,
    pub sync_level: u8
}

impl AmlExecutionContext {
    pub fn new(scope: String) -> AmlExecutionContext {
        let mut idptr = ACPI_TABLE.next_ctx.write();
        let id: u64 = *idptr;

        *idptr += 1;

        AmlExecutionContext {
            scope: scope,
            local_vars: [AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized],
            arg_vars: [AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized],
            state: ExecutionState::EXECUTING,
            namespace_delta: vec!(),
            ctx_id: id,
            sync_level: 0
        }
    }

    pub fn wait_for_event(&mut self, event_ptr: AmlValue) -> Result<bool, AmlError> {
        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };

        let mutex_idx = match event_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Event(count) => {
                if count > 0 {
                    namespace.insert(mutex_idx, AmlValue::Event(count - 1));
                    return Ok(true);
                }
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(false)
    }

    pub fn signal_event(&mut self, event_ptr: AmlValue) -> Result<(), AmlError> {
        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };

        let mutex_idx = match event_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Event(count) => {
                namespace.insert(mutex_idx, AmlValue::Event(count + 1));
                return Ok(());
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(())
    }

    pub fn release_mutex(&mut self, mutex_ptr: AmlValue) -> Result<(), AmlError> {
        let id = self.ctx_id;

        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };

        let mutex_idx = match mutex_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Mutex((sync_level, owner)) => {
                if let Some(o) = owner {
                    if o == id {
                        if sync_level == self.sync_level {
                            namespace.insert(mutex_idx, AmlValue::Mutex((sync_level, None)));
                            return Ok(());
                        } else {
                            return Err(AmlError::AmlValueError);
                        }
                    } else {
                        return Err(AmlError::AmlHardFatal);
                    }
                }
            },
            AmlValue::OperationRegion(ref region) => {
                if let Some(o) = region.accessed_by {
                    if o == id {
                        let mut new_region = region.clone();
                        new_region.accessed_by = None;

                        namespace.insert(mutex_idx, AmlValue::OperationRegion(new_region));
                        return Ok(());
                    } else {
                        return Err(AmlError::AmlHardFatal);
                    }
                }
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(())
    }

    pub fn acquire_mutex(&mut self, mutex_ptr: AmlValue) -> Result<bool, AmlError> {
        let id = self.ctx_id;

        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };
        let mutex_idx = match mutex_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Mutex((sync_level, owner)) => {
                if owner == None {
                    if sync_level < self.sync_level {
                        return Err(AmlError::AmlValueError);
                    }

                    namespace.insert(mutex_idx, AmlValue::Mutex((sync_level, Some(id))));
                    self.sync_level = sync_level;

                    return Ok(true);
                }
            },
            AmlValue::OperationRegion(ref o) => {
                if o.accessed_by == None {
                    let mut new_region = o.clone();
                    new_region.accessed_by = Some(id);

                    namespace.insert(mutex_idx, AmlValue::OperationRegion(new_region));
                    return Ok(true);
                }
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(false)
    }

    pub fn add_to_namespace(&mut self, name: String, value: AmlValue) -> Result<(), AmlError> {
        let mut namespace = ACPI_TABLE.namespace.write();

        if let Some(ref mut namespace) = *namespace {
            if namespace.contains_key(&name) {
                return Err(AmlError::AmlValueError);
            }

            self.namespace_delta.push(name.clone());
            namespace.insert(name, value);

            Ok(())
        } else {
            Err(AmlError::AmlValueError)
        }
    }

    pub fn clean_namespace(&mut self) {
        let mut namespace = ACPI_TABLE.namespace.write();

        if let Some(ref mut namespace) = *namespace {
            for k in &self.namespace_delta {
                namespace.remove(k);
            }
        }
    }

    pub fn init_arg_vars(&mut self, parameters: Vec<AmlValue>) {
        if parameters.len() > 8 {
            return;
        }

        let mut cur = 0;
        while cur < parameters.len() {
            self.arg_vars[cur] = parameters[cur].clone();
            cur += 1;
        }
    }

    pub fn prelock(&mut self) -> RwLockWriteGuard<'static, Option<BTreeMap<String, AmlValue>>> {
        ACPI_TABLE.namespace.write()
    }

    pub fn modify(&mut self, name: AmlValue, value: AmlValue) -> Result<(), AmlError> {
        match name {
            AmlValue::ObjectReference(r) => match r {
                ObjectReference::ArgObj(_) => return Err(AmlError::AmlValueError),
                ObjectReference::LocalObj(i) => self.local_vars[i as usize] = value,
                ObjectReference::Object(s) => if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
                    namespace.insert(s, value);
                },
                ObjectReference::Index(c, v) => {
                    let idx = v.get_as_integer()? as usize;
                    match *c {
                        AmlValue::ObjectReference(r) => match r {
                            ObjectReference::Object(s) => {
                                if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
                                    let obj = if let Some(s) = namespace.get(&s) {
                                        s.clone()
                                    } else {
                                        return Err(AmlError::AmlValueError);
                                    };

                                    match obj {
                                        AmlValue::String(ref string) => {
                                            let mut bytes = string.clone().into_bytes();
                                            bytes[idx] = value.get_as_integer()? as u8;

                                            let string = String::from_utf8(bytes).unwrap();
                                            namespace.insert(s, AmlValue::String(string));
                                        },
                                        AmlValue::Buffer(ref b) => {
                                            let mut b = b.clone();
                                            b[idx] = value.get_as_integer()? as u8;

                                            namespace.insert(s, AmlValue::Buffer(b));
                                        },
                                        AmlValue::Package(ref p) => {
                                            let mut p = p.clone();
                                            p[idx] = value;

                                            namespace.insert(s, AmlValue::Package(p));
                                        },
                                        _ => return Err(AmlError::AmlValueError)
                                    }
                                }
                            },
                            _ => return Err(AmlError::AmlValueError)
                        },
                        _ => return Err(AmlError::AmlValueError)
                    }
                }
            },
            AmlValue::String(s) => if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
                namespace.insert(s, value);
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(())
    }

    pub fn get(&self, name: AmlValue) -> Result<AmlValue, AmlError> {
        Ok(match name {
            AmlValue::ObjectReference(r) => match r {
                ObjectReference::ArgObj(i) => self.arg_vars[i as usize].clone(),
                ObjectReference::LocalObj(i) => self.local_vars[i as usize].clone(),
                ObjectReference::Object(ref s) => if let Some(ref namespace) = *ACPI_TABLE.namespace.read() {
                    if let Some(o) = namespace.get(s) {
                        o.clone()
                    } else {
                        AmlValue::None
                    }
                } else { AmlValue::None },
                ObjectReference::Index(c, v) => if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
                    let idx = v.get_as_integer()? as usize;
                    match *c {
                        AmlValue::Package(p) => p[idx].clone(),
                        _ => AmlValue::None
                    }
                } else {
                    AmlValue::None
                }
            },
            AmlValue::String(ref s) => if let Some(ref namespace) = *ACPI_TABLE.namespace.read() {
                if let Some(o) = namespace.get(s) {
                    o.clone()
                } else {
                    AmlValue::None
                }
            } else { AmlValue::None },
            _ => AmlValue::None
        })
    }
}
<commit_msg>Completed context modify, aside from BufferFields and nested Indexes<commit_after>use collections::string::String;
use collections::btree_map::BTreeMap;
use collections::vec::Vec;

use spin::RwLockWriteGuard;

use super::namespace::{ AmlValue, ObjectReference };
use super::AmlError;

use acpi::ACPI_TABLE;

pub type ParseResult = Result<AmlParseType, AmlError>;
pub type AmlParseType = AmlParseTypeGeneric<AmlValue>;

pub struct AmlParseTypeGeneric<T> {
    pub val: T,
    pub len: usize
}

pub enum ExecutionState {
    EXECUTING,
    CONTINUE,
    BREAK,
    RETURN(AmlValue)
}

pub struct AmlExecutionContext {
    pub scope: String,
    pub local_vars: [AmlValue; 8],
    pub arg_vars: [AmlValue; 8],
    pub state: ExecutionState,
    pub namespace_delta: Vec<String>,
    pub ctx_id: u64,
    pub sync_level: u8
}

impl AmlExecutionContext {
    pub fn new(scope: String) -> AmlExecutionContext {
        let mut idptr = ACPI_TABLE.next_ctx.write();
        let id: u64 = *idptr;

        *idptr += 1;

        AmlExecutionContext {
            scope: scope,
            local_vars: [AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized,
                         AmlValue::Uninitialized],
            arg_vars: [AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized,
                       AmlValue::Uninitialized],
            state: ExecutionState::EXECUTING,
            namespace_delta: vec!(),
            ctx_id: id,
            sync_level: 0
        }
    }

    pub fn wait_for_event(&mut self, event_ptr: AmlValue) -> Result<bool, AmlError> {
        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };

        let mutex_idx = match event_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Event(count) => {
                if count > 0 {
                    namespace.insert(mutex_idx, AmlValue::Event(count - 1));
                    return Ok(true);
                }
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(false)
    }

    pub fn signal_event(&mut self, event_ptr: AmlValue) -> Result<(), AmlError> {
        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };

        let mutex_idx = match event_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Event(count) => {
                namespace.insert(mutex_idx, AmlValue::Event(count + 1));
                return Ok(());
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(())
    }

    pub fn release_mutex(&mut self, mutex_ptr: AmlValue) -> Result<(), AmlError> {
        let id = self.ctx_id;

        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };

        let mutex_idx = match mutex_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Mutex((sync_level, owner)) => {
                if let Some(o) = owner {
                    if o == id {
                        if sync_level == self.sync_level {
                            namespace.insert(mutex_idx, AmlValue::Mutex((sync_level, None)));
                            return Ok(());
                        } else {
                            return Err(AmlError::AmlValueError);
                        }
                    } else {
                        return Err(AmlError::AmlHardFatal);
                    }
                }
            },
            AmlValue::OperationRegion(ref region) => {
                if let Some(o) = region.accessed_by {
                    if o == id {
                        let mut new_region = region.clone();
                        new_region.accessed_by = None;

                        namespace.insert(mutex_idx, AmlValue::OperationRegion(new_region));
                        return Ok(());
                    } else {
                        return Err(AmlError::AmlHardFatal);
                    }
                }
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(())
    }

    pub fn acquire_mutex(&mut self, mutex_ptr: AmlValue) -> Result<bool, AmlError> {
        let id = self.ctx_id;

        let mut namespace_ptr = self.prelock();
        let mut namespace = match *namespace_ptr {
            Some(ref mut n) => n,
            None => return Err(AmlError::AmlHardFatal)
        };
        let mutex_idx = match mutex_ptr {
            AmlValue::String(ref s) => s.clone(),
            AmlValue::ObjectReference(ref o) => match *o {
                ObjectReference::Object(ref s) => s.clone(),
                _ => return Err(AmlError::AmlValueError)
            },
            _ => return Err(AmlError::AmlValueError)
        };

        let mutex = match namespace.get(&mutex_idx) {
            Some(s) => s.clone(),
            None => return Err(AmlError::AmlValueError)
        };

        match mutex {
            AmlValue::Mutex((sync_level, owner)) => {
                if owner == None {
                    if sync_level < self.sync_level {
                        return Err(AmlError::AmlValueError);
                    }

                    namespace.insert(mutex_idx, AmlValue::Mutex((sync_level, Some(id))));
                    self.sync_level = sync_level;

                    return Ok(true);
                }
            },
            AmlValue::OperationRegion(ref o) => {
                if o.accessed_by == None {
                    let mut new_region = o.clone();
                    new_region.accessed_by = Some(id);

                    namespace.insert(mutex_idx, AmlValue::OperationRegion(new_region));
                    return Ok(true);
                }
            },
            _ => return Err(AmlError::AmlValueError)
        }

        Ok(false)
    }

    pub fn add_to_namespace(&mut self, name: String, value: AmlValue) -> Result<(), AmlError> {
        let mut namespace = ACPI_TABLE.namespace.write();

        if let Some(ref mut namespace) = *namespace {
            if namespace.contains_key(&name) {
                return Err(AmlError::AmlValueError);
            }

            self.namespace_delta.push(name.clone());
            namespace.insert(name, value);

            Ok(())
        } else {
            Err(AmlError::AmlValueError)
        }
    }

    pub fn clean_namespace(&mut self) {
        let mut namespace = ACPI_TABLE.namespace.write();

        if let Some(ref mut namespace) = *namespace {
            for k in &self.namespace_delta {
                namespace.remove(k);
            }
        }
    }

    pub fn init_arg_vars(&mut self, parameters: Vec<AmlValue>) {
        if parameters.len() > 8 {
            return;
        }

        let mut cur = 0;
        while cur < parameters.len() {
            self.arg_vars[cur] = parameters[cur].clone();
            cur += 1;
        }
    }

    pub fn prelock(&mut self) -> RwLockWriteGuard<'static, Option<BTreeMap<String, AmlValue>>> {
        ACPI_TABLE.namespace.write()
    }

    fn modify_local_obj(&mut self, local: usize, value: AmlValue) -> Result<(), AmlError> {
        self.local_vars[local] = value;
        Ok(())
    }

    fn modify_object(&mut self, name: String, value: AmlValue) -> Result<(), AmlError> {
        if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
            namespace.insert(name, value);
            Ok(())
        } else {
            Err(AmlError::AmlHardFatal)
        }
    }

    fn modify_index_final(&mut self, name: String, value: AmlValue, indices: Vec<u64>) -> Result<(), AmlError> {
        if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
            let obj = if let Some(s) = namespace.get(&name) {
                s.clone()
            } else {
                return Err(AmlError::AmlValueError);
            };

            match obj {
                AmlValue::String(ref string) => {
                    if indices.len() != 1 {
                        return Err(AmlError::AmlValueError);
                    }

                    let mut bytes = string.clone().into_bytes();
                    bytes[indices[0] as usize] = value.get_as_integer()? as u8;

                    let string = String::from_utf8(bytes).unwrap();
                    namespace.insert(name, AmlValue::String(string));
                },
                AmlValue::Buffer(ref b) => {
                    if indices.len() != 1 {
                        return Err(AmlError::AmlValueError);
                    }

                    let mut b = b.clone();
                    b[indices[0] as usize] = value.get_as_integer()? as u8;

                    namespace.insert(name, AmlValue::Buffer(b));
                },
                AmlValue::Package(ref p) => {
                    // TODO: Nested references need to be handled here
                    if indices.len() < 0 {
                        return Err(AmlError::AmlValueError);
                    }

                    let mut p = p.clone();
//                    let ref mut current = p;

                    p[indices[0] as usize] = value;

                    namespace.insert(name, AmlValue::Package(p));
                },
                _ => return Err(AmlError::AmlValueError)
            }
        }

        Ok(())
    }

    pub fn modify(&mut self, name: AmlValue, value: AmlValue) -> Result<(), AmlError> {
        match name {
            AmlValue::ObjectReference(r) => match r {
                ObjectReference::ArgObj(_) => Err(AmlError::AmlValueError),
                ObjectReference::LocalObj(i) => self.modify_local_obj(i as usize, value),
                ObjectReference::Object(s) => self.modify_object(s, value),
                ObjectReference::Index(c, v) => {
                    let idx = v.get_as_integer()?;
                    match *c {
                        AmlValue::ObjectReference(r) => match r {
                            ObjectReference::Object(s) => self.modify_index_final(s, value, vec!(idx)),
                            _ => Err(AmlError::AmlValueError)
                        },
                        _ => Err(AmlError::AmlValueError)
                    }
                }
            },
            AmlValue::String(s) => self.modify_object(s, value),
            _ => Err(AmlError::AmlValueError)
        }
    }

    pub fn get(&self, name: AmlValue) -> Result<AmlValue, AmlError> {
        Ok(match name {
            AmlValue::ObjectReference(r) => match r {
                ObjectReference::ArgObj(i) => self.arg_vars[i as usize].clone(),
                ObjectReference::LocalObj(i) => self.local_vars[i as usize].clone(),
                ObjectReference::Object(ref s) => if let Some(ref namespace) = *ACPI_TABLE.namespace.read() {
                    if let Some(o) = namespace.get(s) {
                        o.clone()
                    } else {
                        AmlValue::None
                    }
                } else { AmlValue::None },
                ObjectReference::Index(c, v) => if let Some(ref mut namespace) = *ACPI_TABLE.namespace.write() {
                    let idx = v.get_as_integer()? as usize;
                    match *c {
                        AmlValue::Package(p) => p[idx].clone(),
                        _ => AmlValue::None
                    }
                } else {
                    AmlValue::None
                }
            },
            AmlValue::String(ref s) => if let Some(ref namespace) = *ACPI_TABLE.namespace.read() {
                if let Some(o) = namespace.get(s) {
                    o.clone()
                } else {
                    AmlValue::None
                }
            } else { AmlValue::None },
            _ => AmlValue::None
        })
    }
}
<|endoftext|>


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

use extra::url;
use extra::url::Url;
use std::hashmap::HashMap;
use std::os;

/**
Create a URL object from a string. Does various helpful browsery things like

* If there's no current url and the path looks like a file then it will
  create a file url based of the current working directory
* If there's a current url and the new path is relative then the new url
  is based off the current url

*/
// TODO: about:failure->
pub fn make_url(str_url: ~str, current_url: Option<Url>) -> Url {
    let str_url = str_url.trim_chars(& &[' ', '\t', '\n', '\r', '\x0C']).to_owned();
    let schm = url::get_scheme(str_url);
    let str_url = match schm {
        Err(_) => {
            if current_url.is_none() {
                // Assume we've been given a file path. If it's absolute just return
                // it, otherwise make it absolute with the cwd.
                if str_url.starts_with("/") {
                    ~"file://" + str_url
                } else {
                    let mut path = os::getcwd();
                    path.push(str_url);
                    // FIXME (#1094): not the right way to transform a path
                    ~"file://" + path.display().to_str()
                }
            } else {
                let current_url = current_url.unwrap();
                debug!("make_url: current_url: {:?}", current_url);

                let mut new_url = current_url.clone();
                new_url.query = ~[];
                new_url.fragment = None;

                if str_url.starts_with("//") {
                    new_url.scheme + ":" + str_url
                } else if current_url.path.is_empty() || str_url.starts_with("/") {
                    new_url.path = ~"/";
                    new_url.to_str() + str_url.trim_left_chars(&'/')
                } else if str_url.starts_with("#") {
                    new_url.to_str() + str_url
                } else { // relative path
                    let base_path = current_url.path.trim_right_chars(&|c: char| c != '/');
                    new_url.path = base_path.to_owned();
                    new_url.to_str() + str_url
                }
            }
        },
        Ok((scheme, page)) => {
            match scheme {
                ~"about" => {
                    match page {
                        ~"failure" => {
                            let mut path = os::getcwd();
                            path.push("../src/test/html/failure.html");
                            // FIXME (#1094): not the right way to transform a path
                            ~"file://" + path.display().to_str()
                        }
                        // TODO: handle the rest of the about: pages
                        _ => str_url
                    }
                },
                ~"data" => {
                    // Drop whitespace within data: URLs, e.g. newlines within a base64
                    // src="..." block.  Whitespace intended as content should be
                    // %-encoded or base64'd.
                    str_url.chars().filter(|&c| !c.is_whitespace()).collect()
                },
                _ => str_url
            }
        }
    };

    // FIXME: Need to handle errors
    url::from_str(str_url).unwrap()
}

#[cfg(test)]
mod make_url_tests {
    use super::make_url;
    use std::os;

    #[test]
    fn should_create_absolute_file_url_if_current_url_is_none_and_str_url_looks_filey() {
        let file = ~"local.html";
        let url = make_url(file, None);
        debug!("url: {:?}", url);
        assert!(url.scheme == ~"file");
        let path = os::getcwd();
        // FIXME (#1094): not the right way to transform a path
        assert!(url.path.contains(path.display().to_str()));
    }

    #[test]
    fn should_create_url_based_on_old_url_1() {
        let old_str = ~"http://example.com";
        let old_url = make_url(old_str, None);
        let new_str = ~"index.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/index.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_2() {
        let old_str = ~"http://example.com/";
        let old_url = make_url(old_str, None);
        let new_str = ~"index.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/index.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_3() {
        let old_str = ~"http://example.com/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"crumpet.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/crumpet.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_4() {
        let old_str = ~"http://example.com/snarf/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"crumpet.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/snarf/crumpet.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_5() {
        let old_str = ~"http://example.com/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"#top";
        let new_url = make_url(new_str, Some(old_url));

        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/index.html");
        assert!(new_url.fragment == Some(~"top"));
    }

    #[test]
    fn should_create_url_based_on_old_url_6() {
        use extra::url::UserInfo;

        let old_str = ~"http://foo:bar@example.com:8080/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"#top";
        let new_url = make_url(new_str, Some(old_url));

        assert!(new_url.scheme == ~"http");
        assert!(new_url.user == Some(UserInfo { user: ~"foo", pass: Some(~"bar") }));
        assert!(new_url.host == ~"example.com");
        assert!(new_url.port == Some(~"8080"));
        assert!(new_url.path == ~"/index.html");
        assert!(new_url.fragment == Some(~"top"));
    }

    #[test]
    fn should_create_url_based_on_old_url_7() {
        let old_str = ~"https://example.com/snarf/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"//example.com/crumpet.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"https");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/crumpet.html");
    }

}

pub type UrlMap<T> = HashMap<Url, T>;

pub fn url_map<T: Clone + 'static>() -> UrlMap<T> {
    HashMap::new()
}

<commit_msg>auto merge of #1541 : teosz/servo/master, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use extra::url;
use extra::url::Url;
use std::hashmap::HashMap;
use std::os;

/**
Create a URL object from a string. Does various helpful browsery things like

* If there's no current url and the path looks like a file then it will
  create a file url based of the current working directory
* If there's a current url and the new path is relative then the new url
  is based off the current url

*/
// TODO: about:failure->
pub fn make_url(str_url: &str, current_url: Option<Url>) -> Url {
    let str_url = str_url.trim_chars(& &[' ', '\t', '\n', '\r', '\x0C']).to_owned();
    let schm = url::get_scheme(str_url);
    let str_url = match schm {
        Err(_) => {
            if current_url.is_none() {
                // Assume we've been given a file path. If it's absolute just return
                // it, otherwise make it absolute with the cwd.
                if str_url.starts_with("/") {
                    ~"file://" + str_url
                } else {
                    let mut path = os::getcwd();
                    path.push(str_url);
                    // FIXME (#1094): not the right way to transform a path
                    ~"file://" + path.display().to_str()
                }
            } else {
                let current_url = current_url.unwrap();
                debug!("make_url: current_url: {:?}", current_url);

                let mut new_url = current_url.clone();
                new_url.query = ~[];
                new_url.fragment = None;

                if str_url.starts_with("//") {
                    new_url.scheme + ":" + str_url
                } else if current_url.path.is_empty() || str_url.starts_with("/") {
                    new_url.path = ~"/";
                    new_url.to_str() + str_url.trim_left_chars(&'/')
                } else if str_url.starts_with("#") {
                    new_url.to_str() + str_url
                } else { // relative path
                    let base_path = current_url.path.trim_right_chars(&|c: char| c != '/');
                    new_url.path = base_path.to_owned();
                    new_url.to_str() + str_url
                }
            }
        },
        Ok((scheme, page)) => {
            match scheme {
                ~"about" => {
                    match page {
                        ~"failure" => {
                            let mut path = os::getcwd();
                            path.push("../src/test/html/failure.html");
                            // FIXME (#1094): not the right way to transform a path
                            ~"file://" + path.display().to_str()
                        }
                        // TODO: handle the rest of the about: pages
                        _ => str_url
                    }
                },
                ~"data" => {
                    // Drop whitespace within data: URLs, e.g. newlines within a base64
                    // src="..." block.  Whitespace intended as content should be
                    // %-encoded or base64'd.
                    str_url.chars().filter(|&c| !c.is_whitespace()).collect()
                },
                _ => str_url
            }
        }
    };

    // FIXME: Need to handle errors
    url::from_str(str_url).unwrap()
}

#[cfg(test)]
mod make_url_tests {
    use super::make_url;
    use std::os;

    #[test]
    fn should_create_absolute_file_url_if_current_url_is_none_and_str_url_looks_filey() {
        let file = ~"local.html";
        let url = make_url(file, None);
        debug!("url: {:?}", url);
        assert!(url.scheme == ~"file");
        let path = os::getcwd();
        // FIXME (#1094): not the right way to transform a path
        assert!(url.path.contains(path.display().to_str()));
    }

    #[test]
    fn should_create_url_based_on_old_url_1() {
        let old_str = ~"http://example.com";
        let old_url = make_url(old_str, None);
        let new_str = ~"index.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/index.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_2() {
        let old_str = ~"http://example.com/";
        let old_url = make_url(old_str, None);
        let new_str = ~"index.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/index.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_3() {
        let old_str = ~"http://example.com/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"crumpet.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/crumpet.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_4() {
        let old_str = ~"http://example.com/snarf/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"crumpet.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/snarf/crumpet.html");
    }

    #[test]
    fn should_create_url_based_on_old_url_5() {
        let old_str = ~"http://example.com/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"#top";
        let new_url = make_url(new_str, Some(old_url));

        assert!(new_url.scheme == ~"http");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/index.html");
        assert!(new_url.fragment == Some(~"top"));
    }

    #[test]
    fn should_create_url_based_on_old_url_6() {
        use extra::url::UserInfo;

        let old_str = ~"http://foo:bar@example.com:8080/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"#top";
        let new_url = make_url(new_str, Some(old_url));

        assert!(new_url.scheme == ~"http");
        assert!(new_url.user == Some(UserInfo { user: ~"foo", pass: Some(~"bar") }));
        assert!(new_url.host == ~"example.com");
        assert!(new_url.port == Some(~"8080"));
        assert!(new_url.path == ~"/index.html");
        assert!(new_url.fragment == Some(~"top"));
    }

    #[test]
    fn should_create_url_based_on_old_url_7() {
        let old_str = ~"https://example.com/snarf/index.html";
        let old_url = make_url(old_str, None);
        let new_str = ~"//example.com/crumpet.html";
        let new_url = make_url(new_str, Some(old_url));
        assert!(new_url.scheme == ~"https");
        assert!(new_url.host == ~"example.com");
        assert!(new_url.path == ~"/crumpet.html");
    }

}

pub type UrlMap<T> = HashMap<Url, T>;

pub fn url_map<T: Clone + 'static>() -> UrlMap<T> {
    HashMap::new()
}

<|endoftext|>


<commit_before>// This lexer is based on the grammar I found here: https://github.com/antlr/grammars-v4/blob/master/asm6502/asm6502.g4
// It looks like it matches the various 6502 assembly examples I have seen online.

use std;
use std::error::Error;
use std::fs::File;
use std::io::{self, Read};
use std::str;

use assembler::token::{ImmediateBase, Token};
use ::opcodes::OpCode;

#[derive(Debug, PartialEq)]
pub struct LexerError {
    pub message: String,
}

impl LexerError {
    fn unexpected_ident<A, B>(expected: A, found: B) -> LexerError
        where A: std::fmt::Display,
              B: std::fmt::Display
    {
        LexerError {
            message: format!("ERR: Unexpected identifier. Found '{}', expected '{}'",
                             found,
                             expected),
        }
    }
}

impl From<std::io::Error> for LexerError {
    fn from(error: std::io::Error) -> LexerError {
        LexerError { message: error.description().into() }
    }
}

impl From<String> for LexerError {
    fn from(error: String) -> LexerError {
        LexerError { message: error }
    }
}

impl<'a> From<&'a str> for LexerError {
    fn from(error: &str) -> LexerError {
        LexerError { message: error.into() }
    }
}

/// Lexer accepts the program code as a string
/// and converts it to a list of Tokens
pub struct Lexer;

impl Lexer {
    /// Returns a vector of Tokens given an input of
    /// 6502 assembly code
    pub fn lex_string<S>(input: S) -> Result<Vec<Vec<Token>>, LexerError>
        where S: Into<String>
    {
        Ok(Self::lex(input.into())?)
    }

    /// Returns a vector of Tokens given a file
    /// to load 6502 assembly code from
    pub fn lex_file<P>(path: P) -> Result<Vec<Vec<Token>>, LexerError>
        where P: AsRef<std::path::Path>
    {
        let mut file = File::open(&path)?;
        let mut contents = String::new();

        file.read_to_string(&mut contents)?;

        Ok(Self::lex(contents)?)
    }

    /// Performs the bulk of the lexing logic
    fn lex(source: String) -> Result<Vec<Vec<Token>>, LexerError> {

        let mut result = Vec::new();

        for line in source.lines() {
            if line.trim().len() == 0 {
                continue;
            }
            let mut tokens = Vec::new();
            let mut idx = 0;
            let line = &line[..].as_bytes();
            while idx < line.len() {
                let c = line[idx] as char;
                if c.is_alphanumeric() {
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '$' {
                    let token = Self::consume_address(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '#' {
                    let token = Self::consume_number(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '.' {
                    idx += 1;
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    if let Token::Label(label) = token {
                        tokens.push(Token::Directive(label));
                    }
                } else {
                    idx += 1;
                }
            }

            result.push(tokens);
        }

        Ok(result)
    }

    fn consume_alphanumeric(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();

        while *idx < line.len() {
            let c = line[*idx] as char;
            if c.is_whitespace() || c == ';' || c == ':' || c == '\n' {
                return Ok(Self::classify(&tok));
            } else {
                tok.push(c);
            }

            *idx += 1;
        }

        Err("ERR: An error occurred while consuming an alphanumeric identifier".into())
    }

    fn consume_number(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        *idx += 1;
        let mut c = line[*idx] as char;
        let mut base = ImmediateBase::Base16;

        // Very next character should be a dollar sign
        if c != '$' {
            if c.is_digit(10) {
                base = ImmediateBase::Base10;
                // Consume every number
                loop {
                    if c.is_digit(10) {
                        tok.push(c);
                        *idx += 1;
                        if *idx < line.len() {
                            c = line[*idx] as char;
                        } else {
                            break;
                        }
                    } else {
                        if c == ',' {
                            break;
                        } else {
                            return Err(LexerError::unexpected_ident("{digit}", c));
                        }
                    }
                }

                return Ok(Token::Immediate(tok.clone(), base));
            } else {
                return Err(LexerError::unexpected_ident("{digit}", c));
            }
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c as char);
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c);
            *idx += 0x02;
            return Ok(Token::Immediate(str::from_utf8(&tok[..].as_bytes()).unwrap().into(), base));
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        Err("An error occurred while consuming an immediate identifier".into())
    }

    fn consume_address(mut idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        if let Token::Immediate(val, _) = Self::consume_number(&mut idx, &line)? {
            // If the length is greater than 2.. its not a Zero Page address
            if val.len() > 2 {
                let mut token_type = Token::Absolute(val.clone());
                // Check for AbsoluteX
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' {
                    *idx += 1;  // Jump over the comma
                    Self::consume_whitespace(&mut idx, line);
                    let c = line[*idx] as char;
                    if c == 'X' {
                        token_type = Token::AbsoluteX(val.clone());
                    } else if c == 'Y' {
                        token_type = Token::AbsoluteY(val.clone());
                    }
                    *idx += 0x02;
                }

                Ok(token_type)
            } else {
                let mut token_type = Token::ZeroPage(val.clone());

                // Check for ZeroPageX:
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' &&
                   line[*idx + 0x01] as char == 'X' {
                    token_type = Token::ZeroPageX(val.clone());
                    *idx += 0x02;
                }

                Ok(token_type)
            }
        } else {
            Err(LexerError::from("Error consuming address"))
        }
    }

    fn consume_whitespace(mut idx: &mut usize, line: &[u8]) {
        loop {
            if *idx < line.len() {
                if (line[*idx] as char).is_whitespace() {
                    *idx += 1;
                } else {
                    break;
                }
            } else {
                break;
            }
        }
    }

    fn classify(input: &str) -> Token {
        let mut tok = String::from(input);
        if let Some(opcode) = OpCode::from_mnemonic(tok.clone()) {
            Token::OpCode(tok.clone())
        } else {
            Token::Label(input.into())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, Token};

    #[test]
    fn can_classify_simple_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn does_not_classify_unknown_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            .LOL LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::Directive("LOL".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_classify_labels() {
        let tokens = Lexer::lex_string("MAIN: LDA #$20").unwrap();

        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid() {
        assert_eq!(Lexer::lex_string("
            LDA #$INVALID20
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "I")));
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #$2Z
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "Z")));
    }

    #[test]
    fn immediate_values_throw_errors_when_not_even_a_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #@hotmail.com
        "),
                   Err(LexerError::unexpected_ident("{digit}", "@")));
    }

    #[test]
    fn immediate_values_accept_base_ten() {
        let tokens = Lexer::lex_string("LDA #10").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("10".into(), ImmediateBase::Base10)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_base_ten_does_not_accept_hex() {
        assert_eq!(Lexer::lex_string("LDA #1A"),
                   Err(LexerError::unexpected_ident("{digit}", "A")));
    }

    #[test]
    fn can_figure_out_zero_page_opcode() {
        let tokens = Lexer::lex_string("LDA $44").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPage("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_zero_page_x_opcode() {
        let tokens = Lexer::lex_string("LDA $44,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPageX("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::Absolute("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_x_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteX("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_y_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,Y").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_y_address_opcode_when_excess_whitespace() {
        let tokens = Lexer::lex_string("LDA $4400,        Y").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }
}
<commit_msg>Add support for ignoring comments<commit_after>// This lexer is based on the grammar I found here: https://github.com/antlr/grammars-v4/blob/master/asm6502/asm6502.g4
// It looks like it matches the various 6502 assembly examples I have seen online.

use std;
use std::error::Error;
use std::fs::File;
use std::io::{self, Read};
use std::str;

use assembler::token::{ImmediateBase, Token};
use ::opcodes::OpCode;

#[derive(Debug, PartialEq)]
pub struct LexerError {
    pub message: String,
}

impl LexerError {
    fn unexpected_ident<A, B>(expected: A, found: B) -> LexerError
        where A: std::fmt::Display,
              B: std::fmt::Display
    {
        LexerError {
            message: format!("ERR: Unexpected identifier. Found '{}', expected '{}'",
                             found,
                             expected),
        }
    }
}

impl From<std::io::Error> for LexerError {
    fn from(error: std::io::Error) -> LexerError {
        LexerError { message: error.description().into() }
    }
}

impl From<String> for LexerError {
    fn from(error: String) -> LexerError {
        LexerError { message: error }
    }
}

impl<'a> From<&'a str> for LexerError {
    fn from(error: &str) -> LexerError {
        LexerError { message: error.into() }
    }
}

/// Lexer accepts the program code as a string
/// and converts it to a list of Tokens
pub struct Lexer;

impl Lexer {
    /// Returns a vector of Tokens given an input of
    /// 6502 assembly code
    pub fn lex_string<S>(input: S) -> Result<Vec<Vec<Token>>, LexerError>
        where S: Into<String>
    {
        Ok(Self::lex(input.into())?)
    }

    /// Returns a vector of Tokens given a file
    /// to load 6502 assembly code from
    pub fn lex_file<P>(path: P) -> Result<Vec<Vec<Token>>, LexerError>
        where P: AsRef<std::path::Path>
    {
        let mut file = File::open(&path)?;
        let mut contents = String::new();

        file.read_to_string(&mut contents)?;

        Ok(Self::lex(contents)?)
    }

    /// Performs the bulk of the lexing logic
    fn lex(source: String) -> Result<Vec<Vec<Token>>, LexerError> {

        let mut result = Vec::new();

        for line in source.lines() {
            if line.trim().len() == 0 {
                continue;
            }
            let mut tokens = Vec::new();
            let mut idx = 0;
            let line = &line[..].as_bytes();
            while idx < line.len() {
                let c = line[idx] as char;
                if c.is_alphanumeric() {
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    tokens.push(token);
                } else if c == ';' {
                    // Skip the rest of this line
                    break;
                } else if c == '$' {
                    let token = Self::consume_address(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '#' {
                    let token = Self::consume_number(&mut idx, line)?;
                    tokens.push(token);
                } else if c == '.' {
                    idx += 1;
                    let token = Self::consume_alphanumeric(&mut idx, line)?;
                    if let Token::Label(label) = token {
                        tokens.push(Token::Directive(label));
                    }
                } else {
                    idx += 1;
                }
            }

            result.push(tokens);
        }

        Ok(result)
    }

    fn consume_alphanumeric(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();

        while *idx < line.len() {
            let c = line[*idx] as char;
            if c.is_whitespace() || c == ';' || c == ':' || c == '\n' {
                return Ok(Self::classify(&tok));
            } else {
                tok.push(c);
            }

            *idx += 1;
        }

        Err("ERR: An error occurred while consuming an alphanumeric identifier".into())
    }

    fn consume_number(idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        *idx += 1;
        let mut c = line[*idx] as char;
        let mut base = ImmediateBase::Base16;

        // Very next character should be a dollar sign
        if c != '$' {
            if c.is_digit(10) {
                base = ImmediateBase::Base10;
                // Consume every number
                loop {
                    if c.is_digit(10) {
                        tok.push(c);
                        *idx += 1;
                        if *idx < line.len() {
                            c = line[*idx] as char;
                        } else {
                            break;
                        }
                    } else {
                        if c == ',' {
                            break;
                        } else {
                            return Err(LexerError::unexpected_ident("{digit}", c));
                        }
                    }
                }

                return Ok(Token::Immediate(tok.clone(), base));
            } else {
                return Err(LexerError::unexpected_ident("{digit}", c));
            }
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c as char);
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        *idx += 1;
        let c = line[*idx] as char;
        if c.is_digit(16) {
            tok.push(c);
            *idx += 0x02;
            return Ok(Token::Immediate(str::from_utf8(&tok[..].as_bytes()).unwrap().into(), base));
        } else {
            return Err(LexerError::unexpected_ident("{hex_digit}", c));
        }

        Err("An error occurred while consuming an immediate identifier".into())
    }

    fn consume_address(mut idx: &mut usize, line: &[u8]) -> Result<Token, LexerError> {
        let mut tok = String::new();
        if let Token::Immediate(val, _) = Self::consume_number(&mut idx, &line)? {
            // If the length is greater than 2.. its not a Zero Page address
            if val.len() > 2 {
                let mut token_type = Token::Absolute(val.clone());
                // Check for AbsoluteX
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' {
                    *idx += 1;  // Jump over the comma
                    Self::consume_whitespace(&mut idx, line);
                    let c = line[*idx] as char;
                    if c == 'X' {
                        token_type = Token::AbsoluteX(val.clone());
                    } else if c == 'Y' {
                        token_type = Token::AbsoluteY(val.clone());
                    }
                    *idx += 0x02;
                }

                Ok(token_type)
            } else {
                let mut token_type = Token::ZeroPage(val.clone());

                // Check for ZeroPageX:
                if *idx + 0x01 < line.len() && line[*idx] as char == ',' &&
                   line[*idx + 0x01] as char == 'X' {
                    token_type = Token::ZeroPageX(val.clone());
                    *idx += 0x02;
                }

                Ok(token_type)
            }
        } else {
            Err(LexerError::from("Error consuming address"))
        }
    }

    fn consume_whitespace(mut idx: &mut usize, line: &[u8]) {
        loop {
            if *idx < line.len() {
                if (line[*idx] as char).is_whitespace() {
                    *idx += 1;
                } else {
                    break;
                }
            } else {
                break;
            }
        }
    }

    fn classify(input: &str) -> Token {
        let mut tok = String::from(input);
        if let Some(opcode) = OpCode::from_mnemonic(tok.clone()) {
            Token::OpCode(tok.clone())
        } else {
            Token::Label(input.into())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::assembler::token::{ImmediateBase, Token};

    #[test]
    fn can_classify_simple_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn does_not_classify_unknown_labels_and_opcodes() {
        let tokens = Lexer::lex_string("
            .LOL LDA #$20
        ")
            .unwrap();

        assert_eq!(&[Token::Directive("LOL".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn can_classify_labels() {
        let tokens = Lexer::lex_string("MAIN: LDA #$20").unwrap();

        assert_eq!(&[Token::Label("MAIN".into()),
                     Token::OpCode("LDA".into()),
                     Token::Immediate("20".into(), ImmediateBase::Base16)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid() {
        assert_eq!(Lexer::lex_string("
            LDA #$INVALID20
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "I")));
    }

    #[test]
    fn immediate_values_throw_errors_when_invalid_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #$2Z
        "),
                   Err(LexerError::unexpected_ident("{hex_digit}", "Z")));
    }

    #[test]
    fn immediate_values_throw_errors_when_not_even_a_hex_code() {
        assert_eq!(Lexer::lex_string("
            LDA #@hotmail.com
        "),
                   Err(LexerError::unexpected_ident("{digit}", "@")));
    }

    #[test]
    fn immediate_values_accept_base_ten() {
        let tokens = Lexer::lex_string("LDA #10").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()),
                     Token::Immediate("10".into(), ImmediateBase::Base10)],
                   &tokens[0][..]);
    }

    #[test]
    fn immediate_values_base_ten_does_not_accept_hex() {
        assert_eq!(Lexer::lex_string("LDA #1A"),
                   Err(LexerError::unexpected_ident("{digit}", "A")));
    }

    #[test]
    fn can_figure_out_zero_page_opcode() {
        let tokens = Lexer::lex_string("LDA $44").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPage("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_zero_page_x_opcode() {
        let tokens = Lexer::lex_string("LDA $44,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::ZeroPageX("44".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::Absolute("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_x_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,X").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteX("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_y_address_opcode() {
        let tokens = Lexer::lex_string("LDA $4400,Y").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn can_figure_out_absolute_y_address_opcode_when_excess_whitespace() {
        let tokens = Lexer::lex_string("LDA $4400,        Y").unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }

    #[test]
    fn does_skip_comments() {
        let tokens = Lexer::lex_string("LDA $4400, Y ; This loads the value at $4400 + Y into \
                                        the A register")
            .unwrap();
        assert_eq!(&[Token::OpCode("LDA".into()), Token::AbsoluteY("4400".into())],
                   &tokens[0][..]);
    }
}
<|endoftext|>


<commit_before>//! `sled` is a flash-sympathetic persistent lock-free B+ tree.
//!
//! # Examples
//!
//! ```
//! let config = sled::ConfigBuilder::new().temporary(true).build();
//!
//! let t = sled::Tree::start(config).unwrap();
//!
//! t.set(b"yo!".to_vec(), b"v1".to_vec());
//! assert_eq!(t.get(b"yo!"), Ok(Some(b"v1".to_vec())));
//!
//! t.cas(
//!     b"yo!".to_vec(),       // key
//!     Some(b"v1".to_vec()),  // old value, None for not present
//!     Some(b"v2".to_vec()),  // new value, None for delete
//! ).unwrap();
//!
//! let mut iter = t.scan(b"a non-present key before yo!");
//! // assert_eq!(iter.next(), Some(Ok((b"yo!".to_vec(), b"v2".to_vec()))));
//! // assert_eq!(iter.next(), None);
//!
//! t.del(b"yo!");
//! assert_eq!(t.get(b"yo!"), Ok(None));
//! ```

#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#![cfg_attr(test, deny(bad_style))]
#![cfg_attr(test, deny(future_incompatible))]
#![cfg_attr(test, deny(nonstandard_style))]
#![cfg_attr(test, deny(rust_2018_compatibility))]
#![cfg_attr(test, deny(rust_2018_idioms))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", allow(inline_always))]

extern crate pagecache;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log as _log;

mod bound;
mod data;
mod frag;
mod iter;
mod materializer;
mod node;
mod pinned_value;
mod prefix;
mod tree;

pub use self::iter::Iter;
pub use self::pinned_value::PinnedValue;
/// atomic lock-free tree
pub use self::tree::Tree;

use pagecache::*;

pub use pagecache::{Config, ConfigBuilder, Error, Result};

use self::bound::Bound;
use self::data::Data;
use self::frag::{ChildSplit, ParentSplit};
use self::node::Node;
use self::prefix::{prefix_cmp, prefix_decode, prefix_encode};

pub(crate) use self::frag::Frag;
pub(crate) use self::materializer::BLinkMaterializer;

type Key = Vec<u8>;
type KeyRef<'a> = &'a [u8];
type Value = Vec<u8>;

type TreePtr<'g> = pagecache::PagePtr<'g, Frag>;
<commit_msg>Fix doctest<commit_after>//! `sled` is a flash-sympathetic persistent lock-free B+ tree.
//!
//! # Examples
//!
//! ```
//! let config = sled::ConfigBuilder::new().temporary(true).build();
//!
//! let t = sled::Tree::start(config).unwrap();
//!
//! t.set(b"yo!".to_vec(), b"v1".to_vec());
//! assert!(t.get(b"yo!").unwrap().unwrap() == &*b"v1".to_vec());
//!
//! t.cas(
//!     b"yo!".to_vec(),       // key
//!     Some(b"v1"),           // old value, None for not present
//!     Some(b"v2".to_vec()),  // new value, None for delete
//! ).unwrap();
//!
//! let mut iter = t.scan(b"a non-present key before yo!");
//! // assert_eq!(iter.next(), Some(Ok((b"yo!".to_vec(), b"v2".to_vec()))));
//! // assert_eq!(iter.next(), None);
//!
//! t.del(b"yo!");
//! assert_eq!(t.get(b"yo!"), Ok(None));
//! ```

#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#![cfg_attr(test, deny(bad_style))]
#![cfg_attr(test, deny(future_incompatible))]
#![cfg_attr(test, deny(nonstandard_style))]
#![cfg_attr(test, deny(rust_2018_compatibility))]
#![cfg_attr(test, deny(rust_2018_idioms))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", allow(inline_always))]

extern crate pagecache;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log as _log;

mod bound;
mod data;
mod frag;
mod iter;
mod materializer;
mod node;
mod pinned_value;
mod prefix;
mod tree;

pub use self::iter::Iter;
pub use self::pinned_value::PinnedValue;
/// atomic lock-free tree
pub use self::tree::Tree;

use pagecache::*;

pub use pagecache::{Config, ConfigBuilder, Error, Result};

use self::bound::Bound;
use self::data::Data;
use self::frag::{ChildSplit, ParentSplit};
use self::node::Node;
use self::prefix::{prefix_cmp, prefix_decode, prefix_encode};

pub(crate) use self::frag::Frag;
pub(crate) use self::materializer::BLinkMaterializer;

type Key = Vec<u8>;
type KeyRef<'a> = &'a [u8];
type Value = Vec<u8>;

type TreePtr<'g> = pagecache::PagePtr<'g, Frag>;
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt;

use ::geom::{Geom, Size, Table};
use ::lattice_parser::ParseLatticeError;

pub type Hint = Option<u8>;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Side { In, Out }
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Edge { Line, Cross }

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Puzzle {
    size: Size,
    hint: Table<Hint>,
    side: Table<Option<Side>>,
    edge_v: Table<Option<Edge>>,
    edge_h: Table<Option<Edge>>
}

impl Puzzle {
    #[inline]
    pub fn new(size: Size) -> Puzzle {
        assert!(size.0 > 0 && size.1 > 0);
        let hint = Table::new_empty(size, None, None);
        let side = Table::new_empty(size, Some(Side::Out), None);
        let edge_v = Table::new_empty(Size(size.0, size.1 + 1), Some(Edge::Cross), None);
        let edge_h = Table::new_empty(Size(size.0 + 1, size.1), Some(Edge::Cross), None);
        Puzzle { size: size, hint: hint, side: side, edge_v: edge_v, edge_h: edge_h }
    }

    #[inline]
    fn with_data(size: Size, hint: Vec<Hint>, side: Vec<Option<Side>>,
                 edge_v: Vec<Option<Edge>>, edge_h: Vec<Option<Edge>>) -> Puzzle {
        assert!(size.0 > 0 && size.1 > 0);
        let hint = Table::new(size, None, hint);
        let side = Table::new(size, Some(Side::Out), side);
        let edge_v = Table::new(Size(size.0, size.1 + 1), Some(Edge::Cross), edge_v);
        let edge_h = Table::new(Size(size.0 + 1, size.1), Some(Edge::Cross), edge_h);
        Puzzle { size: size, hint: hint, side: side, edge_v: edge_v, edge_h: edge_h }
    }

    #[inline]
    pub fn hint(&self) -> &Table<Hint> { &self.hint }
    #[inline]
    pub fn side(&self) -> &Table<Option<Side>> { &self.side }
    #[inline]
    pub fn edge_h(&self) -> &Table<Option<Edge>> { &self.edge_h }
    #[inline]
    pub fn edge_v(&self) -> &Table<Option<Edge>> { &self.edge_v }

    #[inline]
    pub fn hint_mut(&mut self) -> &mut Table<Hint> { &mut self.hint }
    #[inline]
    pub fn side_mut(&mut self) -> &mut Table<Option<Side>> { &mut self.side }
    #[inline]
    pub fn edge_h_mut(&mut self) -> &mut Table<Option<Edge>> { &mut self.edge_h }
    #[inline]
    pub fn edge_v_mut(&mut self) -> &mut Table<Option<Edge>> { &mut self.edge_v }
}

impl Geom for Puzzle {
    #[inline]
    fn size(&self) -> Size { self.size }
}

#[derive(Copy, Clone, Debug)]
pub struct ParsePuzzleError {
    kind: PuzzleErrorKind
}

#[derive(Copy, Clone, Debug)]
enum PuzzleErrorKind {
    Empty,
    TooSmallRows,
    TooSmallColumns,
    LengthMismatch,
    InvalidHint,
    Lattice(ParseLatticeError)
}

impl From<ParseLatticeError> for ParsePuzzleError {
    fn from(err: ParseLatticeError) -> ParsePuzzleError {
        ParsePuzzleError {
            kind: PuzzleErrorKind::Lattice(err)
        }
    }
}

impl Error for ParsePuzzleError {
    fn description(&self) -> &str {
        use self::PuzzleErrorKind::*;
        match self.kind {
            Empty => "cannot parse puzzle from empty string",
            TooSmallRows => "the number of rows is too small to parse puzzle",
            TooSmallColumns => "the number of columns is too small to parse puzzle",
            LengthMismatch => "the length of lines are not same",
            InvalidHint => "invalid hint found in string",
            Lattice(ref e) => e.description()
        }
    }
    fn cause(&self) -> Option<&Error> {
        use self::PuzzleErrorKind::*;
        match self.kind {
            Empty | TooSmallRows | TooSmallColumns | LengthMismatch | InvalidHint
                => None,
            Lattice(ref e) => Some(e)
        }
    }
}

impl fmt::Display for ParsePuzzleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl ParsePuzzleError {
    fn empty() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::Empty }
    }
    fn too_small_rows() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::TooSmallRows }
    }
    fn too_small_columns() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::TooSmallColumns }
    }
    fn length_mismatch() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::LengthMismatch }
    }
    fn invalid_hint() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::InvalidHint }
    }
}

mod from_str_impl {
    use super::{Puzzle, Edge, ParsePuzzleError as Error};
    use std::str::FromStr;
    use geom::Size;
    use lattice_parser::LatticeParser;

    impl FromStr for Puzzle {
        type Err = Error;

        fn from_str(s: &str) -> Result<Puzzle, Error> {
            let mut mat = s.lines()
                .map(|l| l.trim_matches('\n'))
                .map(|l| l.chars().collect::<Vec<_>>())
                .skip_while(|l| l.is_empty())
                .collect::<Vec<_>>();

            // Drop trailing empty lines
            while mat.last().map(|l| l.len()) == Some(0) {
                let _ = mat.pop();
            }

            if mat.len() == 0 { return Err(Error::empty()) }

            if mat[0].iter().any(|&c| c == '+') {
                parse_pat1(mat)
            } else {
                parse_pat2(mat)
            }
        }
    }

    fn parse_pat1(mat: Vec<Vec<char>>) -> Result<Puzzle, Error> {
        let parser = try!(LatticeParser::from_lines(&mat));

        let rows = parser.num_rows();
        let cols = parser.num_cols();

        if rows <= 1 { return Err(Error::too_small_rows()) }
        if cols <= 1 { return Err(Error::too_small_columns()) }

        let edge_v = parser.v_edges()
            .map(|(_, s)| {
                if s.is_empty() {
                    None
                } else if s.chars().all(|c| c == 'x') {
                    Some(Edge::Cross)
                } else if s.chars().all(|c| c == '|') {
                    Some(Edge::Line)
                } else {
                    None
                }
            }).collect();

        let edge_h = parser.h_edges()
            .map(|(_, s)| {
                if s.is_empty() {
                    None
                } else if s.chars().all(|c| c == 'x') {
                    Some(Edge::Cross)
                } else if s.chars().all(|c| c == '-') {
                    Some(Edge::Line)
                } else {
                    None
                }
            }).collect();

        let hint = parser.cells()
            .filter_map(|(_, s)| {
                match s.trim_matches(' ') {
                    "0" => Some(Some(0)),
                    "1" => Some(Some(1)),
                    "2" => Some(Some(2)),
                    "3" => Some(Some(3)),
                    "" | "_" | "-" => Some(None),
                    _ => None
                }
            }).collect::<Vec<_>>();
        if hint.len() != (rows - 1) * (cols - 1) {
            return Err(Error::invalid_hint())
        }

        let size = Size((rows - 1) as i32, (cols - 1) as i32);
        let side = vec![None; (rows - 1) * (cols - 1)];
        Ok(Puzzle::with_data(size, hint, side, edge_v, edge_h))
    }

    fn parse_pat2(mat: Vec<Vec<char>>) -> Result<Puzzle, Error> {
        let row = mat.len();
        if row < 1 { return Err(Error::too_small_rows()) }
        let col = mat[0].len();
        if col < 1 { return Err(Error::too_small_columns()) }
        if mat[1..].iter().any(|r| r.len() != col) {
            return Err(Error::length_mismatch())
        }

        let hint = mat.iter().flat_map(|line| {
            line.iter().filter_map(|&c| {
                match c {
                    '0' => Some(Some(0)),
                    '1' => Some(Some(1)),
                    '2' => Some(Some(2)),
                    '3' => Some(Some(3)),
                    '_' | '-' => Some(None),
                    _ => None
                }
            })
        }).collect::<Vec<_>>();
        if hint.len() != row * col {
            return Err(Error::invalid_hint())
        }

        let size = Size(row as i32, col as i32);
        let side = vec![None; row * col];
        let edge_v = vec![None; row * (col + 1)];
        let edge_h = vec![None; (row + 1) * col];
        Ok(Puzzle::with_data(size, hint, side, edge_v, edge_h))
    }
}

mod display_impl {
    use super::{Puzzle, Edge};
    use std::fmt;
    use geom::{Geom, Point};

    struct Cross;
    impl fmt::Display for Cross {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "+")
        }
    }

    struct HEdge<'a>(&'a Puzzle, Point);
    impl<'a> fmt::Display for HEdge<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let HEdge(puzzle, p) = *self;
            match puzzle.edge_h[p] {
                Some(Edge::Cross) => try!(write!(f, "x")),
                Some(Edge::Line) => try!(write!(f, "-")),
                None => try!(write!(f, " "))
            }
            Ok(())
        }
    }

    struct VEdge<'a>(&'a Puzzle, Point);
    impl<'a> fmt::Display for VEdge<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let VEdge(puzzle, p) = *self;
            match puzzle.edge_v[p] {
                Some(Edge::Cross) => try!(write!(f, "x")),
                Some(Edge::Line) => try!(write!(f, "|")),
                None => try!(write!(f, " "))
            }
            Ok(())
        }
    }

    struct EdgeRow<'a>(&'a Puzzle, i32);
    impl<'a> fmt::Display for EdgeRow<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let EdgeRow(puzzle, r) = *self;
            for c in 0..puzzle.column() {
                let p = Point(r, c);
                try!(write!(f, "{}", Cross));
                try!(write!(f, "{}", HEdge(puzzle, p)));
            }
            try!(write!(f, "{}", Cross));
            Ok(())
        }
    }

    struct CellRow<'a>(&'a Puzzle, i32);
    impl<'a> fmt::Display for CellRow<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let CellRow(puzzle, r) = *self;
            for c in 0..puzzle.column() {
                let p = Point(r, c);
                try!(write!(f, "{}", VEdge(puzzle, p)));
                match puzzle.hint[p] {
                    Some(n) => try!(write!(f, "{}", n)),
                    None => try!(write!(f, " "))
                }
            }
            try!(write!(f, "{}", VEdge(puzzle, Point(r, puzzle.column()))));
            Ok(())
        }
    }

    impl fmt::Display for Puzzle {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            for r in 0..self.row() {
                try!(writeln!(f, "{}", EdgeRow(self, r)));
                try!(writeln!(f, "{}", CellRow(self, r)));
            }
            try!(writeln!(f, "{}", EdgeRow(self, self.row())));
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Puzzle;
    use geom::{Geom, Size, Point};

    #[test]
    fn parse() {
        let input = "123___
______
3_____
";
        let puzzle = input.parse::<Puzzle>().unwrap();
        let hint = puzzle.hint();
        assert_eq!(Size(3, 6), hint.size());
        assert_eq!(Some(1), hint[Point(0, 0)]);
        assert_eq!(Some(2), hint[Point(0, 1)]);
        assert_eq!(Some(3), hint[Point(0, 2)]);
        assert_eq!(None, hint[Point(0, 3)]);
        assert_eq!(None, hint[Point(0, 4)]);
        assert_eq!(None, hint[Point(0, 5)]);
        assert_eq!(None, hint[Point(1, 0)]);
        assert_eq!(None, hint[Point(1, 1)]);
        assert_eq!(None, hint[Point(1, 2)]);
        assert_eq!(None, hint[Point(1, 3)]);
        assert_eq!(None, hint[Point(1, 4)]);
        assert_eq!(None, hint[Point(1, 5)]);
        assert_eq!(Some(3), hint[Point(2, 0)]);
        assert_eq!(None, hint[Point(2, 1)]);
        assert_eq!(None, hint[Point(2, 2)]);
        assert_eq!(None, hint[Point(2, 3)]);
        assert_eq!(None, hint[Point(2, 4)]);
        assert_eq!(None, hint[Point(2, 5)]);

        assert_eq!(Ok(&puzzle), puzzle.to_string().parse::<Puzzle>().as_ref());

        assert_eq!(Err(()), "1243".parse::<Puzzle>());

        let input = "
+--+ +-+!!+asdf
+  + + +  +
|  |1|    |
|  | |  2 |
+  + + +  +
";
        let output = "+-+ +-+ +

+ + + + +
| |1|  2|
+ + + + +
";

        let puzzle = input.parse::<Puzzle>().unwrap();
        let hint = puzzle.hint();
        assert_eq!(Some(1), hint[Point(1, 1)]);
        assert_eq!(Some(2), hint[Point(1, 3)]);
        assert_eq!(output, puzzle.to_string());

        assert_eq!(Err(()), "".parse::<Puzzle>());

        let input = "
+ + + +
 1 2 3
+ + + +
";
        let puzzle = input.parse::<Puzzle>().unwrap();
        let hint = puzzle.hint();
        assert_eq!(Some(1), hint[Point(0, 0)]);
        assert_eq!(Some(2), hint[Point(0, 1)]);
        assert_eq!(Some(3), hint[Point(0, 2)]);
    }
}
<commit_msg>core: fix test failure<commit_after>use std::error::Error;
use std::fmt;

use ::geom::{Geom, Size, Table};
use ::lattice_parser::ParseLatticeError;

pub type Hint = Option<u8>;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Side { In, Out }
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum Edge { Line, Cross }

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Puzzle {
    size: Size,
    hint: Table<Hint>,
    side: Table<Option<Side>>,
    edge_v: Table<Option<Edge>>,
    edge_h: Table<Option<Edge>>
}

impl Puzzle {
    #[inline]
    pub fn new(size: Size) -> Puzzle {
        assert!(size.0 > 0 && size.1 > 0);
        let hint = Table::new_empty(size, None, None);
        let side = Table::new_empty(size, Some(Side::Out), None);
        let edge_v = Table::new_empty(Size(size.0, size.1 + 1), Some(Edge::Cross), None);
        let edge_h = Table::new_empty(Size(size.0 + 1, size.1), Some(Edge::Cross), None);
        Puzzle { size: size, hint: hint, side: side, edge_v: edge_v, edge_h: edge_h }
    }

    #[inline]
    fn with_data(size: Size, hint: Vec<Hint>, side: Vec<Option<Side>>,
                 edge_v: Vec<Option<Edge>>, edge_h: Vec<Option<Edge>>) -> Puzzle {
        assert!(size.0 > 0 && size.1 > 0);
        let hint = Table::new(size, None, hint);
        let side = Table::new(size, Some(Side::Out), side);
        let edge_v = Table::new(Size(size.0, size.1 + 1), Some(Edge::Cross), edge_v);
        let edge_h = Table::new(Size(size.0 + 1, size.1), Some(Edge::Cross), edge_h);
        Puzzle { size: size, hint: hint, side: side, edge_v: edge_v, edge_h: edge_h }
    }

    #[inline]
    pub fn hint(&self) -> &Table<Hint> { &self.hint }
    #[inline]
    pub fn side(&self) -> &Table<Option<Side>> { &self.side }
    #[inline]
    pub fn edge_h(&self) -> &Table<Option<Edge>> { &self.edge_h }
    #[inline]
    pub fn edge_v(&self) -> &Table<Option<Edge>> { &self.edge_v }

    #[inline]
    pub fn hint_mut(&mut self) -> &mut Table<Hint> { &mut self.hint }
    #[inline]
    pub fn side_mut(&mut self) -> &mut Table<Option<Side>> { &mut self.side }
    #[inline]
    pub fn edge_h_mut(&mut self) -> &mut Table<Option<Edge>> { &mut self.edge_h }
    #[inline]
    pub fn edge_v_mut(&mut self) -> &mut Table<Option<Edge>> { &mut self.edge_v }
}

impl Geom for Puzzle {
    #[inline]
    fn size(&self) -> Size { self.size }
}

#[derive(Copy, Clone, Debug)]
pub struct ParsePuzzleError {
    kind: PuzzleErrorKind
}

#[derive(Copy, Clone, Debug)]
enum PuzzleErrorKind {
    Empty,
    TooSmallRows,
    TooSmallColumns,
    LengthMismatch,
    InvalidHint,
    Lattice(ParseLatticeError)
}

impl From<ParseLatticeError> for ParsePuzzleError {
    fn from(err: ParseLatticeError) -> ParsePuzzleError {
        ParsePuzzleError {
            kind: PuzzleErrorKind::Lattice(err)
        }
    }
}

impl Error for ParsePuzzleError {
    fn description(&self) -> &str {
        use self::PuzzleErrorKind::*;
        match self.kind {
            Empty => "cannot parse puzzle from empty string",
            TooSmallRows => "the number of rows is too small to parse puzzle",
            TooSmallColumns => "the number of columns is too small to parse puzzle",
            LengthMismatch => "the length of lines are not same",
            InvalidHint => "invalid hint found in string",
            Lattice(ref e) => e.description()
        }
    }
    fn cause(&self) -> Option<&Error> {
        use self::PuzzleErrorKind::*;
        match self.kind {
            Empty | TooSmallRows | TooSmallColumns | LengthMismatch | InvalidHint
                => None,
            Lattice(ref e) => Some(e)
        }
    }
}

impl fmt::Display for ParsePuzzleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.description().fmt(f)
    }
}

impl ParsePuzzleError {
    fn empty() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::Empty }
    }
    fn too_small_rows() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::TooSmallRows }
    }
    fn too_small_columns() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::TooSmallColumns }
    }
    fn length_mismatch() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::LengthMismatch }
    }
    fn invalid_hint() -> ParsePuzzleError {
        ParsePuzzleError { kind: PuzzleErrorKind::InvalidHint }
    }
}

mod from_str_impl {
    use super::{Puzzle, Edge, ParsePuzzleError as Error};
    use std::str::FromStr;
    use geom::Size;
    use lattice_parser::LatticeParser;

    impl FromStr for Puzzle {
        type Err = Error;

        fn from_str(s: &str) -> Result<Puzzle, Error> {
            let mut mat = s.lines()
                .map(|l| l.trim_matches('\n'))
                .map(|l| l.chars().collect::<Vec<_>>())
                .skip_while(|l| l.is_empty())
                .collect::<Vec<_>>();

            // Drop trailing empty lines
            while mat.last().map(|l| l.len()) == Some(0) {
                let _ = mat.pop();
            }

            if mat.len() == 0 { return Err(Error::empty()) }

            if mat[0].iter().any(|&c| c == '+') {
                parse_pat1(mat)
            } else {
                parse_pat2(mat)
            }
        }
    }

    fn parse_pat1(mat: Vec<Vec<char>>) -> Result<Puzzle, Error> {
        let parser = try!(LatticeParser::from_lines(&mat));

        let rows = parser.num_rows();
        let cols = parser.num_cols();

        if rows <= 1 { return Err(Error::too_small_rows()) }
        if cols <= 1 { return Err(Error::too_small_columns()) }

        let edge_v = parser.v_edges()
            .map(|(_, s)| {
                if s.is_empty() {
                    None
                } else if s.chars().all(|c| c == 'x') {
                    Some(Edge::Cross)
                } else if s.chars().all(|c| c == '|') {
                    Some(Edge::Line)
                } else {
                    None
                }
            }).collect();

        let edge_h = parser.h_edges()
            .map(|(_, s)| {
                if s.is_empty() {
                    None
                } else if s.chars().all(|c| c == 'x') {
                    Some(Edge::Cross)
                } else if s.chars().all(|c| c == '-') {
                    Some(Edge::Line)
                } else {
                    None
                }
            }).collect();

        let hint = parser.cells()
            .filter_map(|(_, s)| {
                match s.trim_matches(' ') {
                    "0" => Some(Some(0)),
                    "1" => Some(Some(1)),
                    "2" => Some(Some(2)),
                    "3" => Some(Some(3)),
                    "" | "_" | "-" => Some(None),
                    _ => None
                }
            }).collect::<Vec<_>>();
        if hint.len() != (rows - 1) * (cols - 1) {
            return Err(Error::invalid_hint())
        }

        let size = Size((rows - 1) as i32, (cols - 1) as i32);
        let side = vec![None; (rows - 1) * (cols - 1)];
        Ok(Puzzle::with_data(size, hint, side, edge_v, edge_h))
    }

    fn parse_pat2(mat: Vec<Vec<char>>) -> Result<Puzzle, Error> {
        let row = mat.len();
        if row < 1 { return Err(Error::too_small_rows()) }
        let col = mat[0].len();
        if col < 1 { return Err(Error::too_small_columns()) }
        if mat[1..].iter().any(|r| r.len() != col) {
            return Err(Error::length_mismatch())
        }

        let hint = mat.iter().flat_map(|line| {
            line.iter().filter_map(|&c| {
                match c {
                    '0' => Some(Some(0)),
                    '1' => Some(Some(1)),
                    '2' => Some(Some(2)),
                    '3' => Some(Some(3)),
                    '_' | '-' => Some(None),
                    _ => None
                }
            })
        }).collect::<Vec<_>>();
        if hint.len() != row * col {
            return Err(Error::invalid_hint())
        }

        let size = Size(row as i32, col as i32);
        let side = vec![None; row * col];
        let edge_v = vec![None; row * (col + 1)];
        let edge_h = vec![None; (row + 1) * col];
        Ok(Puzzle::with_data(size, hint, side, edge_v, edge_h))
    }
}

mod display_impl {
    use super::{Puzzle, Edge};
    use std::fmt;
    use geom::{Geom, Point};

    struct Cross;
    impl fmt::Display for Cross {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "+")
        }
    }

    struct HEdge<'a>(&'a Puzzle, Point);
    impl<'a> fmt::Display for HEdge<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let HEdge(puzzle, p) = *self;
            match puzzle.edge_h[p] {
                Some(Edge::Cross) => try!(write!(f, "x")),
                Some(Edge::Line) => try!(write!(f, "-")),
                None => try!(write!(f, " "))
            }
            Ok(())
        }
    }

    struct VEdge<'a>(&'a Puzzle, Point);
    impl<'a> fmt::Display for VEdge<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let VEdge(puzzle, p) = *self;
            match puzzle.edge_v[p] {
                Some(Edge::Cross) => try!(write!(f, "x")),
                Some(Edge::Line) => try!(write!(f, "|")),
                None => try!(write!(f, " "))
            }
            Ok(())
        }
    }

    struct EdgeRow<'a>(&'a Puzzle, i32);
    impl<'a> fmt::Display for EdgeRow<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let EdgeRow(puzzle, r) = *self;
            for c in 0..puzzle.column() {
                let p = Point(r, c);
                try!(write!(f, "{}", Cross));
                try!(write!(f, "{}", HEdge(puzzle, p)));
            }
            try!(write!(f, "{}", Cross));
            Ok(())
        }
    }

    struct CellRow<'a>(&'a Puzzle, i32);
    impl<'a> fmt::Display for CellRow<'a> {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let CellRow(puzzle, r) = *self;
            for c in 0..puzzle.column() {
                let p = Point(r, c);
                try!(write!(f, "{}", VEdge(puzzle, p)));
                match puzzle.hint[p] {
                    Some(n) => try!(write!(f, "{}", n)),
                    None => try!(write!(f, " "))
                }
            }
            try!(write!(f, "{}", VEdge(puzzle, Point(r, puzzle.column()))));
            Ok(())
        }
    }

    impl fmt::Display for Puzzle {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            for r in 0..self.row() {
                try!(writeln!(f, "{}", EdgeRow(self, r)));
                try!(writeln!(f, "{}", CellRow(self, r)));
            }
            try!(writeln!(f, "{}", EdgeRow(self, self.row())));
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Puzzle;
    use geom::{Geom, Size, Point};

    #[test]
    fn parse() {
        let input = "123___
______
3_____
";
        let puzzle = input.parse::<Puzzle>().unwrap();
        let hint = puzzle.hint();
        assert_eq!(Size(3, 6), hint.size());
        assert_eq!(Some(1), hint[Point(0, 0)]);
        assert_eq!(Some(2), hint[Point(0, 1)]);
        assert_eq!(Some(3), hint[Point(0, 2)]);
        assert_eq!(None, hint[Point(0, 3)]);
        assert_eq!(None, hint[Point(0, 4)]);
        assert_eq!(None, hint[Point(0, 5)]);
        assert_eq!(None, hint[Point(1, 0)]);
        assert_eq!(None, hint[Point(1, 1)]);
        assert_eq!(None, hint[Point(1, 2)]);
        assert_eq!(None, hint[Point(1, 3)]);
        assert_eq!(None, hint[Point(1, 4)]);
        assert_eq!(None, hint[Point(1, 5)]);
        assert_eq!(Some(3), hint[Point(2, 0)]);
        assert_eq!(None, hint[Point(2, 1)]);
        assert_eq!(None, hint[Point(2, 2)]);
        assert_eq!(None, hint[Point(2, 3)]);
        assert_eq!(None, hint[Point(2, 4)]);
        assert_eq!(None, hint[Point(2, 5)]);

        assert_eq!(&puzzle,
                   puzzle.to_string().parse::<Puzzle>().as_ref().unwrap());

        assert!("1243".parse::<Puzzle>().is_err());

        let input = "
+--+ +-+!!+asdf
+  + + +  +
|  |1|    |
|  | |  2 |
+  + + +  +
";
        let output = "+-+ +-+ +

+ + + + +
| |1|  2|
+ + + + +
";

        let puzzle = input.parse::<Puzzle>().unwrap();
        let hint = puzzle.hint();
        assert_eq!(Some(1), hint[Point(1, 1)]);
        assert_eq!(Some(2), hint[Point(1, 3)]);
        assert_eq!(output, puzzle.to_string());

        assert!("".parse::<Puzzle>().is_err());

        let input = "
+ + + +
 1 2 3
+ + + +
";
        let puzzle = input.parse::<Puzzle>().unwrap();
        let hint = puzzle.hint();
        assert_eq!(Some(1), hint[Point(0, 0)]);
        assert_eq!(Some(2), hint[Point(0, 1)]);
        assert_eq!(Some(3), hint[Point(0, 2)]);
    }
}
<|endoftext|>


<commit_before>use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

fn main() {

    let stdin = std::io::stdin();
    let reader = stdin.lock();
    let mut writer = std::io::stdout();

    let mut pairs = vec![0u32; 2];
    for readline in reader.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let elts: Vec<&str> = line[..].split("\t").collect();
            pairs[0] = elts[0].parse().ok().expect("malformed src");
            pairs[1] = elts[1].parse().ok().expect("malformed dst");
            writer.write_all(unsafe { _typed_as_byte_slice(&pairs[..]) }).unwrap();
        }
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<commit_msg>added printer<commit_after>use std::io::{Read, BufRead, BufReader, BufWriter, Write, Result};
use std::fs::File;
use std::slice;
use std::mem;

fn main() {

    let parse_mode = match std::env::args().skip(1).next().as_ref().map(|x| &x[..]) {
        Some("parse") => true,
        Some("print") => false,
        _ => panic!("parse_mode not one of print or parse"),
    };

    let stdin = std::io::stdin();
    let mut reader = stdin.lock();
    let mut writer = std::io::stdout();

    if parse_mode {

        let mut pairs = vec![0u32; 2];
        for readline in reader.lines() {
            let line = readline.ok().expect("read error");
            if !line.starts_with('#') {
                let elts: Vec<&str> = line[..].split("\t").collect();
                pairs[0] = elts[0].parse().ok().expect("malformed src");
                pairs[1] = elts[1].parse().ok().expect("malformed dst");
                writer.write_all(unsafe { typed_as_byte_slices(&pairs[..]) }).unwrap();
            }
        }

    }
    else {

        let mut bytes = vec![0u8; 1024];
        while let Ok(edges) = read_as_typed::<_,(u32,u32)>(&mut reader, &mut bytes) {
            for &(src,dst) in edges {
                println!("{} {}", src,dst);
            }
        }

    }
}

unsafe fn typed_as_byte_slices<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}

// naughty method using unsafe transmute to read a filled binary buffer as a typed buffer
fn read_as_typed<'a, R: Read, T: Copy>(reader: &mut R, buffer: &'a mut[u8]) -> Result<&'a[T]> {
    if mem::size_of::<T>() * (buffer.len() / mem::size_of::<T>()) < buffer.len() {
        panic!("buffer size must be a multiple of mem::size_of::<T>() = {:?}", mem::size_of::<T>());
    }

    let mut read = try!(reader.read(buffer));
    while mem::size_of::<T>() * (read / mem::size_of::<T>()) < read {
        read += try!(reader.read(&mut buffer[read..]));
    }

    Ok(unsafe { ::std::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut T, read / mem::size_of::<T>()) } )
}
<|endoftext|>