<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 std::collections::HashMap;
use std::iter::IntoIterator;
use std::slice::{Iter, IterMut};

use uuid::Uuid;

use super::engine::{HasName, HasUuid};

/// Map UUID and name to T items.
#[derive(Debug)]
pub struct Table<T: HasName + HasUuid> {
    items: Vec<T>,
    name_map: HashMap<String, usize>,
    uuid_map: HashMap<Uuid, usize>,
}

impl<T: HasName + HasUuid> Default for Table<T> {
    fn default() -> Table<T> {
        Table {
            items: Vec::default(),
            name_map: HashMap::default(),
            uuid_map: HashMap::default(),
        }
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a mut Table<T> {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;

    fn into_iter(self) -> IterMut<'a, T> {
        self.items.iter_mut()
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a Table<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.items.iter()
    }
}

/// All operations are O(1).
/// The implementation does not priviledge the name key over the UUID key
/// in any way. They are both treated as constants once the item has been
/// inserted. In order to rename a T item, it must be removed, renamed, and
/// reinserted under the new name.
impl<T: HasName + HasUuid> Table<T> {
    /// Empty this table of all its items, returning them in a vector.
    pub fn empty(self) -> Vec<T> {
        self.items
    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

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

    /// Returns true if map has an item corresponding to this name, else false.
    pub fn contains_name(&self, name: &str) -> bool {
        self.name_map.contains_key(name)
    }

    /// Returns true if map has an item corresponding to this uuid, else false.
    pub fn contains_uuid(&self, uuid: Uuid) -> bool {
        self.uuid_map.contains_key(&uuid)
    }

    /// Get item by name.
    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_map.get(name).map(|index| &self.items[*index])
    }

    /// Get item by uuid.
    pub fn get_by_uuid(&self, uuid: Uuid) -> Option<&T> {
        self.uuid_map
            .get(&uuid)
            .map(|index| &self.items[*index])
    }

    /// Get mutable item by name.
    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
        if let Some(index) = self.name_map.get(name) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Get mutable item by uuid.
    pub fn get_mut_by_uuid(&mut self, uuid: Uuid) -> Option<&mut T> {
        if let Some(index) = self.uuid_map.get(&uuid) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Removes the item corresponding to name if there is one.
    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
        if let Some(index) = self.name_map.remove(name) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("name_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.uuid_map.remove(&item.uuid());

            // Remove the name again, in case there is only one item.
            self.name_map.remove(name);

            Some(item)
        } else {
            None
        }
    }

    /// Removes the item corresponding to the uuid if there is one.
    pub fn remove_by_uuid(&mut self, uuid: Uuid) -> Option<T> {
        if let Some(index) = self.uuid_map.remove(&uuid) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("uuid_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.name_map.remove(item.name());

            // Remove the uuid again, in case there is only one item.
            self.uuid_map.remove(&uuid);

            Some(item)
        } else {
            None
        }
    }

    /// Inserts an item for given uuid and name.
    /// Returns a list of the items displaced, which may be empty if no items
    /// are displaced, have one entry if the uuid and the name map to the same
    /// item, and may have two entries if the uuid and the name map to
    /// different items.
    pub fn insert(&mut self, item: T) -> Vec<T> {
        let name_item = self.remove_by_name(item.name());
        let uuid_item = self.remove_by_uuid(item.uuid());

        let future_last_index = self.items.len();
        self.name_map
            .insert(item.name().into(), future_last_index);
        self.uuid_map.insert(item.uuid(), future_last_index);

        self.items.push(item);

        match (name_item, uuid_item) {
            (None, None) => vec![],
            (None, Some(uuid_item)) => vec![uuid_item],
            (Some(name_item), None) => vec![name_item],
            (Some(name_item), Some(uuid_item)) => vec![name_item, uuid_item],
        }
    }
}

#[cfg(test)]
mod tests {

    use rand;
    use uuid::Uuid;

    use super::super::engine::{HasName, HasUuid};

    use super::Table;

    #[derive(Debug)]
    struct TestThing {
        name: String,
        uuid: Uuid,
        stuff: u32,
    }

    // A global invariant checker for the table.
    // Verifies proper relationship between internal data structures.
    fn table_invariant<T>(table: &Table<T>) -> ()
        where T: HasName + HasUuid
    {
        let ref items = table.items;
        let ref name_map = table.name_map;
        let ref uuid_map = table.uuid_map;
        for i in 0..items.len() {
            let name = items[i].name();
            let uuid = items[i].uuid();
            assert_eq!(name_map.get(name).unwrap(), &i);
            assert_eq!(uuid_map.get(&uuid).unwrap(), &i);
        }

        for name in name_map.keys() {
            let index = name_map.get(name).unwrap();
            assert_eq!(items[*index].name(), name);
        }

        for &uuid in uuid_map.keys() {
            let index = uuid_map.get(&uuid).unwrap();
            assert_eq!(items[*index].uuid(), uuid);
        }

    }

    impl TestThing {
        pub fn new(name: &str, uuid: Uuid) -> TestThing {
            TestThing {
                name: name.to_owned(),
                uuid: uuid.clone(),
                stuff: rand::random::<u32>(),
            }
        }
    }

    impl HasUuid for TestThing {
        fn uuid(&self) -> Uuid {
            self.uuid
        }
    }

    impl HasName for TestThing {
        fn name(&self) -> &str {
            &self.name
        }
    }

    #[test]
    /// Remove a test object by its uuid.
    /// Mutate the removed test object.
    /// Verify that the table is now empty and that removing by name yields
    /// no result.
    fn remove_existing_item() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        t.insert(TestThing::new(&name, uuid));
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_some());
        assert!(t.get_by_uuid(uuid).is_some());
        let thing = t.remove_by_uuid(uuid);
        table_invariant(&t);
        assert!(thing.is_some());
        let mut thing = thing.unwrap();
        thing.stuff = 0;
        assert!(t.is_empty());
        assert!(t.remove_by_name(&name).is_none());
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_none());
        assert!(t.get_by_uuid(uuid).is_none());
    }

    #[test]
    /// Insert a thing and then insert another thing with same keys.
    /// The previously inserted thing should be returned.
    /// You can't insert the identical thing, because that would be a move.
    /// This is good, because then you can't have a thing that is both in
    /// the table and not in the table.
    fn insert_same_keys() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;
        let displaced = t.insert(thing);
        table_invariant(&t);

        // There was nothing previously, so displaced must be empty.
        assert!(displaced.is_empty());

        // t now contains the inserted thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key);

        // Add another thing with the same keys.
        let thing2 = TestThing::new(&name, uuid);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // It has displaced the old thing.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);

        // But it contains a thing with the same keys.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }

    #[test]
    /// Insert a thing and then insert another thing with the same name.
    /// The previously inserted thing should be returned.
    fn insert_same_name() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;

        // There was nothing in the table before, so displaced is empty.
        let displaced = t.insert(thing);
        table_invariant(&t);
        assert!(displaced.is_empty());

        // t now contains thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));

        // Insert new item with different UUID.
        let uuid2 = Uuid::new_v4();
        let thing2 = TestThing::new(&name, uuid2);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // The items displaced consist exactly of the first item.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);
        assert_eq!(displaced_item.stuff, thing_key);

        // The table contains the new item and has no memory of the old.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid2));
        assert!(!t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid2).unwrap().stuff, thing_key2);
        assert_eq!(t.get_by_name(&name).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }
}
<commit_msg>Fix typo<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 std::collections::HashMap;
use std::iter::IntoIterator;
use std::slice::{Iter, IterMut};

use uuid::Uuid;

use super::engine::{HasName, HasUuid};

/// Map UUID and name to T items.
#[derive(Debug)]
pub struct Table<T: HasName + HasUuid> {
    items: Vec<T>,
    name_map: HashMap<String, usize>,
    uuid_map: HashMap<Uuid, usize>,
}

impl<T: HasName + HasUuid> Default for Table<T> {
    fn default() -> Table<T> {
        Table {
            items: Vec::default(),
            name_map: HashMap::default(),
            uuid_map: HashMap::default(),
        }
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a mut Table<T> {
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;

    fn into_iter(self) -> IterMut<'a, T> {
        self.items.iter_mut()
    }
}

impl<'a, T: HasName + HasUuid> IntoIterator for &'a Table<T> {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.items.iter()
    }
}

/// All operations are O(1).
/// The implementation does not privilege the name key over the UUID key
/// in any way. They are both treated as constants once the item has been
/// inserted. In order to rename a T item, it must be removed, renamed, and
/// reinserted under the new name.
impl<T: HasName + HasUuid> Table<T> {
    /// Empty this table of all its items, returning them in a vector.
    pub fn empty(self) -> Vec<T> {
        self.items
    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

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

    /// Returns true if map has an item corresponding to this name, else false.
    pub fn contains_name(&self, name: &str) -> bool {
        self.name_map.contains_key(name)
    }

    /// Returns true if map has an item corresponding to this uuid, else false.
    pub fn contains_uuid(&self, uuid: Uuid) -> bool {
        self.uuid_map.contains_key(&uuid)
    }

    /// Get item by name.
    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_map.get(name).map(|index| &self.items[*index])
    }

    /// Get item by uuid.
    pub fn get_by_uuid(&self, uuid: Uuid) -> Option<&T> {
        self.uuid_map
            .get(&uuid)
            .map(|index| &self.items[*index])
    }

    /// Get mutable item by name.
    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
        if let Some(index) = self.name_map.get(name) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Get mutable item by uuid.
    pub fn get_mut_by_uuid(&mut self, uuid: Uuid) -> Option<&mut T> {
        if let Some(index) = self.uuid_map.get(&uuid) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Removes the item corresponding to name if there is one.
    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
        if let Some(index) = self.name_map.remove(name) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("name_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.uuid_map.remove(&item.uuid());

            // Remove the name again, in case there is only one item.
            self.name_map.remove(name);

            Some(item)
        } else {
            None
        }
    }

    /// Removes the item corresponding to the uuid if there is one.
    pub fn remove_by_uuid(&mut self, uuid: Uuid) -> Option<T> {
        if let Some(index) = self.uuid_map.remove(&uuid) {
            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items
                                     .last()
                                     .expect("uuid_map is non-empty <-> items is non-empty");
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.name_map.remove(item.name());

            // Remove the uuid again, in case there is only one item.
            self.uuid_map.remove(&uuid);

            Some(item)
        } else {
            None
        }
    }

    /// Inserts an item for given uuid and name.
    /// Returns a list of the items displaced, which may be empty if no items
    /// are displaced, have one entry if the uuid and the name map to the same
    /// item, and may have two entries if the uuid and the name map to
    /// different items.
    pub fn insert(&mut self, item: T) -> Vec<T> {
        let name_item = self.remove_by_name(item.name());
        let uuid_item = self.remove_by_uuid(item.uuid());

        let future_last_index = self.items.len();
        self.name_map
            .insert(item.name().into(), future_last_index);
        self.uuid_map.insert(item.uuid(), future_last_index);

        self.items.push(item);

        match (name_item, uuid_item) {
            (None, None) => vec![],
            (None, Some(uuid_item)) => vec![uuid_item],
            (Some(name_item), None) => vec![name_item],
            (Some(name_item), Some(uuid_item)) => vec![name_item, uuid_item],
        }
    }
}

#[cfg(test)]
mod tests {

    use rand;
    use uuid::Uuid;

    use super::super::engine::{HasName, HasUuid};

    use super::Table;

    #[derive(Debug)]
    struct TestThing {
        name: String,
        uuid: Uuid,
        stuff: u32,
    }

    // A global invariant checker for the table.
    // Verifies proper relationship between internal data structures.
    fn table_invariant<T>(table: &Table<T>) -> ()
        where T: HasName + HasUuid
    {
        let ref items = table.items;
        let ref name_map = table.name_map;
        let ref uuid_map = table.uuid_map;
        for i in 0..items.len() {
            let name = items[i].name();
            let uuid = items[i].uuid();
            assert_eq!(name_map.get(name).unwrap(), &i);
            assert_eq!(uuid_map.get(&uuid).unwrap(), &i);
        }

        for name in name_map.keys() {
            let index = name_map.get(name).unwrap();
            assert_eq!(items[*index].name(), name);
        }

        for &uuid in uuid_map.keys() {
            let index = uuid_map.get(&uuid).unwrap();
            assert_eq!(items[*index].uuid(), uuid);
        }

    }

    impl TestThing {
        pub fn new(name: &str, uuid: Uuid) -> TestThing {
            TestThing {
                name: name.to_owned(),
                uuid: uuid.clone(),
                stuff: rand::random::<u32>(),
            }
        }
    }

    impl HasUuid for TestThing {
        fn uuid(&self) -> Uuid {
            self.uuid
        }
    }

    impl HasName for TestThing {
        fn name(&self) -> &str {
            &self.name
        }
    }

    #[test]
    /// Remove a test object by its uuid.
    /// Mutate the removed test object.
    /// Verify that the table is now empty and that removing by name yields
    /// no result.
    fn remove_existing_item() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        t.insert(TestThing::new(&name, uuid));
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_some());
        assert!(t.get_by_uuid(uuid).is_some());
        let thing = t.remove_by_uuid(uuid);
        table_invariant(&t);
        assert!(thing.is_some());
        let mut thing = thing.unwrap();
        thing.stuff = 0;
        assert!(t.is_empty());
        assert!(t.remove_by_name(&name).is_none());
        table_invariant(&t);

        assert!(t.get_by_name(&name).is_none());
        assert!(t.get_by_uuid(uuid).is_none());
    }

    #[test]
    /// Insert a thing and then insert another thing with same keys.
    /// The previously inserted thing should be returned.
    /// You can't insert the identical thing, because that would be a move.
    /// This is good, because then you can't have a thing that is both in
    /// the table and not in the table.
    fn insert_same_keys() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;
        let displaced = t.insert(thing);
        table_invariant(&t);

        // There was nothing previously, so displaced must be empty.
        assert!(displaced.is_empty());

        // t now contains the inserted thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key);

        // Add another thing with the same keys.
        let thing2 = TestThing::new(&name, uuid);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // It has displaced the old thing.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);

        // But it contains a thing with the same keys.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }

    #[test]
    /// Insert a thing and then insert another thing with the same name.
    /// The previously inserted thing should be returned.
    fn insert_same_name() {
        let mut t: Table<TestThing> = Table::default();
        table_invariant(&t);

        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, uuid);
        let thing_key = thing.stuff;

        // There was nothing in the table before, so displaced is empty.
        let displaced = t.insert(thing);
        table_invariant(&t);
        assert!(displaced.is_empty());

        // t now contains thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid));

        // Insert new item with different UUID.
        let uuid2 = Uuid::new_v4();
        let thing2 = TestThing::new(&name, uuid2);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);
        table_invariant(&t);

        // The items displaced consist exactly of the first item.
        assert_eq!(displaced.len(), 1);
        let ref displaced_item = displaced[0];
        assert_eq!(displaced_item.name(), name);
        assert_eq!(displaced_item.uuid(), uuid);
        assert_eq!(displaced_item.stuff, thing_key);

        // The table contains the new item and has no memory of the old.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(uuid2));
        assert!(!t.contains_uuid(uuid));
        assert_eq!(t.get_by_uuid(uuid2).unwrap().stuff, thing_key2);
        assert_eq!(t.get_by_name(&name).unwrap().stuff, thing_key2);
        assert_eq!(t.len(), 1);
    }
}
<|endoftext|>


<commit_before>use symbols::*;
use support::*;

//TODO change String to Symbol
///start, end, name, namespace
pub struct MemberBlock (pub usize, pub usize, pub String, pub String, pub Option<Vec<String>>);

pub struct ClassStructure
{
    //TODO add constructor vector
    pub static_variables: Vec<MemberBlock>,
    pub static_methods: Vec<MemberBlock>,
    pub static_classes: Vec<MemberBlock>,

    pub non_static_variables: Vec<MemberBlock>,
    pub non_static_methods: Vec<MemberBlock>,
    pub non_static_classes: Vec<MemberBlock>,
}

impl ClassStructure
{
    pub fn new() -> ClassStructure
    {
        ClassStructure
        {
            static_variables: Vec::new(),
            static_methods: Vec::new(),
            static_classes: Vec::new(),

            non_static_variables: Vec::new(),
            non_static_methods: Vec::new(),
            non_static_classes: Vec::new(),
        }
    }
}

pub struct SymbolTable
{
    pub children_scopes: Vec<Symbol>,
}

impl<'a> SymbolTable
{
    pub fn new() -> SymbolTable
    {
        SymbolTable
        {
            children_scopes: Vec::new(),
        }
    }
}

impl StaticSymbolTable for SymbolTable
{
    /// Return all symbols in this table with the specified name (in any namespace)
    fn lookup_by_name(&self, name: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.name == name
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.namespace == namespace
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
    /// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('.').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect(".");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Variable(ref variable_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol>
    {
        //TODO use argument_types
        let mut namespaces: Vec<&str> = namespace.split_terminator('.').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect(".");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a structure (class, enum) by its name and namespace.
    /// Duplicate classes in the same namespace are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol)>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('.').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect(".");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Structure(ref sub_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Adds a symbol to this table and allocates it's location
    /// Returns true if the symbol could be added; false otherwise
    /// Duplicate symbols are not allowed
    /// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass, namespace: String, name: String, is_static: bool, in_method: bool, is_parameter: bool, local_variable_count: u16, static_variable_count: u16, parameter_offset: u16) -> bool
    {
        let mut static_label = namespace.replace(".", "_").clone();
        static_label.push_str("_static");
        let mut method_namespace = namespace.replace(".", "_").clone();
        method_namespace.push_str("_");
        method_namespace.push_str(&*name);

        //TODO add return false
        let mut location: SymbolLocation = match class
        {
            //TODO replace with storing logic
            SymbolClass::Structure(ref sub_type) => SymbolLocation::Structured,
            SymbolClass::Variable(ref variable_type) => match in_method
                {
                    true => match is_parameter
                        {
                            true => SymbolLocation::MethodArgument(parameter_offset),
                            false =>SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                        },
                    false => match is_static
                        {
                            true => SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                            false => SymbolLocation::InstancedMemory(local_variable_count * 4),
                        },
                },
            SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => SymbolLocation::Memory(MemoryAddress {label_name: method_namespace, offset: 0}),
        };

        let mut symbol: Symbol =  Symbol
        {
            name: name,
            namespace: namespace,
            is_static: is_static,
            symbol_class: class,
            location: location,
        };

        self.children_scopes.push(symbol);
        return true;
    }
}
<commit_msg>Change to underscore namespace searching<commit_after>use symbols::*;
use support::*;

//TODO change String to Symbol
///start, end, name, namespace
pub struct MemberBlock (pub usize, pub usize, pub String, pub String, pub Option<Vec<String>>);

pub struct ClassStructure
{
    //TODO add constructor vector
    pub static_variables: Vec<MemberBlock>,
    pub static_methods: Vec<MemberBlock>,
    pub static_classes: Vec<MemberBlock>,

    pub non_static_variables: Vec<MemberBlock>,
    pub non_static_methods: Vec<MemberBlock>,
    pub non_static_classes: Vec<MemberBlock>,
}

impl ClassStructure
{
    pub fn new() -> ClassStructure
    {
        ClassStructure
        {
            static_variables: Vec::new(),
            static_methods: Vec::new(),
            static_classes: Vec::new(),

            non_static_variables: Vec::new(),
            non_static_methods: Vec::new(),
            non_static_classes: Vec::new(),
        }
    }
}

pub struct SymbolTable
{
    pub children_scopes: Vec<Symbol>,
}

impl<'a> SymbolTable
{
    pub fn new() -> SymbolTable
    {
        SymbolTable
        {
            children_scopes: Vec::new(),
        }
    }
}

impl StaticSymbolTable for SymbolTable
{
    /// Return all symbols in this table with the specified name (in any namespace)
    fn lookup_by_name(&self, name: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.name == name
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Return all symbols in this table with the specified namespace
	fn lookup_by_namespace(&self, namespace: &str) -> Vec<&Symbol>
    {
        let mut symbols: Vec<&Symbol> = Vec::new();
        for symbol in self.children_scopes.iter()
        {
            if symbol.namespace == namespace
            {
                symbols.push(symbol.clone());
            }
        }
        symbols
    }

    /// Lookup a variable by its name and namespace. Duplicate symbols are not allowed, so the result will be unique
    /// @return the specified symbol or None if the specified symbol is not in this namespace
	fn lookup_variable(&self, namespace: &str, name: &str) -> Option<&Symbol>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('_').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect("_");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Variable(ref variable_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a function by its name and namespace. Functions with the same signature are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_function(&self, namespace: &str, name: &str, argument_types: &Vec<String>) -> Option<&Symbol>
    {
        //TODO use argument_types
        let mut namespaces: Vec<&str> = namespace.split_terminator('.').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect(".");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Lookup a structure (class, enum) by its name and namespace.
    /// Duplicate classes in the same namespace are not allowed, so the result will be unique
    /// If no result is found in the direct namespace, the parent namespaces will be searched
    /// @return the specified symbol or None if the specified symbol is not in this namespace or a parent namespace
	fn lookup_structure(&self, namespace: &str, name: &str) -> Option<(&Symbol)>
    {
        let mut namespaces: Vec<&str> = namespace.split_terminator('.').collect();
        let mut length;
        let mut current_namespace;

        loop
        {
            current_namespace = namespaces.connect(".");

            if namespaces.is_empty()
            {
                break;
            }
            else
            {
                for symbol in self.lookup_by_namespace(&*current_namespace).iter()
                {
                    if symbol.namespace == current_namespace
                    {
                        if symbol.name == name
                        {
                            match symbol.symbol_class
                            {
                                SymbolClass::Structure(ref sub_type) => return Some((symbol).clone()),
                                _ => continue,
                            };
                        }
                    }
                }
                length = namespaces.len();
                namespaces.remove(length - 1);
            }
        }
        None
    }

    /// Adds a symbol to this table and allocates it's location
    /// Returns true if the symbol could be added; false otherwise
    /// Duplicate symbols are not allowed
    /// TODO: support overloaded methods
	fn add(&mut self, class: SymbolClass, namespace: String, name: String, is_static: bool, in_method: bool, is_parameter: bool, local_variable_count: u16, static_variable_count: u16, parameter_offset: u16) -> bool
    {
        let mut static_label = namespace.replace(".", "_").clone();
        static_label.push_str("_static");
        let mut method_namespace = namespace.replace(".", "_").clone();
        method_namespace.push_str("_");
        method_namespace.push_str(&*name);

        //TODO add return false
        let mut location: SymbolLocation = match class
        {
            //TODO replace with storing logic
            SymbolClass::Structure(ref sub_type) => SymbolLocation::Structured,
            SymbolClass::Variable(ref variable_type) => match in_method
                {
                    true => match is_parameter
                        {
                            true => SymbolLocation::MethodArgument(parameter_offset),
                            false =>SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                        },
                    false => match is_static
                        {
                            true => SymbolLocation::Memory(MemoryAddress {label_name: static_label, offset: static_variable_count * 4}),
                            false => SymbolLocation::InstancedMemory(local_variable_count * 4),
                        },
                },
            SymbolClass::Function(ref return_type, ref arguments, ref static_label, static_length) => SymbolLocation::Memory(MemoryAddress {label_name: method_namespace, offset: 0}),
        };

        let mut symbol: Symbol =  Symbol
        {
            name: name,
            namespace: namespace,
            is_static: is_static,
            symbol_class: class,
            location: location,
        };

        self.children_scopes.push(symbol);
        return true;
    }
}
<|endoftext|>


<commit_before>use libc::{ENOENT, EACCES};
use fuse::{FileType, ReplyAttr, ReplyEntry, ReplyDirectory, Request, ReplyEmpty};

use fs::constants;
use fs::GoodDataFS;
use fs::helpers::create_inode_directory_attributes;
use fs::inode;
use object::{Project, ProjectCreate, ProjectCreateBody, ProjectCreateMeta, ProjectCreateContent};

use super::item;

use std::path::Path;

// TODO: This probably needs to be generated dynamically
pub const PROJECTS_ITEMS: [item::ProjectItem; 0] = [];

pub fn getattr(fs: &mut GoodDataFS, _req: &Request, ino: u64, reply: ReplyAttr) {
    match ino {
        constants::INODE_PROJECTS => {
            reply.attr(&constants::DEFAULT_TTL, &fs.get_projects_dir_attributes())
        }
        _ => {
            let inode = inode::Inode::deserialize(ino);
            if inode.project > 0 && inode.category == constants::Category::Internal as u8 &&
               inode.item == 0 && inode.reserved == 0 {
                reply.attr(&constants::DEFAULT_TTL,
                           &create_inode_directory_attributes(ino))
            }
        }
    }
}

pub fn lookup(fs: &mut GoodDataFS, _req: &Request, _parent: u64, name: &Path, reply: ReplyEntry) {
    match name.to_str() {
        Some(constants::PROJECTS_JSON_FILENAME) => {
            reply.entry(&constants::DEFAULT_TTL,
                        &fs.get_projects_json_attributes(),
                        0);
        }
        _ => {
            let mut i: u64 = 0;
            for project in fs.client.projects_fetch_if_none() {
                let title: &String = project.project()
                    .meta()
                    .title()
                    .as_ref()
                    .unwrap();

                if title == name.to_str().unwrap() {
                    break;
                }
                i += 1;
            }

            if i < fs.client().projects().as_ref().unwrap().len() as u64 {
                let inode = (i + 1) << 48;
                let attr = create_inode_directory_attributes(inode);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else {
                reply.error(ENOENT);
            }
        }
    }
}

pub fn readdir(fs: &mut GoodDataFS,
               _req: &Request,
               ino: u64,
               _fh: u64,
               in_offset: u64,
               mut reply: ReplyDirectory) {

    fs.client.projects_fetch();

    let mut offset: u64 = in_offset;
    for project in fs.client().projects().as_ref().unwrap().into_iter() {
        let title: &String = project.project()
            .meta()
            .title()
            .as_ref()
            .unwrap();

        let inode = inode::Inode {
            project: (offset + 1) as u16,
            category: 0,
            item: 0,
            reserved: 0,
        };
        info!("readdir() - Adding path {:?}, inode {:?}", title, inode);
        // let sanitized = re.replace_all(&title[..], "_");
        reply.add(ino, in_offset, FileType::Directory, title);
        offset += 1;
    }

    reply.add(ino,
              offset,
              FileType::RegularFile,
              constants::PROJECTS_JSON_FILENAME);

    reply.ok();

    // TODO: Refactor items above in this
    // let mut offset = 0;
    //
    // // Iterate over all projects::PROJECTS_ITEMS
    // for item in items::projects::PROJECTS_ITEMS.into_iter() {
    // }
}

pub fn create(fs: &mut GoodDataFS, name: &Path, reply: ReplyEntry) {
    match fs.client().token.clone() {
        Some(token) => {
            let project = ProjectCreate {
                project: ProjectCreateBody {
                    content: ProjectCreateContent {
                        guidedNavigation: "1".to_string(),
                        environment: fs.client().environment.clone().unwrap(),
                        driver: fs.client().driver.clone().unwrap(),
                        authorizationToken: token,
                    },
                    meta: ProjectCreateMeta {
                        title: name.to_str().unwrap().to_string(),
                        summary: name.to_str().unwrap().to_string(),
                    },
                },
            };
            fs.client.create_project(project);
            let inode = ((fs.client.projects.as_ref().unwrap().len()) as u64) << 48;
            let attr = create_inode_directory_attributes(inode);
            reply.entry(&constants::DEFAULT_TTL, &attr, 0);
        }
        None => reply.error(EACCES),
    }
}

pub fn rmdir(fs: &mut GoodDataFS, name: &Path, reply: ReplyEmpty) {
    let title = &name.to_str().unwrap().to_string();
    match find_project_by_title(fs.client().projects().as_ref().unwrap(), title) {
        Some(project) => {
            fs.client.delete_project(project);
            fs.client.projects_fetch();
            reply.ok();
        }
        None => {
            error!("WTF? Project not found: {}", title);
            reply.error(ENOENT);
        }
    }
}

fn find_project_by_title(projects: &Vec<Project>, title: &String) -> Option<Project> {
    for project in projects {
        if project.project().meta().title().as_ref().unwrap() == title {
            return Some(project.clone());
        }
    }
    None
}
<commit_msg>Sanitize project title<commit_after>use libc::{ENOENT, EACCES};
use fuse::{FileType, ReplyAttr, ReplyEntry, ReplyDirectory, Request, ReplyEmpty};

use fs::constants;
use fs::GoodDataFS;
use fs::helpers::create_inode_directory_attributes;
use fs::inode;
use object::{Project, ProjectCreate, ProjectCreateBody, ProjectCreateMeta, ProjectCreateContent};

use super::item;

use std::path::Path;

use regex::Regex;

// TODO: This probably needs to be generated dynamically
pub const PROJECTS_ITEMS: [item::ProjectItem; 0] = [];

pub fn getattr(fs: &mut GoodDataFS, _req: &Request, ino: u64, reply: ReplyAttr) {
    match ino {
        constants::INODE_PROJECTS => {
            reply.attr(&constants::DEFAULT_TTL, &fs.get_projects_dir_attributes())
        }
        _ => {
            let inode = inode::Inode::deserialize(ino);
            if inode.project > 0 && inode.category == constants::Category::Internal as u8 &&
               inode.item == 0 && inode.reserved == 0 {
                reply.attr(&constants::DEFAULT_TTL,
                           &create_inode_directory_attributes(ino))
            }
        }
    }
}

pub fn lookup(fs: &mut GoodDataFS, _req: &Request, _parent: u64, name: &Path, reply: ReplyEntry) {
    match name.to_str() {
        Some(constants::PROJECTS_JSON_FILENAME) => {
            reply.entry(&constants::DEFAULT_TTL,
                        &fs.get_projects_json_attributes(),
                        0);
        }
        _ => {
            let mut i: u64 = 0;
            for project in fs.client.projects_fetch_if_none() {
                let title: &String = project.project()
                    .meta()
                    .title()
                    .as_ref()
                    .unwrap();

                let re = Regex::new("[^a-zA-Z0-9]+").unwrap();
                let sanitized_title = re.replace_all(&title[..], "_");

                if sanitized_title == name.to_str().unwrap() {
                    break;
                }
                i += 1;
            }

            if i < fs.client().projects().as_ref().unwrap().len() as u64 {
                let inode = (i + 1) << 48;
                let attr = create_inode_directory_attributes(inode);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else {
                reply.error(ENOENT);
            }
        }
    }
}

pub fn readdir(fs: &mut GoodDataFS,
               _req: &Request,
               ino: u64,
               _fh: u64,
               in_offset: u64,
               mut reply: ReplyDirectory) {

    fs.client.projects_fetch();

    let mut offset: u64 = in_offset;
    for project in fs.client().projects().as_ref().unwrap().into_iter() {
        let title: &String = project.project()
            .meta()
            .title()
            .as_ref()
            .unwrap();

        let inode = inode::Inode {
            project: (offset + 1) as u16,
            category: 0,
            item: 0,
            reserved: 0,
        };
        info!("readdir() - Adding path {:?}, inode {:?}", title, inode);
        let re = Regex::new("[^a-zA-Z0-9]+").unwrap();
        let sanitized = re.replace_all(&title[..], "_");
        reply.add(ino, in_offset, FileType::Directory, sanitized);
        offset += 1;
    }

    reply.add(ino,
              offset,
              FileType::RegularFile,
              constants::PROJECTS_JSON_FILENAME);

    reply.ok();

    // TODO: Refactor items above in this
    // let mut offset = 0;
    //
    // // Iterate over all projects::PROJECTS_ITEMS
    // for item in items::projects::PROJECTS_ITEMS.into_iter() {
    // }
}

pub fn create(fs: &mut GoodDataFS, name: &Path, reply: ReplyEntry) {
    match fs.client().token.clone() {
        Some(token) => {
            let project = ProjectCreate {
                project: ProjectCreateBody {
                    content: ProjectCreateContent {
                        guidedNavigation: "1".to_string(),
                        environment: fs.client().environment.clone().unwrap(),
                        driver: fs.client().driver.clone().unwrap(),
                        authorizationToken: token,
                    },
                    meta: ProjectCreateMeta {
                        title: name.to_str().unwrap().to_string(),
                        summary: name.to_str().unwrap().to_string(),
                    },
                },
            };
            fs.client.create_project(project);
            let inode = ((fs.client.projects.as_ref().unwrap().len()) as u64) << 48;
            let attr = create_inode_directory_attributes(inode);
            reply.entry(&constants::DEFAULT_TTL, &attr, 0);
        }
        None => reply.error(EACCES),
    }
}

pub fn rmdir(fs: &mut GoodDataFS, name: &Path, reply: ReplyEmpty) {
    let title = &name.to_str().unwrap().to_string();
    match find_project_by_title(fs.client().projects().as_ref().unwrap(), title) {
        Some(project) => {
            fs.client.delete_project(project);
            fs.client.projects_fetch();
            reply.ok();
        }
        None => {
            error!("WTF? Project not found: {}", title);
            reply.error(ENOENT);
        }
    }
}

fn find_project_by_title(projects: &Vec<Project>, title: &String) -> Option<Project> {
    let re = Regex::new("[^a-zA-Z0-9]+").unwrap();
    let sanitized_in = re.replace_all(&title[..], "_");

    for project in projects {
        let sanitized_project =
            re.replace_all(&project.project().meta().title().as_ref().unwrap()[..], "_");
        if sanitized_project == sanitized_in {
            return Some(project.clone());
        }
    }
    None
}
<|endoftext|>


<commit_before>//!
//! The script interpreter runs scripts directly from their parse tree. This is
//! a fairly slow way to run scripts but comparatively simple to implement.
//!

use std::result::Result;
use serde_json::*;

use silkthread_base::*;

use super::script::*;

///
/// A tool representing a script that will be interepreted
///
pub struct InterpretedScriptTool {
    statements: Vec<Script>
}

///
/// Creates an unquoted version of a string
///
fn unquote_string(string: &str) -> String {
    let chars: Vec<char>    = string.chars().collect();
    let mut result          = String::new();
    let mut index           = 1;
    while index < chars.len()-1 {
        // Push character
        let chr = chars[index];

        match chr {
            '\\' => {
                let quoted = chars[index+1];
                match quoted {
                    'n' => result.push('\n'),
                    'r' => result.push('\r'),
                    't' => result.push('\t'),
                    quoted => result.push(quoted)
                }
            },
            chr => result.push(chr)
        }

        // Next character
        index += 1;
    }

    result
}

///
/// Parses a number string
///
fn parse_number(number: &str) -> Value {
    if number.contains('.') || number.contains('e') || number.contains('E') {
        json![ number.parse::<f64>().unwrap() ]
    } else if number.starts_with("0x") {
        json![ i64::from_str_radix(&number[2..], 16).unwrap() ]
    } else {
        json![ number.parse::<i64>().unwrap() ]
    }
}

///
/// Script evaluation error
///
#[derive(Serialize, Deserialize)]
pub enum ScriptEvaluationError {
    /// Tried to evaluate an expression type that's not implemented yet
    ExpressionNotImplemented,

    /// Tried to evaluate a statement type that's not implemented yet
    StatementNotImplemented,

    /// Tried to look up a tool and it couldn't be found
    ToolNameNotFound,

    /// Found an expression that can't be treated as a tool where a tool name was expected
    ExpressionDoesNotEvaluateToTool
}

///
/// Creates an execution error
///
fn generate_script_error(error: ScriptEvaluationError, script: &Script) -> Value {
    json![{
        "error":                error,
        "failed-statement":     script
    }]
}

///
/// Creates an execution error relating to an expression
///
fn generate_expression_error(error: ScriptEvaluationError, expr: &Expression) -> Value {
    json![{
        "error":                error,
        "failed-expression":    expr
    }]
}

impl InterpretedScriptTool {
    ///
    /// Creates a new interpreted script tool from a set of statements
    ///
    pub fn from_statements(statements: Vec<Script>) -> InterpretedScriptTool {
        InterpretedScriptTool { statements: statements }
    }

    ///
    /// Attempts to evaluate an expression to a tool
    ///
    pub fn evaluate_expression_to_tool(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Box<Tool>, Value> {
        match expression {
            &Expression::Identifier(ref name)   => environment.parent_environment.get_json_tool(&name.matched).map_err(|_| generate_expression_error(ScriptEvaluationError::ToolNameNotFound, expression)),
            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionDoesNotEvaluateToTool, expression))
        }
    }

    ///
    /// Calls an expression representing a tool and calls it with the specified parameters
    ///
    pub fn call_tool(tool_name: &Expression, parameters: Value, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression_to_tool(tool_name, environment).and_then(|tool| {
            // TODO: create environment for the tool to run in

            // Invoke the tool and generate the final result
            tool.invoke_json(parameters, environment.parent_environment)
        })
    }

    ///
    /// Evaluates an 'apply' expression
    ///
    pub fn apply(&(ref tool, ref parameters): &(Expression, Expression), environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression(&parameters, environment).and_then(|parameters| {
            InterpretedScriptTool::call_tool(&tool, parameters, environment)
        })
    }

    ///
    /// Evaluates a series of expressions into an array
    ///
    pub fn evaluate_array(exprs: &Vec<Expression>, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        let mut result = vec![];

        for expr in exprs.iter() {
            match InterpretedScriptTool::evaluate_expression(expr, environment) {
                Ok(next) => result.push(next),
                Err(erm) => return Err(erm)
            }
        }

        Ok(Value::Array(result))
    }

    ///
    /// Evaluates a single expression
    ///
    pub fn evaluate_expression(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match expression {
            &Expression::Number(ref n)          => Ok(parse_number(&n.matched)),
            &Expression::String(ref s)          => Ok(Value::String(unquote_string(&s.matched))),

            &Expression::Identifier(_)          => InterpretedScriptTool::call_tool(expression, Value::Null, environment),
            &Expression::Apply(ref expr)        => InterpretedScriptTool::apply(&*expr, environment),

            &Expression::Array(ref exprs)       => InterpretedScriptTool::evaluate_array(exprs, environment),
            &Expression::Tuple(ref exprs)       => InterpretedScriptTool::evaluate_array(exprs, environment),

            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionNotImplemented, expression))
        }
    }

    ///
    /// Evaluates the result of executing a single statement
    ///
    pub fn evaluate_statement(statement: &Script, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match statement {
            &Script::RunCommand(ref expr)   => InterpretedScriptTool::evaluate_expression(expr, environment),

            _                               => Err(generate_script_error(ScriptEvaluationError::StatementNotImplemented, statement))
        }
    }
}

impl Tool for InterpretedScriptTool {
    fn invoke_json(&self, _input: Value, environment: &Environment) -> Result<Value, Value> {
        // Make the environment that this script will run in
        let mut script_environment = ScriptExecutionEnvironment::new(environment);

        // Execute the script
        let mut result = vec![];
        for statement in self.statements.iter() {
            // Evaluate the next statement
            let next_result = match Self::evaluate_statement(statement, &mut script_environment) {
                Ok(result) => result,

                // Fail immediately if any statement generates an error
                Err(fail) => return Err(fail)
            };

            // The script result is built up from the result of each statement
            // TODO: unless there's something like a return statement?
            result.push(next_result);
        }

        // Script is done
        Ok(Value::Array(result))
    }
}

///
/// Represents an execution environment for a running script
///
pub struct ScriptExecutionEnvironment<'a> {
    /// The environment where tools are drawn from
    parent_environment: &'a Environment
}

impl<'a> ScriptExecutionEnvironment<'a> {
    ///
    /// Creates a new script execution environment
    ///
    pub fn new(parent_environment: &'a Environment) -> ScriptExecutionEnvironment<'a> {
        ScriptExecutionEnvironment { parent_environment: parent_environment }
    }
}

#[cfg(test)]
mod test {
    use silkthread_base::basic::*;
    use super::*;

    #[test]
    fn can_evaluate_string() {
        let string_expr         = Expression::string("\"Foo\"");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&string_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Foo"))));
    }

    #[test]
    fn can_evaluate_number() {
        let num_expr            = Expression::number("42");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 42 ]));
    }

    #[test]
    fn can_evaluate_float_number_1() {
        let num_expr            = Expression::number("42.2");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 42.2 ]));
    }

    #[test]
    fn can_evaluate_float_number_2() {
        let num_expr            = Expression::number("42e1");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 42e1 ]));
    }

    #[test]
    fn can_evaluate_hex_number() {
        let num_expr            = Expression::number("0xabcd");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 0xabcd ]));
    }

    #[test]
    fn can_evaluate_array() {
        let array_expr          = Expression::Array(vec![Expression::number("1"), Expression::number("2"), Expression::number("3")]);
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&array_expr, &mut env);

        assert!(result == Ok(json![ [ 1,2,3 ] ]));
    }

    #[test]
    fn can_evaluate_tuple() {
        let tuple_expr          = Expression::Tuple(vec![Expression::number("1"), Expression::number("2"), Expression::number("3")]);
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tuple_expr, &mut env);

        assert!(result == Ok(json![ [ 1,2,3 ] ]));
    }

    #[test]
    fn can_call_tool() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::call_tool(&tool_expr, Value::Null, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_tool_call() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_execute_run_command() {
        let tool_expr           = Script::RunCommand(Expression::identifier("test"));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_statement(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_apply_expression() {
        let tool_expr           = Expression::Apply(Box::new((Expression::identifier("test"), Expression::string("\"Success\""))));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|s: String| s)));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }
}
<commit_msg>Add support for evaluating maps<commit_after>//!
//! The script interpreter runs scripts directly from their parse tree. This is
//! a fairly slow way to run scripts but comparatively simple to implement.
//!

use std::result::Result;
use serde_json::*;

use silkthread_base::*;

use super::script::*;

///
/// A tool representing a script that will be interepreted
///
pub struct InterpretedScriptTool {
    statements: Vec<Script>
}

///
/// Creates an unquoted version of a string
///
fn unquote_string(string: &str) -> String {
    let chars: Vec<char>    = string.chars().collect();
    let mut result          = String::new();
    let mut index           = 1;
    while index < chars.len()-1 {
        // Push character
        let chr = chars[index];

        match chr {
            '\\' => {
                let quoted = chars[index+1];
                match quoted {
                    'n' => result.push('\n'),
                    'r' => result.push('\r'),
                    't' => result.push('\t'),
                    quoted => result.push(quoted)
                }
            },
            chr => result.push(chr)
        }

        // Next character
        index += 1;
    }

    result
}

///
/// Parses a number string
///
fn parse_number(number: &str) -> Value {
    if number.contains('.') || number.contains('e') || number.contains('E') {
        json![ number.parse::<f64>().unwrap() ]
    } else if number.starts_with("0x") {
        json![ i64::from_str_radix(&number[2..], 16).unwrap() ]
    } else {
        json![ number.parse::<i64>().unwrap() ]
    }
}

///
/// Script evaluation error
///
#[derive(Serialize, Deserialize)]
pub enum ScriptEvaluationError {
    /// Tried to evaluate an expression type that's not implemented yet
    ExpressionNotImplemented,

    /// Tried to evaluate a statement type that's not implemented yet
    StatementNotImplemented,

    /// Tried to look up a tool and it couldn't be found
    ToolNameNotFound,

    /// Found an expression that can't be treated as a tool where a tool name was expected
    ExpressionDoesNotEvaluateToTool,

    /// Expressions used as keys in a map must evaluate to a string
    MapKeysMustEvaluateToAString,
}

///
/// Creates an execution error
///
fn generate_script_error(error: ScriptEvaluationError, script: &Script) -> Value {
    json![{
        "error":                error,
        "failed-statement":     script
    }]
}

///
/// Creates an execution error relating to an expression
///
fn generate_expression_error(error: ScriptEvaluationError, expr: &Expression) -> Value {
    json![{
        "error":                error,
        "failed-expression":    expr
    }]
}

impl InterpretedScriptTool {
    ///
    /// Creates a new interpreted script tool from a set of statements
    ///
    pub fn from_statements(statements: Vec<Script>) -> InterpretedScriptTool {
        InterpretedScriptTool { statements: statements }
    }

    ///
    /// Attempts to evaluate an expression to a tool
    ///
    pub fn evaluate_expression_to_tool(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Box<Tool>, Value> {
        match expression {
            &Expression::Identifier(ref name)   => environment.parent_environment.get_json_tool(&name.matched).map_err(|_| generate_expression_error(ScriptEvaluationError::ToolNameNotFound, expression)),
            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionDoesNotEvaluateToTool, expression))
        }
    }

    ///
    /// Calls an expression representing a tool and calls it with the specified parameters
    ///
    pub fn call_tool(tool_name: &Expression, parameters: Value, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression_to_tool(tool_name, environment).and_then(|tool| {
            // TODO: create environment for the tool to run in

            // Invoke the tool and generate the final result
            tool.invoke_json(parameters, environment.parent_environment)
        })
    }

    ///
    /// Evaluates an 'apply' expression
    ///
    pub fn apply(&(ref tool, ref parameters): &(Expression, Expression), environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        InterpretedScriptTool::evaluate_expression(&parameters, environment).and_then(|parameters| {
            InterpretedScriptTool::call_tool(&tool, parameters, environment)
        })
    }

    ///
    /// Evaluates a series of expressions into an array
    ///
    pub fn evaluate_array(exprs: &Vec<Expression>, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        let mut result = vec![];

        for expr in exprs.iter() {
            match InterpretedScriptTool::evaluate_expression(expr, environment) {
                Ok(next) => result.push(next),
                Err(erm) => return Err(erm)
            }
        }

        Ok(Value::Array(result))
    }

    ///
    /// Evaluates a series of expressions into an array
    ///
    pub fn evaluate_map(exprs: &Vec<(Expression, Expression)>, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        let mut result = Map::new();

        for &(ref key_expr, ref value_expr) in exprs.iter() {
            let key = match InterpretedScriptTool::evaluate_expression(key_expr, environment) {
                Ok(Value::String(key))  => key,
                Ok(_)                   => return Err(generate_expression_error(ScriptEvaluationError::MapKeysMustEvaluateToAString, key_expr)),
                Err(erm)                => return Err(erm)
            };

            let value = match InterpretedScriptTool::evaluate_expression(value_expr, environment) {
                Ok(value)   => value,
                Err(erm)    => return Err(erm)
            };

            result.insert(key, value);
        }

        Ok(Value::Object(result))
    }

    ///
    /// Evaluates a single expression
    ///
    pub fn evaluate_expression(expression: &Expression, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match expression {
            &Expression::Number(ref n)          => Ok(parse_number(&n.matched)),
            &Expression::String(ref s)          => Ok(Value::String(unquote_string(&s.matched))),

            &Expression::Identifier(_)          => InterpretedScriptTool::call_tool(expression, Value::Null, environment),
            &Expression::Apply(ref expr)        => InterpretedScriptTool::apply(&*expr, environment),

            &Expression::Array(ref exprs)       => InterpretedScriptTool::evaluate_array(exprs, environment),
            &Expression::Tuple(ref exprs)       => InterpretedScriptTool::evaluate_array(exprs, environment),
            &Expression::Map(ref exprs)         => InterpretedScriptTool::evaluate_map(exprs, environment),

            _                                   => Err(generate_expression_error(ScriptEvaluationError::ExpressionNotImplemented, expression))
        }
    }

    ///
    /// Evaluates the result of executing a single statement
    ///
    pub fn evaluate_statement(statement: &Script, environment: &mut ScriptExecutionEnvironment) -> Result<Value, Value> {
        match statement {
            &Script::RunCommand(ref expr)   => InterpretedScriptTool::evaluate_expression(expr, environment),

            _                               => Err(generate_script_error(ScriptEvaluationError::StatementNotImplemented, statement))
        }
    }
}

impl Tool for InterpretedScriptTool {
    fn invoke_json(&self, _input: Value, environment: &Environment) -> Result<Value, Value> {
        // Make the environment that this script will run in
        let mut script_environment = ScriptExecutionEnvironment::new(environment);

        // Execute the script
        let mut result = vec![];
        for statement in self.statements.iter() {
            // Evaluate the next statement
            let next_result = match Self::evaluate_statement(statement, &mut script_environment) {
                Ok(result) => result,

                // Fail immediately if any statement generates an error
                Err(fail) => return Err(fail)
            };

            // The script result is built up from the result of each statement
            // TODO: unless there's something like a return statement?
            result.push(next_result);
        }

        // Script is done
        Ok(Value::Array(result))
    }
}

///
/// Represents an execution environment for a running script
///
pub struct ScriptExecutionEnvironment<'a> {
    /// The environment where tools are drawn from
    parent_environment: &'a Environment
}

impl<'a> ScriptExecutionEnvironment<'a> {
    ///
    /// Creates a new script execution environment
    ///
    pub fn new(parent_environment: &'a Environment) -> ScriptExecutionEnvironment<'a> {
        ScriptExecutionEnvironment { parent_environment: parent_environment }
    }
}

#[cfg(test)]
mod test {
    use silkthread_base::basic::*;
    use super::*;

    #[test]
    fn can_evaluate_string() {
        let string_expr         = Expression::string("\"Foo\"");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&string_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Foo"))));
    }

    #[test]
    fn can_evaluate_number() {
        let num_expr            = Expression::number("42");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 42 ]));
    }

    #[test]
    fn can_evaluate_float_number_1() {
        let num_expr            = Expression::number("42.2");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 42.2 ]));
    }

    #[test]
    fn can_evaluate_float_number_2() {
        let num_expr            = Expression::number("42e1");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 42e1 ]));
    }

    #[test]
    fn can_evaluate_hex_number() {
        let num_expr            = Expression::number("0xabcd");
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&num_expr, &mut env);

        assert!(result == Ok(json![ 0xabcd ]));
    }

    #[test]
    fn can_evaluate_array() {
        let array_expr          = Expression::Array(vec![Expression::number("1"), Expression::number("2"), Expression::number("3")]);
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&array_expr, &mut env);

        assert!(result == Ok(json![ [ 1,2,3 ] ]));
    }

    #[test]
    fn can_evaluate_tuple() {
        let tuple_expr          = Expression::Tuple(vec![Expression::number("1"), Expression::number("2"), Expression::number("3")]);
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tuple_expr, &mut env);

        assert!(result == Ok(json![ [ 1,2,3 ] ]));
    }

    #[test]
    fn can_evaluate_map() {
        let map_expr            = Expression::Map(vec![ (Expression::string("\"Foo\""), Expression::number("1")), (Expression::string("\"Bar\""), Expression::number("2")) ]);
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&map_expr, &mut env);

        assert!(result == Ok(json![ { "Foo": 1, "Bar": 2 } ]));
    }

    #[test]
    fn can_evaluate_map_with_duplicate_keys() {
        let map_expr            = Expression::Map(vec![ (Expression::string("\"Foo\""), Expression::number("1")), (Expression::string("\"Foo\""), Expression::number("2")) ]);
        let empty_environment   = EmptyEnvironment::new();
        let mut env             = ScriptExecutionEnvironment::new(&empty_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&map_expr, &mut env);

        assert!(result == Ok(json![ { "Foo": 2 } ]));
    }

    #[test]
    fn can_call_tool() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::call_tool(&tool_expr, Value::Null, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_tool_call() {
        let tool_expr           = Expression::identifier("test");
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_execute_run_command() {
        let tool_expr           = Script::RunCommand(Expression::identifier("test"));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_statement(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }

    #[test]
    fn can_evaluate_apply_expression() {
        let tool_expr           = Expression::Apply(Box::new((Expression::identifier("test"), Expression::string("\"Success\""))));
        let tool_environment    = DynamicEnvironment::new();

        tool_environment.define("test", Box::new(make_pure_tool(|s: String| s)));

        let mut env             = ScriptExecutionEnvironment::new(&tool_environment);
        let result              = InterpretedScriptTool::evaluate_expression(&tool_expr, &mut env);

        assert!(result == Ok(Value::String(String::from("Success"))));
    }
}
<|endoftext|>


<commit_before>use geom::point::Point;
use renderer::Renderer;
use frame::Frame;
use draw::RGB;

use super::Scene;

pub struct DevelRenderer {
   scene: Scene,
   aliased: Vec<RGB>,
}

pub const SUBDIVISIONS: usize = 3;

impl DevelRenderer {
   #[inline]
   pub fn new(scene: Scene) -> Self {
      DevelRenderer {
         scene: scene,

         aliased: vec!(),
      }
   }

   fn check_resize(&mut self, width: u32, height: u32) {
      if self.aliased.len() == (width * height) as usize * SUBDIVISIONS * SUBDIVISIONS {
         return;
      }

      self.aliased.resize((width * height) as usize * SUBDIVISIONS * SUBDIVISIONS, RGB::new(0, 0, 0));
   }

   #[inline]
   fn clear(&mut self) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for i in 0..aliased.len() {
         aliased[i] = RGB::default();
      }
   }

   #[inline]
   fn render_aliased(&mut self, frame: &mut Frame) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for poly in &self.scene.polys {
         let points = &poly.points;

         let (left, right) = get_left_right_edges(points);

         let mut left_i = 0;
         let mut right_i = 0;

         let mut left_edge = left[left_i];
         let mut right_edge = right[right_i];

         let mut left_x = left_edge.p1.x;
         let mut right_x = right_edge.p1.x;

         let left_last_i = left.len() - 1;
         let right_last_i = right.len() - 1;

         let mut left_advancer = YAxisAdvancer::new(&left_edge);
         let mut right_advancer = YAxisAdvancer::new(&right_edge);

         let min_y = left_edge.p1.y;
         let max_y = left[left_last_i].p2.y;

         for y in min_y..max_y + 1 {
            if left_edge.p2.y == y && left_i != left_last_i {
               left_i += 1;
               left_edge = left[left_i];
               left_advancer = YAxisAdvancer::new(&left_edge);
            }

            if right_edge.p2.y == y && right_i != right_last_i  {
               right_i += 1;
               right_edge = right[right_i];
               right_advancer = YAxisAdvancer::new(&right_edge);
            }

            for x in left_x..right_x {
               aliased[y as usize * frame.width as usize * SUBDIVISIONS + x as usize] = poly.color;
            }

            left_x = left_advancer.advance();
            right_x = right_advancer.advance();
         }
      }
   }

   #[inline]
   fn downsample(&mut self, frame: &mut Frame) {
      let aliased: &mut Vec<RGB> = self.aliased.as_mut();

      let (width, height) = (frame.width as usize, frame.height as usize);

      for y in 0..height {
         for x in 0..width {
            let color = calc_pixel_color(aliased, width, x, y);

            frame.put_pixel(x as i32, y as i32, &color);
         }
      }
   }
}

impl Renderer for DevelRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.check_resize(width, height);
   }

   #[inline]
   fn render(&mut self, frame: &mut Frame) {
      self.check_resize(frame.width, frame.height);

      self.clear();

      self.render_aliased(frame);

      self.downsample(frame);
   }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Edge {
   p1: Point,
   p2: Point,
}

impl Edge {
   #[inline]
   fn new(p1: Point, p2: Point) -> Self {
      Edge {
         p1: p1,
         p2: p2,
      }
   }
}

struct YAxisAdvancer {
   delta: f64,
   f64x: f64,
}

impl YAxisAdvancer {
   #[inline]
   fn new(edge: &Edge) -> Self {
      let dx = edge.p2.x - edge.p1.x;
      let dy = edge.p2.y - edge.p1.y;

      let delta = dx as f64 / dy as f64;
      let f64x = edge.p1.x as f64;

      YAxisAdvancer {
         delta: delta,
         f64x: f64x,
      }
   }

   #[inline]
   fn advance(&mut self) -> i64 {
      self.f64x += self.delta;
      self.f64x.round() as i64
   }
}

#[inline]
fn get_left_right_edges(points: &Vec<Point>) -> (Vec<Edge>, Vec<Edge>){
   let (min_i, max_i) = find_min_max_y_index(points);

   let left = get_left_edges(points, min_i, max_i);
   let right = get_right_edges(points, min_i, max_i);

   (left, right)
}

#[inline]
fn get_left_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      curr_i += 1;

      if curr_i == points.len() {
         curr_i = 0;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

#[inline]
fn get_right_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      if curr_i == 0 {
         curr_i = points.len() - 1;
      } else {
         curr_i -= 1;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

#[inline]
fn find_min_max_y_index(points: &Vec<Point>) -> (usize, usize) {
   let (first, rest) = points.split_first().unwrap();

   let mut min_y = first.y;
   let mut max_y = min_y;
   let mut min_i = 0;
   let mut max_i = 0;

   for (i, point) in rest.iter().enumerate() {
      if point.y < min_y {
         min_i = i + 1;
         min_y = point.y;
      } else if point.y > max_y {
         max_i = i + 1;
         max_y = point.y;
      }
   }

   (min_i, max_i)
}

#[inline]
fn calc_pixel_color(aliased: &mut Vec<RGB>, width: usize, x: usize, y: usize) -> RGB {
   let mut r: u16 = 0;
   let mut g: u16 = 0;
   let mut b: u16 = 0;
   for box_y in 0..SUBDIVISIONS {
      for box_x in 0..SUBDIVISIONS {
         let pos = (box_y + y * SUBDIVISIONS) * width * SUBDIVISIONS + box_x + x * SUBDIVISIONS;
         let color = &aliased[pos];
         r += color.r as u16;
         g += color.g as u16;
         b += color.b as u16;
      }
   }

   let divisor = (SUBDIVISIONS * SUBDIVISIONS) as u16;

   r /= divisor;
   g /= divisor;
   b /= divisor;

   RGB::new(r as u8, g as u8, b as u8)
}
<commit_msg>Faster clear method for the development rasterizer<commit_after>use std::ptr;

use geom::point::Point;
use renderer::Renderer;
use frame::Frame;
use draw::RGB;

use super::Scene;

pub struct DevelRenderer {
   scene: Scene,
   aliased: Vec<RGB>,
}

pub const SUBDIVISIONS: usize = 3;

impl DevelRenderer {
   #[inline]
   pub fn new(scene: Scene) -> Self {
      DevelRenderer {
         scene: scene,

         aliased: vec!(),
      }
   }

   fn check_resize(&mut self, width: u32, height: u32) {
      if self.aliased.len() == (width * height) as usize * SUBDIVISIONS * SUBDIVISIONS {
         return;
      }

      self.aliased.resize((width * height) as usize * SUBDIVISIONS * SUBDIVISIONS, RGB::new(0, 0, 0));
   }

   #[inline]
   fn clear(&mut self) {
      let len = self.aliased.len();
      let vp = self.aliased.as_mut_ptr();

      unsafe {
         ptr::write_bytes(vp, 0, len);
      }
   }

   #[inline]
   fn render_aliased(&mut self, frame: &mut Frame) {
      let mut aliased: &mut Vec<RGB> = self.aliased.as_mut();

      for poly in &self.scene.polys {
         let points = &poly.points;

         let (left, right) = get_left_right_edges(points);

         let mut left_i = 0;
         let mut right_i = 0;

         let mut left_edge = left[left_i];
         let mut right_edge = right[right_i];

         let mut left_x = left_edge.p1.x;
         let mut right_x = right_edge.p1.x;

         let left_last_i = left.len() - 1;
         let right_last_i = right.len() - 1;

         let mut left_advancer = YAxisAdvancer::new(&left_edge);
         let mut right_advancer = YAxisAdvancer::new(&right_edge);

         let min_y = left_edge.p1.y;
         let max_y = left[left_last_i].p2.y;

         for y in min_y..max_y + 1 {
            if left_edge.p2.y == y && left_i != left_last_i {
               left_i += 1;
               left_edge = left[left_i];
               left_advancer = YAxisAdvancer::new(&left_edge);
            }

            if right_edge.p2.y == y && right_i != right_last_i  {
               right_i += 1;
               right_edge = right[right_i];
               right_advancer = YAxisAdvancer::new(&right_edge);
            }

            for x in left_x..right_x {
               aliased[y as usize * frame.width as usize * SUBDIVISIONS + x as usize] = poly.color;
            }

            left_x = left_advancer.advance();
            right_x = right_advancer.advance();
         }
      }
   }

   #[inline]
   fn downsample(&mut self, frame: &mut Frame) {
      let aliased: &mut Vec<RGB> = self.aliased.as_mut();

      let (width, height) = (frame.width as usize, frame.height as usize);

      for y in 0..height {
         for x in 0..width {
            let color = calc_pixel_color(aliased, width, x, y);

            frame.put_pixel(x as i32, y as i32, &color);
         }
      }
   }
}

impl Renderer for DevelRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.check_resize(width, height);
   }

   #[inline]
   fn render(&mut self, frame: &mut Frame) {
      self.check_resize(frame.width, frame.height);

      self.clear();

      self.render_aliased(frame);

      self.downsample(frame);
   }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Edge {
   p1: Point,
   p2: Point,
}

impl Edge {
   #[inline]
   fn new(p1: Point, p2: Point) -> Self {
      Edge {
         p1: p1,
         p2: p2,
      }
   }
}

struct YAxisAdvancer {
   delta: f64,
   f64x: f64,
}

impl YAxisAdvancer {
   #[inline]
   fn new(edge: &Edge) -> Self {
      let dx = edge.p2.x - edge.p1.x;
      let dy = edge.p2.y - edge.p1.y;

      let delta = dx as f64 / dy as f64;
      let f64x = edge.p1.x as f64;

      YAxisAdvancer {
         delta: delta,
         f64x: f64x,
      }
   }

   #[inline]
   fn advance(&mut self) -> i64 {
      self.f64x += self.delta;
      self.f64x.round() as i64
   }
}

#[inline]
fn get_left_right_edges(points: &Vec<Point>) -> (Vec<Edge>, Vec<Edge>){
   let (min_i, max_i) = find_min_max_y_index(points);

   let left = get_left_edges(points, min_i, max_i);
   let right = get_right_edges(points, min_i, max_i);

   (left, right)
}

#[inline]
fn get_left_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      curr_i += 1;

      if curr_i == points.len() {
         curr_i = 0;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

#[inline]
fn get_right_edges(points: &Vec<Point>, min_i: usize, max_i: usize) -> Vec<Edge> {
   let mut edges = Vec::new();

   let mut curr_i = min_i;
   let mut prev_i = curr_i;

   loop {
      if curr_i == 0 {
         curr_i = points.len() - 1;
      } else {
         curr_i -= 1;
      }

      edges.push(Edge::new(points[prev_i], points[curr_i]));

      prev_i = curr_i;

      if curr_i == max_i {
         break;
      }
   }

   edges
}

#[inline]
fn find_min_max_y_index(points: &Vec<Point>) -> (usize, usize) {
   let (first, rest) = points.split_first().unwrap();

   let mut min_y = first.y;
   let mut max_y = min_y;
   let mut min_i = 0;
   let mut max_i = 0;

   for (i, point) in rest.iter().enumerate() {
      if point.y < min_y {
         min_i = i + 1;
         min_y = point.y;
      } else if point.y > max_y {
         max_i = i + 1;
         max_y = point.y;
      }
   }

   (min_i, max_i)
}

#[inline]
fn calc_pixel_color(aliased: &mut Vec<RGB>, width: usize, x: usize, y: usize) -> RGB {
   let mut r: u16 = 0;
   let mut g: u16 = 0;
   let mut b: u16 = 0;
   for box_y in 0..SUBDIVISIONS {
      for box_x in 0..SUBDIVISIONS {
         let pos = (box_y + y * SUBDIVISIONS) * width * SUBDIVISIONS + box_x + x * SUBDIVISIONS;
         let color = &aliased[pos];
         r += color.r as u16;
         g += color.g as u16;
         b += color.b as u16;
      }
   }

   let divisor = (SUBDIVISIONS * SUBDIVISIONS) as u16;

   r /= divisor;
   g /= divisor;
   b /= divisor;

   RGB::new(r as u8, g as u8, b as u8)
}
<|endoftext|>


<commit_before>use std::cmp;
use asset::*;

impl Asset {
    pub fn gt(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        let decimals = cmp::max(self.decimals, other.decimals);
        let t = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let o = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(t > o)
    }

    pub fn geq(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        let decimals = cmp::max(self.decimals, other.decimals);
        let t = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let o = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(t >= o)
    }

    pub fn lt(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        Some(!(self.geq(other)?))
    }

    pub fn leq(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        Some(!(self.gt(other)?))
    }

    pub fn eq(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        let decimals = cmp::max(self.decimals, other.decimals);
        let t = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let o = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(t == o)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_compare_assets() {
        assert!(get_asset("1 GOLD").gt(&get_asset("0.50 GOLD")).unwrap());
        assert!(get_asset("1.0 GOLD").gt(&get_asset("0.99 GOLD")).unwrap());

        assert!(get_asset("1 GOLD").geq(&get_asset("1.0 GOLD")).unwrap());
        assert!(!get_asset("0.1 GOLD").geq(&get_asset("1.0 GOLD")).unwrap());

        assert!(get_asset("1 GOLD").leq(&get_asset("1.0 GOLD")).unwrap());
        assert!(get_asset("0.1 GOLD").leq(&get_asset("1.0 GOLD")).unwrap());
        assert!(get_asset("5.0 GOLD").leq(&get_asset("10 GOLD")).unwrap());

        assert!(get_asset("1 GOLD").eq(&get_asset("1 GOLD")).unwrap());
        assert!(!get_asset("1 GOLD").gt(&get_asset("1 GOLD")).unwrap());
        assert!(!get_asset("1 GOLD").lt(&get_asset("1 GOLD")).unwrap());
    }

    #[test]
    fn test_invalid_cmp() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.gt(b).is_none());
        assert!(a.geq(b).is_none());
        assert!(a.lt(b).is_none());
        assert!(a.leq(b).is_none());
        assert!(a.eq(b).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<commit_msg>Minor performance improvement comparing asset equality<commit_after>use std::cmp;
use asset::*;

impl Asset {
    pub fn gt(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        let decimals = cmp::max(self.decimals, other.decimals);
        let t = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let o = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(t > o)
    }

    pub fn geq(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        let decimals = cmp::max(self.decimals, other.decimals);
        let t = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let o = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(t >= o)
    }

    pub fn lt(&self, other: &Asset) -> Option<bool> {
        Some(!(self.geq(other)?))
    }

    pub fn leq(&self, other: &Asset) -> Option<bool> {
        Some(!(self.gt(other)?))
    }

    pub fn eq(&self, other: &Asset) -> Option<bool> {
        if self.symbol != other.symbol { return None }
        let decimals = cmp::max(self.decimals, other.decimals);
        let t = set_decimals_i64(self.amount, self.decimals, decimals)?;
        let o = set_decimals_i64(other.amount, other.decimals, decimals)?;
        Some(t == o)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_compare_assets() {
        assert!(get_asset("1 GOLD").gt(&get_asset("0.50 GOLD")).unwrap());
        assert!(get_asset("1.0 GOLD").gt(&get_asset("0.99 GOLD")).unwrap());

        assert!(get_asset("1 GOLD").geq(&get_asset("1.0 GOLD")).unwrap());
        assert!(!get_asset("0.1 GOLD").geq(&get_asset("1.0 GOLD")).unwrap());

        assert!(get_asset("1 GOLD").leq(&get_asset("1.0 GOLD")).unwrap());
        assert!(get_asset("0.1 GOLD").leq(&get_asset("1.0 GOLD")).unwrap());
        assert!(get_asset("5.0 GOLD").leq(&get_asset("10 GOLD")).unwrap());

        assert!(get_asset("1 GOLD").eq(&get_asset("1 GOLD")).unwrap());
        assert!(!get_asset("1 GOLD").gt(&get_asset("1 GOLD")).unwrap());
        assert!(!get_asset("1 GOLD").lt(&get_asset("1 GOLD")).unwrap());
    }

    #[test]
    fn test_invalid_cmp() {
        let a = &get_asset("10 GOLD");
        let b = &get_asset("10 SILVER");

        assert!(a.gt(b).is_none());
        assert!(a.geq(b).is_none());
        assert!(a.lt(b).is_none());
        assert!(a.leq(b).is_none());
        assert!(a.eq(b).is_none());
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<|endoftext|>