<commit_before>//! Bounds-checked buffer cursor type.
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::cell::RefCell;
use buffer::{Position, GapBuffer};

/// Read-only wrapper for a `Position`, to allow field level access to a
/// buffer's cursor while simultaneously enforcing bounds-checking when
/// updating its value.
#[derive(Clone)]
pub struct Cursor {
    pub data: Rc<RefCell<GapBuffer>>,
    pub position: Position,
    sticky_offset: usize,
}

impl Deref for Cursor {
    type Target = Position;

    fn deref(&self) -> &Position {
        &self.position
    }
}

impl DerefMut for Cursor {
    fn deref_mut(&mut self) -> &mut Position {
        &mut self.position
    }
}

impl Cursor {
    /// Initializes a cursor bound to the specified gap buffer, at the specified position.
    pub fn new(data: Rc<RefCell<GapBuffer>>, position: Position) -> Cursor {
        Cursor{
            data: data,
            position: position,
            sticky_offset: position.offset
        }
    }

    /// Moves the cursor to the specified location. The location is
    /// bounds-checked against the data and the cursor will not be
    /// updated if it is out-of-bounds.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::buffer::Position;
    ///
    /// let mut buffer = Buffer::new();
    /// let in_bounds = Position{ line: 0, offset: 2 };
    /// let out_of_bounds = Position{ line: 2, offset: 2 };
    /// buffer.insert("scribe");
    ///
    /// buffer.cursor.move_to(in_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    ///
    /// buffer.cursor.move_to(out_of_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    /// ```
    pub fn move_to(&mut self, position: Position) -> bool {
        if self.data.borrow().in_bounds(&position) {
            self.position = position;

            // Remember this offset so that we can try
            // to maintain it when moving across lines.
            self.sticky_offset = position.offset;

            return true
        }
        false
    }

    /// Decrements the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_up(&mut self) {
        // Don't bother if we are already at the top.
        if self.line == 0 { return; }

        let target_line = self.line-1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line above, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.chars().count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Increments the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_down(&mut self) {
        let target_line = self.line+1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line below, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.chars().count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Decrements the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_left(&mut self) {
        // Don't bother if we are already at the left edge.
        if self.offset == 0 { return; }

        let new_position = Position{ line: self.line, offset: self.offset-1 };
        self.move_to(new_position);
    }

    /// Increments the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_right(&mut self) {
        let new_position = Position{ line: self.line, offset: self.offset+1 };
        self.move_to(new_position);
    }

    /// Sets the cursor offset to 0: the start of the current line.
    pub fn move_to_start_of_line(&mut self) {
        let new_position = Position{ line: self.line, offset: 0 };
        self.move_to(new_position);
    }

    /// Moves the cursor offset to after the last character on the current line.
    pub fn move_to_end_of_line(&mut self) {
        let data = self.data.borrow().to_string();
        let current_line = data.lines().nth(self.line);
        match current_line {
            Some(line) => {
                let new_position = Position{ line: self.line, offset: line.chars().count() };
                self.move_to(new_position);
            },
            None => (),
        }
    }

    /// Moves the cursor to the last line in the buffer.
    pub fn move_to_last_line(&mut self) {
        // Figure out the number and length of the last line.
        let mut line = 0;
        let mut length = 0;
        for c in self.data.borrow().to_string().chars() {
            if c == '\n' {
                line += 1;
                length = 0;
            } else {
                length += 1;
            }
        }

        let target_position =
            if length < self.sticky_offset {
                // Current offset is beyond the last line's length; move to the end of it.
                Position{ line: line, offset: length }
            } else {
                // Current offset is available on the last line; go there.
                Position{ line: line, offset: self.sticky_offset }
            };
        self.move_to(target_position);
    }

    /// Moves the cursor to the first line in the buffer.
    pub fn move_to_first_line(&mut self) {
        // Figure out the length of the first line.
        let length = match self.data.borrow().to_string().lines().nth(0) {
            Some(line_content) => line_content.len(),
            None => 0
        };

        let target_position =
            if length < self.sticky_offset {
                // Current offset is beyond the first line's length; move to the end of it.
                Position{ line: 0, offset: length }
            } else {
                // Current offset is available on the first line; go there.
                Position{ line: 0, offset: self.sticky_offset }
            };
        self.move_to(target_position);
    }
}

#[cfg(test)]
mod tests {
    use buffer::{Cursor, GapBuffer, Position};
    use std::rc::Rc;
    use std::cell::RefCell;

    #[test]
    fn move_up_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.\nAnother line that is longer.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 20 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_down_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("Another line that is longer.\nThis is a test.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 2, offset: 20 });
        cursor.move_up();
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_down_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_to_sets_persisted_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_to(Position{ line: 1, offset: 5 });
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 5);
    }

    #[test]
    fn move_to_start_of_line_sets_offset_to_zero() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 5 });
        cursor.move_to_start_of_line();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_end_of_line_sets_offset_the_line_length() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_end_of_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_left_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_left();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_last_line_moves_to_same_offset_on_last_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 2 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 2);
    }

    #[test]
    fn move_to_last_line_moves_to_end_of_last_line_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 4);
    }

    #[test]
    fn move_to_last_line_moves_last_line_when_it_is_a_trailing_newline() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast\n".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 2 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 3);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_first_line_moves_to_same_offset_on_first_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 2 });
        cursor.move_to_first_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 2);
    }

    #[test]
    fn move_to_first_line_moves_to_end_of_first_line_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 6 });
        cursor.move_to_first_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 5);
    }
}
<commit_msg>Update cursor movements to support graphemes<commit_after>//! Bounds-checked buffer cursor type.
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::cell::RefCell;
use buffer::{Position, GapBuffer};
use unicode_segmentation::UnicodeSegmentation;

/// Read-only wrapper for a `Position`, to allow field level access to a
/// buffer's cursor while simultaneously enforcing bounds-checking when
/// updating its value.
#[derive(Clone)]
pub struct Cursor {
    pub data: Rc<RefCell<GapBuffer>>,
    pub position: Position,
    sticky_offset: usize,
}

impl Deref for Cursor {
    type Target = Position;

    fn deref(&self) -> &Position {
        &self.position
    }
}

impl DerefMut for Cursor {
    fn deref_mut(&mut self) -> &mut Position {
        &mut self.position
    }
}

impl Cursor {
    /// Initializes a cursor bound to the specified gap buffer, at the specified position.
    pub fn new(data: Rc<RefCell<GapBuffer>>, position: Position) -> Cursor {
        Cursor{
            data: data,
            position: position,
            sticky_offset: position.offset
        }
    }

    /// Moves the cursor to the specified location. The location is
    /// bounds-checked against the data and the cursor will not be
    /// updated if it is out-of-bounds.
    ///
    /// # Examples
    ///
    /// ```
    /// use scribe::Buffer;
    /// use scribe::buffer::Position;
    ///
    /// let mut buffer = Buffer::new();
    /// let in_bounds = Position{ line: 0, offset: 2 };
    /// let out_of_bounds = Position{ line: 2, offset: 2 };
    /// buffer.insert("scribe");
    ///
    /// buffer.cursor.move_to(in_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    ///
    /// buffer.cursor.move_to(out_of_bounds);
    /// assert_eq!(buffer.cursor.line, 0);
    /// assert_eq!(buffer.cursor.offset, 2);
    /// ```
    pub fn move_to(&mut self, position: Position) -> bool {
        if self.data.borrow().in_bounds(&position) {
            self.position = position;

            // Remember this offset so that we can try
            // to maintain it when moving across lines.
            self.sticky_offset = position.offset;

            return true
        }
        false
    }

    /// Decrements the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_up(&mut self) {
        // Don't bother if we are already at the top.
        if self.line == 0 { return; }

        let target_line = self.line-1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line above, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.graphemes(true).count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Increments the cursor line. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_down(&mut self) {
        let target_line = self.line+1;
        let new_position = Position{ line: target_line, offset: self.sticky_offset };

        // Try moving to the same offset on the line below, falling back to its EOL.
        if self.move_to(new_position) == false {
            let mut target_offset = 0;
            for (line_number, line) in self.data.borrow().to_string().lines().enumerate() {
                if line_number == target_line {
                    target_offset = line.graphemes(true).count();
                }
            }
            self.move_to(Position{ line: target_line, offset: target_offset });

            // Moving the position successfully updates the sticky offset, but we
            // haven't actually moved to where we really wanted to go (offset-wise).
            // Restore the original desired offset; it might be available on the next try.
            self.sticky_offset = new_position.offset;
        }
    }

    /// Decrements the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_left(&mut self) {
        // Don't bother if we are already at the left edge.
        if self.offset == 0 { return; }

        let new_position = Position{ line: self.line, offset: self.offset-1 };
        self.move_to(new_position);
    }

    /// Increments the cursor offset. The location is bounds-checked against
    /// the data and the cursor will not be updated if it is out-of-bounds.
    pub fn move_right(&mut self) {
        let new_position = Position{ line: self.line, offset: self.offset+1 };
        self.move_to(new_position);
    }

    /// Sets the cursor offset to 0: the start of the current line.
    pub fn move_to_start_of_line(&mut self) {
        let new_position = Position{ line: self.line, offset: 0 };
        self.move_to(new_position);
    }

    /// Moves the cursor offset to after the last character on the current line.
    pub fn move_to_end_of_line(&mut self) {
        let data = self.data.borrow().to_string();
        let current_line = data.lines().nth(self.line);
        match current_line {
            Some(line) => {
                let new_position = Position{ line: self.line, offset: line.graphemes(true).count() };
                self.move_to(new_position);
            },
            None => (),
        }
    }

    /// Moves the cursor to the last line in the buffer.
    pub fn move_to_last_line(&mut self) {
        // Figure out the number and length of the last line.
        let mut line = 0;
        let mut length = 0;
        for c in self.data.borrow().to_string().graphemes(true) {
            if c == "\n" {
                line += 1;
                length = 0;
            } else {
                length += 1;
            }
        }

        let target_position =
            if length < self.sticky_offset {
                // Current offset is beyond the last line's length; move to the end of it.
                Position{ line: line, offset: length }
            } else {
                // Current offset is available on the last line; go there.
                Position{ line: line, offset: self.sticky_offset }
            };
        self.move_to(target_position);
    }

    /// Moves the cursor to the first line in the buffer.
    pub fn move_to_first_line(&mut self) {
        // Figure out the length of the first line.
        let length = self
            .data
            .borrow()
            .to_string()
            .lines()
            .nth(0)
            .map(|line| line.graphemes(true).count())
            .unwrap_or(0);

        let target_position =
            if length < self.sticky_offset {
                // Current offset is beyond the first line's length; move to the end of it.
                Position{ line: 0, offset: length }
            } else {
                // Current offset is available on the first line; go there.
                Position{ line: 0, offset: self.sticky_offset }
            };
        self.move_to(target_position);
    }
}

#[cfg(test)]
mod tests {
    use buffer::{Cursor, GapBuffer, Position};
    use std::rc::Rc;
    use std::cell::RefCell;

    #[test]
    fn move_up_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.\nAnother line that is longer.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 20 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_down_goes_to_eol_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("Another line that is longer.\nThis is a test.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_counts_graphemes_as_a_single_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First नी\nSecond line".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 11 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 7);
    }

    #[test]
    fn move_down_counts_graphemes_as_a_single_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line\nSecond नी".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 10 });
        cursor.move_down();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 8);
    }

    #[test]
    fn move_up_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 2, offset: 20 });
        cursor.move_up();
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_down_persists_offset_across_shorter_lines() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_down();
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 20);
    }

    #[test]
    fn move_to_sets_persisted_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line that is longer.\nThis is a test.\nAnother line that is longer.".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 20 });
        cursor.move_to(Position{ line: 1, offset: 5 });
        cursor.move_down();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 5);
    }

    #[test]
    fn move_to_start_of_line_sets_offset_to_zero() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 5 });
        cursor.move_to_start_of_line();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_end_of_line_counts_graphemes_as_a_single_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First नी".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_to_end_of_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 7);
    }

    #[test]
    fn move_to_end_of_line_sets_offset_the_line_length() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.\nAnother line.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_end_of_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 15);
    }

    #[test]
    fn move_up_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_up();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_left_does_nothing_if_at_the_start_of_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("This is a test.".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 0 });
        cursor.move_left();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_last_line_counts_graphemes_as_a_single_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First line\nLast नी".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 10 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 1);
        assert_eq!(cursor.offset, 6);
    }

    #[test]
    fn move_to_last_line_moves_to_same_offset_on_last_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 2 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 2);
    }

    #[test]
    fn move_to_last_line_moves_to_end_of_last_line_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 5 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 2);
        assert_eq!(cursor.offset, 4);
    }

    #[test]
    fn move_to_last_line_moves_last_line_when_it_is_a_trailing_newline() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast\n".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 2 });
        cursor.move_to_last_line();
        assert_eq!(cursor.line, 3);
        assert_eq!(cursor.offset, 0);
    }

    #[test]
    fn move_to_first_line_counts_graphemes_as_a_single_offset() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new(
            "First नी\nLast line".to_string()
        )));
        let mut cursor = Cursor::new(buffer, Position{ line: 0, offset: 9 });
        cursor.move_to_first_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 7);
    }

    #[test]
    fn move_to_first_line_moves_to_same_offset_on_first_line() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 2 });
        cursor.move_to_first_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 2);
    }

    #[test]
    fn move_to_first_line_moves_to_end_of_first_line_if_offset_would_be_out_of_range() {
        let buffer = Rc::new(RefCell::new(GapBuffer::new("first\nsecond\nlast".to_string())));
        let mut cursor = Cursor::new(buffer, Position{ line: 1, offset: 6 });
        cursor.move_to_first_line();
        assert_eq!(cursor.line, 0);
        assert_eq!(cursor.offset, 5);
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

use criterion::{criterion_group, criterion_main, Criterion};
use chunked_transfer;
use std::io::Write;

extern crate test;

fn encode_benchmark(c: &mut Criterion) {
    c.bench_function("encode", |b| {
        let writer = vec![];
        let mut encoder = chunked_transfer::Encoder::new(writer);
        let mut to_write = vec![b'a'; 1000];

        b.iter(|| {
            test::black_box(encoder.write_all(&mut to_write));
        });
    });
}

criterion_group!(benches, encode_benchmark);
criterion_main!(benches);
<commit_msg>rustfmt<commit_after>#![feature(test)]

use chunked_transfer;
use criterion::{criterion_group, criterion_main, Criterion};
use std::io::Write;

extern crate test;

fn encode_benchmark(c: &mut Criterion) {
    c.bench_function("encode", |b| {
        let writer = vec![];
        let mut encoder = chunked_transfer::Encoder::new(writer);
        let mut to_write = vec![b'a'; 1000];

        b.iter(|| {
            test::black_box(encoder.write_all(&mut to_write));
        });
    });
}

criterion_group!(benches, encode_benchmark);
criterion_main!(benches);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use std;
use std::rand::*;
use common::*;
use eval_capnp::*;

pub type RequestBuilder = Expression::Builder;
pub type ResponseBuilder = EvaluationResult::Builder;
pub type Expectation = i32;
<commit_msg>a start on makeExpression<commit_after>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use std;
use std::rand::*;
use common::*;
use eval_capnp::*;

pub type RequestBuilder = Expression::Builder;
pub type ResponseBuilder = EvaluationResult::Builder;
pub type Expectation = i32;

fn makeExpression(rng : &mut FastRand, exp : Expression::Builder, depth : uint) -> i32 {
    exp.setOp(unsafe {
            std::cast::transmute(rng.gen_uint_range(0, Operation::modulus as uint + 1))});

    let mut left : u32 = 0;
    let mut right : u32 = 0;

    if (rng.nextLessThan(8) < (depth as u32)) {
        left = rng.nextLessThan(128) + 1;
        exp.getLeft().setValue(left as i32);
    } else {
        // how are we going to do this?
    }

    0
}
<|endoftext|>


<commit_before>/* mc.rs - N64 Memory Controller */

/*
    Aggregates N64 system constants.
    Handles reading from and writing to the console's memory map.
 */

/* Access to the N64's memories. */
use n64::N64;

/* N64 memory sizes. */
pub const N64_IRAM_SIZE: usize = 0x400000;
pub const N64_ERAM_SIZE: usize = 0x400000;

/*
    0x0000 0000 to 0x03EF FFFF RDRAM Memory
    0x03F0 0000 to 0x03FF FFFF RDRAM Registers
    0x0400 0000 to 0x040F FFFF SP Registers
    0x0410 0000 to 0x041F FFFF DP Command Registers
    0x0420 0000 to 0x042F FFFF DP Span Registers
    0x0430 0000 to 0x043F FFFF MIPS Interface (MI) Registers
    0x0440 0000 to 0x044F FFFF Video Interface (VI) Registers
    0x0450 0000 to 0x045F FFFF Audio Interface (AI) Registers
    0x0460 0000 to 0x046F FFFF Peripheral Interface (PI) Registers
    0x0470 0000 to 0x047F FFFF RDRAM Interface (RI) Registers
    0x0480 0000 to 0x048F FFFF Serial Interface (SI) Registers
    0x0490 0000 to 0x04FF FFFF Unused
    0x0500 0000 to 0x05FF FFFF Cartridge Domain 2 Address 1
    0x0600 0000 to 0x07FF FFFF Cartridge Domain 1 Address 1
    0x0800 0000 to 0x0FFF FFFF Cartridge Domain 2 Address 2
    0x1000 0000 to 0x1FBF FFFF Cartridge Domain 1 Address 2
    0x1FC0 0000 to 0x1FC0 07BF PIF Boot ROM
    0x1FC0 07C0 to 0x1FC0 07FF PIF RAM
    0x1FC0 0800 to 0x1FCF FFFF Reserved
    0x1FD0 0000 to 0x7FFF FFFF Cartridge Domain 1 Address 3
    0x8000 0000 to 0xFFFF FFFF External SysAD Device
*/

const RDRAM_MEM_START: usize = 0x0000_0000;
const RDRAM_MEM_END: usize = 0x003E_FFFF;

const RDRAM_REG_START: usize = 0x03F0_0000;
const RDRAM_REG_END: usize = 0x03FF_FFFF;

const SP_REG_START: usize = 0x0400_0000;
const SP_REG_END: usize = 0x040F_FFFF;

const DP_CMD_START: usize = 0x0410_0000;
const DP_CMD_END: usize = 0x041F_FFFF;

const DP_SPAN_START: usize = 0x0420_0000;
const DP_SPAN_END: usize = 0x042F_FFFF;

const MI_REG_START: usize = 0x0430_0000;
const MI_REG_END: usize = 0x043F_FFFF;

const VI_REG_START: usize = 0x0440_0000;
const VI_REG_END: usize = 0x044F_FFFF;

const AI_REG_START: usize = 0x0450_0000;
const AI_REG_END: usize = 0x045F_FFFF;

const PI_REG_START: usize = 0x0460_0000;
const PI_REG_END: usize = 0x046F_FFFF;

const RI_REG_START: usize = 0x0470_0000;
const RI_REG_END: usize = 0x047F_FFFF;

const SI_REG_START: usize = 0x0480_0000;
const SI_REG_END: usize = 0x048F_FFFF;

const UNUSED_START: usize = 0x0490_0000;
const UNUSED_END: usize = 0x04FF_FFFF;

const CART_DOM2_A1_START: usize = 0x0500_0000;
const CART_DOM2_A1_END: usize = 0x05FF_FFFF;

const CART_DOM1_A1_START: usize = 0x0600_0000;
const CART_DOM1_A1_END: usize = 0x07FF_FFFF;

const CART_DOM2_A2_START: usize = 0x0800_0000;
const CART_DOM2_A2_END: usize = 0x0FFF_FFFF;

const CART_DOM1_A2_START: usize = 0x1000_0000;
const CART_DOM1_A2_END: usize = 0x1FBF_FFFF;

pub const PIF_ROM_START: usize = 0x1FC0_0000;
const PIF_ROM_END: usize = 0x1FC0_07BF;

pub const PIF_RAM_START: usize = 0x1FC0_07C0;
pub const PIF_RAM_END: usize = 0x1FC0_07FF;

const RESERVED_START: usize = 0x1FC0_0800;
const RESERVED_END: usize = 0x1FCF_FFFF;

const CART_DOM1_A3_START: usize = 0x1FD0_0000;
const CART_DOM1_A3_END: usize = 0x7FFF_FFFF;

const SYSAD_START: usize = 0x8000_0000;
const SYSAD_END: usize = 0xFFFF_FFFF;

fn read32(addr: usize, mem: &Box<[u8]>) -> u32 {
	println!("Reading word from relative address {:#x}", addr);
	/* Obtain a slice starting at the read address. */
	let b: &[u8] = &mem[addr .. addr + 4];
	/* Extract each of the word's bytes and use them to create a u32. */
	let w = (((b[0] as u32) << 24) | ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | (b[3] as u32));
    /* Byte swap and adjust the endianness of the read word. */
    u32::from_be(w.swap_bytes())
}

/* Reads a word from the provided N64's memory map. */
pub fn read(n64: &N64, addr: usize) -> u32 {
    println!("Reading word from physical address 0x{:08x}", addr);
    /* Match the memory address to a peripheral address range. */
    match addr {
        RDRAM_MEM_START ... RDRAM_MEM_END       =>
            read32(addr - RDRAM_MEM_START, &n64.iram),
        RDRAM_REG_START ... RDRAM_REG_END       => 0,
        SP_REG_START ... SP_REG_END             => 0,
        DP_CMD_START ... DP_CMD_END             => 0,
        DP_SPAN_START ... DP_SPAN_END           => 0,
        MI_REG_START ... MI_REG_END             => 0,
        VI_REG_START ... VI_REG_END             => 0,
        AI_REG_START ... AI_REG_END             => 0,
        PI_REG_START ... PI_REG_END             => 0,
        RI_REG_START ... RI_REG_END             => 0,
        SI_REG_START ... SI_REG_END             => 0,
        UNUSED_START ... UNUSED_END             => 0,
        CART_DOM2_A1_START ... CART_DOM2_A1_END =>
            read32(addr - CART_DOM2_A1_START, &n64.crom),
        CART_DOM1_A1_START ... CART_DOM1_A1_END =>
            read32(addr - CART_DOM1_A1_START, &n64.crom),
        CART_DOM2_A2_START ... CART_DOM2_A2_END =>
            read32(addr - CART_DOM2_A2_START, &n64.crom),
        CART_DOM1_A2_START ... CART_DOM1_A2_END =>
            read32(addr - CART_DOM1_A2_START, &n64.crom),
        PIF_ROM_START ... PIF_ROM_END           =>
            read32(addr - PIF_ROM_START, &n64.pif.prom),
        PIF_RAM_START ... PIF_RAM_END           =>
            read32(addr - PIF_RAM_START, &n64.pif.pram),
        RESERVED_START ... RESERVED_END         => 0,
        CART_DOM1_A3_START ... CART_DOM1_A3_END =>
            read32(addr - CART_DOM1_A3_START, &n64.crom),
        SYSAD_START ... SYSAD_END               => 0,
        _ => panic!("Unrecognized physical address: {:#x}", addr)
    }
}

fn write32(addr: usize, val: u32, mem: &Box<[u8]>) {
	println!("Writing word to relative address {:#x}", addr);
	/* Obtain a slice of bytes of the u32. */
	let b: &[u8] = &[(val >> 24) as u8, (val >> 16) as u8, (val >> 8) as u8, val as u8];
}

/* Writes a word to the provided N64's memory map. */
pub fn write(n64: &mut N64, addr: usize, val: u32) {
    println!("Reading word from physical address 0x{:08x}", addr);
    /* Match the memory address to a peripheral address range. */
    match addr {
        RDRAM_MEM_START ... RDRAM_MEM_END       =>
            write32(addr - RDRAM_MEM_START, val, &n64.iram),
        RDRAM_REG_START ... RDRAM_REG_END       =>
            return,
        SP_REG_START ... SP_REG_END             =>
            return,
        DP_CMD_START ... DP_CMD_END             =>
            return,
        DP_SPAN_START ... DP_SPAN_END           =>
            return,
        MI_REG_START ... MI_REG_END             =>
            return,
        VI_REG_START ... VI_REG_END             =>
            return,
        AI_REG_START ... AI_REG_END             =>
            return,
        PI_REG_START ... PI_REG_END             =>
            return,
        RI_REG_START ... RI_REG_END             =>
            return,
        SI_REG_START ... SI_REG_END             =>
            return,
        UNUSED_START ... UNUSED_END             =>
            return,
        CART_DOM2_A1_START ... CART_DOM2_A1_END =>
            write32(addr - CART_DOM2_A1_START, val, &n64.crom),
        CART_DOM1_A1_START ... CART_DOM1_A1_END =>
            write32(addr - CART_DOM1_A1_START, val, &n64.crom),
        CART_DOM2_A2_START ... CART_DOM2_A2_END =>
            write32(addr - CART_DOM2_A2_START, val, &n64.crom),
        CART_DOM1_A2_START ... CART_DOM1_A2_END =>
            write32(addr - CART_DOM1_A2_START, val, &n64.crom),
        PIF_ROM_START ... PIF_ROM_END           =>
            write32(addr - PIF_ROM_START, val, &n64.pif.prom),
        PIF_RAM_START ... PIF_RAM_END           =>
            write32(addr - PIF_RAM_START, val, &n64.pif.pram),
        RESERVED_START ... RESERVED_END         =>
            return,
        CART_DOM1_A3_START ... CART_DOM1_A3_END =>
            write32(addr - CART_DOM1_A3_START, val, &n64.crom),
        SYSAD_START ... SYSAD_END               =>
            return,
        _ => panic!("Unrecognized physical address: {:#x}", addr)
    }
}
<commit_msg>Fixed writing mechanism.<commit_after>/* mc.rs - N64 Memory Controller */

/*
    Aggregates N64 system constants.
    Handles reading from and writing to the console's memory map.
 */

/* Access to the N64's memories. */
use n64::N64;

/* N64 memory sizes. */
pub const N64_IRAM_SIZE: usize = 0x400000;
pub const N64_ERAM_SIZE: usize = 0x400000;

/*
    0x0000 0000 to 0x03EF FFFF RDRAM Memory
    0x03F0 0000 to 0x03FF FFFF RDRAM Registers
    0x0400 0000 to 0x040F FFFF SP Registers
    0x0410 0000 to 0x041F FFFF DP Command Registers
    0x0420 0000 to 0x042F FFFF DP Span Registers
    0x0430 0000 to 0x043F FFFF MIPS Interface (MI) Registers
    0x0440 0000 to 0x044F FFFF Video Interface (VI) Registers
    0x0450 0000 to 0x045F FFFF Audio Interface (AI) Registers
    0x0460 0000 to 0x046F FFFF Peripheral Interface (PI) Registers
    0x0470 0000 to 0x047F FFFF RDRAM Interface (RI) Registers
    0x0480 0000 to 0x048F FFFF Serial Interface (SI) Registers
    0x0490 0000 to 0x04FF FFFF Unused
    0x0500 0000 to 0x05FF FFFF Cartridge Domain 2 Address 1
    0x0600 0000 to 0x07FF FFFF Cartridge Domain 1 Address 1
    0x0800 0000 to 0x0FFF FFFF Cartridge Domain 2 Address 2
    0x1000 0000 to 0x1FBF FFFF Cartridge Domain 1 Address 2
    0x1FC0 0000 to 0x1FC0 07BF PIF Boot ROM
    0x1FC0 07C0 to 0x1FC0 07FF PIF RAM
    0x1FC0 0800 to 0x1FCF FFFF Reserved
    0x1FD0 0000 to 0x7FFF FFFF Cartridge Domain 1 Address 3
    0x8000 0000 to 0xFFFF FFFF External SysAD Device
*/

const RDRAM_MEM_START: usize = 0x0000_0000;
const RDRAM_MEM_END: usize = 0x003E_FFFF;

const RDRAM_REG_START: usize = 0x03F0_0000;
const RDRAM_REG_END: usize = 0x03FF_FFFF;

const SP_REG_START: usize = 0x0400_0000;
const SP_REG_END: usize = 0x040F_FFFF;

const DP_CMD_START: usize = 0x0410_0000;
const DP_CMD_END: usize = 0x041F_FFFF;

const DP_SPAN_START: usize = 0x0420_0000;
const DP_SPAN_END: usize = 0x042F_FFFF;

const MI_REG_START: usize = 0x0430_0000;
const MI_REG_END: usize = 0x043F_FFFF;

const VI_REG_START: usize = 0x0440_0000;
const VI_REG_END: usize = 0x044F_FFFF;

const AI_REG_START: usize = 0x0450_0000;
const AI_REG_END: usize = 0x045F_FFFF;

const PI_REG_START: usize = 0x0460_0000;
const PI_REG_END: usize = 0x046F_FFFF;

const RI_REG_START: usize = 0x0470_0000;
const RI_REG_END: usize = 0x047F_FFFF;

const SI_REG_START: usize = 0x0480_0000;
const SI_REG_END: usize = 0x048F_FFFF;

const UNUSED_START: usize = 0x0490_0000;
const UNUSED_END: usize = 0x04FF_FFFF;

const CART_DOM2_A1_START: usize = 0x0500_0000;
const CART_DOM2_A1_END: usize = 0x05FF_FFFF;

const CART_DOM1_A1_START: usize = 0x0600_0000;
const CART_DOM1_A1_END: usize = 0x07FF_FFFF;

const CART_DOM2_A2_START: usize = 0x0800_0000;
const CART_DOM2_A2_END: usize = 0x0FFF_FFFF;

const CART_DOM1_A2_START: usize = 0x1000_0000;
const CART_DOM1_A2_END: usize = 0x1FBF_FFFF;

pub const PIF_ROM_START: usize = 0x1FC0_0000;
const PIF_ROM_END: usize = 0x1FC0_07BF;

pub const PIF_RAM_START: usize = 0x1FC0_07C0;
pub const PIF_RAM_END: usize = 0x1FC0_07FF;

const RESERVED_START: usize = 0x1FC0_0800;
const RESERVED_END: usize = 0x1FCF_FFFF;

const CART_DOM1_A3_START: usize = 0x1FD0_0000;
const CART_DOM1_A3_END: usize = 0x7FFF_FFFF;

const SYSAD_START: usize = 0x8000_0000;
const SYSAD_END: usize = 0xFFFF_FFFF;

fn read32(addr: usize, mem: &Box<[u8]>) -> u32 {
	println!("Reading word from relative address {:#x}", addr);
	/* Obtain a slice starting at the read address. */
	let b: &[u8] = &mem[addr .. addr + 4];
	/* Extract each of the word's bytes and use them to create a u32. */
	let w = ((b[0] as u32) << 24) | ((b[1] as u32) << 16) | ((b[2] as u32) << 8) | b[3] as u32;
    /* Byte swap and adjust the endianness of the read word. */
    u32::from_be(w.swap_bytes())
}

/* Reads a word from the provided N64's memory map. */
pub fn read(n64: &N64, addr: usize) -> u32 {
    println!("Reading word from physical address 0x{:08x}", addr);
    /* Match the memory address to a peripheral address range. */
    match addr {
        RDRAM_MEM_START ... RDRAM_MEM_END       =>
            read32(addr - RDRAM_MEM_START, &n64.iram),
        RDRAM_REG_START ... RDRAM_REG_END       => 0,
        SP_REG_START ... SP_REG_END             => 0,
        DP_CMD_START ... DP_CMD_END             => 0,
        DP_SPAN_START ... DP_SPAN_END           => 0,
        MI_REG_START ... MI_REG_END             => 0,
        VI_REG_START ... VI_REG_END             => 0,
        AI_REG_START ... AI_REG_END             => 0,
        PI_REG_START ... PI_REG_END             => 0,
        RI_REG_START ... RI_REG_END             => 0,
        SI_REG_START ... SI_REG_END             => 0,
        UNUSED_START ... UNUSED_END             => 0,
        CART_DOM2_A1_START ... CART_DOM2_A1_END =>
            read32(addr - CART_DOM2_A1_START, &n64.crom),
        CART_DOM1_A1_START ... CART_DOM1_A1_END =>
            read32(addr - CART_DOM1_A1_START, &n64.crom),
        CART_DOM2_A2_START ... CART_DOM2_A2_END =>
            read32(addr - CART_DOM2_A2_START, &n64.crom),
        CART_DOM1_A2_START ... CART_DOM1_A2_END =>
            read32(addr - CART_DOM1_A2_START, &n64.crom),
        PIF_ROM_START ... PIF_ROM_END           =>
            read32(addr - PIF_ROM_START, &n64.pif.prom),
        PIF_RAM_START ... PIF_RAM_END           =>
            read32(addr - PIF_RAM_START, &n64.pif.pram),
        RESERVED_START ... RESERVED_END         => 0,
        CART_DOM1_A3_START ... CART_DOM1_A3_END =>
            read32(addr - CART_DOM1_A3_START, &n64.crom),
        SYSAD_START ... SYSAD_END               => 0,
        _ => panic!("Unrecognized physical address: {:#x}", addr)
    }
}

use std::io::Write;

fn write32(val: u32, addr: usize, mem: &mut Box<[u8]>) {
	println!("Writing word to relative address {:#x}", addr);
	/* Obtain a slice of bytes from the u32. */
    let from: &[u8] = &[(val >> 24) as u8, (val >> 16) as u8, (val >> 8) as u8, val as u8];
    /* Write the slice into memory. */
    mem[addr .. addr + 4].copy_from_slice(from);
}

/* Writes a word to the provided N64's memory map. */
pub fn write(n64: &mut N64, val: u32, addr: usize) {
    println!("Reading word from physical address 0x{:08x}", addr);
    /* Match the memory address to a peripheral address range. */
    match addr {
        RDRAM_MEM_START ... RDRAM_MEM_END       =>
            write32(val, addr - RDRAM_MEM_START, &mut n64.iram),
        RDRAM_REG_START ... RDRAM_REG_END       =>
            return,
        SP_REG_START ... SP_REG_END             =>
            return,
        DP_CMD_START ... DP_CMD_END             =>
            return,
        DP_SPAN_START ... DP_SPAN_END           =>
            return,
        MI_REG_START ... MI_REG_END             =>
            return,
        VI_REG_START ... VI_REG_END             =>
            return,
        AI_REG_START ... AI_REG_END             =>
            return,
        PI_REG_START ... PI_REG_END             =>
            return,
        RI_REG_START ... RI_REG_END             =>
            return,
        SI_REG_START ... SI_REG_END             =>
            return,
        UNUSED_START ... UNUSED_END             =>
            return,
        CART_DOM2_A1_START ... CART_DOM2_A1_END =>
            panic!("Attempt to write to a read-only location {:#x}.", addr),
        CART_DOM1_A1_START ... CART_DOM1_A1_END =>
            panic!("Attempt to write to a read-only location {:#x}.", addr),
        CART_DOM2_A2_START ... CART_DOM2_A2_END =>
            panic!("Attempt to write to a read-only location {:#x}.", addr),
        CART_DOM1_A2_START ... CART_DOM1_A2_END =>
            panic!("Attempt to write to a read-only location {:#x}.", addr),
        PIF_ROM_START ... PIF_ROM_END           =>
            panic!("Attempt to write to a read-only location {:#x}.", addr),
        PIF_RAM_START ... PIF_RAM_END           =>
            write32(val, addr - PIF_RAM_START, &mut n64.pif.pram),
        RESERVED_START ... RESERVED_END         =>
            return,
        CART_DOM1_A3_START ... CART_DOM1_A3_END =>
            panic!("Attempt to write to a read-only location {:#x}.", addr),
        SYSAD_START ... SYSAD_END               =>
            return,
        _ => panic!("Unrecognized physical address: {:#x}", addr)
    }
}
<|endoftext|>


<commit_before>//! XML-RPC response parser.

use {Value, Response, Fault};
use error::ParseError;

use base64;
use xml::reader::{XmlEvent, EventReader};
use xml::name::OwnedName;
use iso8601::datetime;
use std::io::{self, ErrorKind, Read};
use std::collections::BTreeMap;

pub type ParseResult<T> = Result<T, ParseError>;

/// Reads an `XmlEvent` from a reader, disposing events we want to ignore.
fn pull_event<R: Read>(reader: &mut EventReader<R>) -> ParseResult<XmlEvent> {
    loop {
        let event = try!(reader.next());
        match event {
            XmlEvent::StartDocument { .. }
            | XmlEvent::Comment(_)
            | XmlEvent::Whitespace(_)
            | XmlEvent::ProcessingInstruction { .. } => {},
            XmlEvent::StartElement { .. }
            | XmlEvent::EndElement { .. }
            | XmlEvent::EndDocument
            | XmlEvent::CData(_)
            | XmlEvent::Characters(_) => return Ok(event),
        }
    }
}

/// Expects an opening tag like `<tag>` without attributes (and a local name without namespaces).
fn expect_open<R: Read>(reader: &mut EventReader<R>, tag: &str) -> ParseResult<()> {
    match try!(pull_event(reader)) {
        XmlEvent::StartElement { ref name, ref attributes, .. }
        if name == &OwnedName::local(tag) => {
            if !attributes.is_empty() {
                return unexpected(format!("unexpected attributes in <{}>", tag));
            }

            Ok(())
        }
        _ => return unexpected(format!("expected <{}>", tag)),
    }
}

/// Expects a closing tag like `</tag>` with a local name without namespaces.
fn expect_close<R: Read>(reader: &mut EventReader<R>, tag: &str) -> ParseResult<()> {
    match try!(pull_event(reader)) {
        XmlEvent::EndElement { ref name } if name == &OwnedName::local(tag) => {
            Ok(())
        }
        _ => unexpected(format!("expected </{}>", tag)),
    }
}

/// Builds and returns an `Err(UnexpectedXml)`.
fn unexpected<T, E: ToString>(err: E) -> ParseResult<T> {
    Err(ParseError::UnexpectedXml(err.to_string()))
}

/// Parses a response from an XML reader.
pub fn parse_response<R: Read>(reader: &mut EventReader<R>) -> ParseResult<Response> {
    let response: Response;

    // <methodResponse>
    try!(expect_open(reader, "methodResponse"));

    // <fault> / <params>
    match try!(pull_event(reader)) {
        XmlEvent::StartElement { ref name, ref attributes, .. } => {
            if !attributes.is_empty() {
                return unexpected("unexpected attributes");
            }

            if name == &OwnedName::local("fault") {
                let value = try!(parse_value(reader));
                let fault = try!(Fault::from_value(value).ok_or_else(|| {
                    io::Error::new(ErrorKind::Other, "malformed <fault>")
                }));
                response = Err(fault);
            } else if name == &OwnedName::local("params") {
                // <param>
                try!(expect_open(reader, "param"));

                let value = try!(parse_value(reader));
                response = Ok(value);

                // </param>
                try!(expect_close(reader, "param"));
            } else {
                return unexpected(format!("expected <fault> or <params>, got {}", name));
            }
        }
        _ => return unexpected("expected <fault> or <params>"),
    }

    Ok(response)
}

pub fn parse_value<R: Read>(reader: &mut EventReader<R>) -> ParseResult<Value> {
    let value: Value;

    // <value>
    try!(expect_open(reader, "value"));

    // Raw string or specific type tag
    value = match try!(pull_event(reader)) {
        XmlEvent::StartElement { ref name, ref attributes, .. } => {
            if !attributes.is_empty() {
                return unexpected(format!("unexpected attributes in <{}>", name));
            }

            if name == &OwnedName::local("struct") {
                let mut members = BTreeMap::new();
                loop {
                    match try!(pull_event(reader)) {
                        XmlEvent::EndElement { ref name } if name == &OwnedName::local("struct") => break,
                        XmlEvent::StartElement { ref name, ref attributes, .. } if name == &OwnedName::local("member") => {
                            // <member>
                            if !attributes.is_empty() {
                                return unexpected(format!("unexpected attributes in <{}>", name));
                            }

                            // <name>NAME</name>
                            try!(expect_open(reader, "name"));
                            let name = match try!(pull_event(reader)) {
                                XmlEvent::Characters(string) => string,
                                _ => return unexpected("expected CDATA"),
                            };
                            try!(expect_close(reader, "name"));

                            // Value
                            let value = try!(parse_value(reader));

                            // </member>
                            try!(expect_close(reader, "member"));

                            members.insert(name, value);
                        }
                        _ => return unexpected("expected </struct> or <member>"),
                    }
                }

                Value::Struct(members)
            } else if name == &OwnedName::local("array") {
                unimplemented!();   // TODO
            } else {
                // All other types expect raw characters...
                let data = match try!(pull_event(reader)) {
                    XmlEvent::Characters(string) => string,
                    _ => return unexpected("expected characters"),
                };

                // ...and a corresponding close tag
                try!(expect_close(reader, &name.local_name));

                if name == &OwnedName::local("i4") || name == &OwnedName::local("int") {
                    Value::Int(try!(data.parse::<i32>().map_err(|_| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for integer: {}", data))
                    })))
                } else if name == &OwnedName::local("boolean") {
                    let val = match data.trim() {
                        "0" => false,
                        "1" => true,
                        _ => return Err(io::Error::new(ErrorKind::Other, format!("invalid value for <boolean>: {}", data)).into())
                    };

                    Value::Bool(val)
                } else if name == &OwnedName::local("string") {
                    Value::String(data.clone())
                } else if name == &OwnedName::local("double") {
                    Value::Double(try!(data.parse::<f64>().map_err(|_| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for double: {}", data))
                    })))
                } else if name == &OwnedName::local("dateTime.iso8601") {
                    Value::DateTime(try!(datetime(&data).map_err(|e| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for dateTime.iso8601: {} ({})", data, e))
                    })))
                } else if name == &OwnedName::local("base64") {
                    Value::Base64(try!(base64::decode(&data).map_err(|_| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for base64: {}", data))
                    })))
                } else {
                    return unexpected("invalid <value> content");
                }
            }
        }
        XmlEvent::Characters(string) => {
            Value::String(string)
        }
        _ => return unexpected("invalid <value> content"),
    };

    // </value>
    try!(expect_close(reader, "value"));

    Ok(value)
}

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

    use {Response, Value};
    use xml::EventReader;
    use error::Fault;
    use std::fmt::Debug;

    fn read_response(xml: &str) -> ParseResult<Response> {
        parse_response(&mut EventReader::from_str(xml))
    }

    fn read_value(xml: &str) -> ParseResult<Value> {
        parse_value(&mut EventReader::from_str(xml))
    }

    fn assert_ok<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => println!("assert_ok successful on Ok: {:?}", t),
            Err(e) => panic!("assert_ok called on Err value: {:?}", e),
        }
    }

    fn assert_err<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => panic!("assert_err called on Ok value: {:?}", t),
            Err(e) => println!("assert_err successful on Err: {:?}", e),
        }
    }

    #[test]
    fn parses_fault() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn ignores_additional_fault_fields() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            <member>
               <name>unnecessaryParameter</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn rejects_invalid_faults() {
        // Make sure to reject type errors in <fault>s - They're specified to contain specifically
        // typed fields.
        assert!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><string>I'm not an int!</string></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##).is_err());
    }

    #[test]
    fn parses_string_value_with_whitespace() {
        assert_eq!(read_value("<value><string>  I'm a string!  </string></value>"),
            Ok(Value::String("  I'm a string!  ".into())));
    }

    #[test]
    fn parses_date_values() {
        assert_ok(read_value("<value><dateTime.iso8601>2015-02-18T23:16:09Z</dateTime.iso8601></value>"));
        assert_ok(read_value("<value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>"));
    }

    #[test]
    fn parses_raw_value_as_string() {
        assert_eq!(read_value("<value>\t  I'm a string!  </value>"),
            Ok(Value::String("\t  I'm a string!  ".into())));
    }

    #[test]
    fn rejects_value_with_attributes() {
        // XXX we *should* reject everything with attributes
        assert_err(read_value(r#"<value name="ble">\t  I'm a string!  </value>"#));
    }
}
<commit_msg>Implement parsing of <array> values<commit_after>//! XML-RPC response parser.

use {Value, Response, Fault};
use error::ParseError;

use base64;
use xml::reader::{XmlEvent, EventReader};
use xml::name::OwnedName;
use iso8601::datetime;
use std::io::{self, ErrorKind, Read};
use std::collections::BTreeMap;

pub type ParseResult<T> = Result<T, ParseError>;

/// Reads an `XmlEvent` from a reader, disposing events we want to ignore.
fn pull_event<R: Read>(reader: &mut EventReader<R>) -> ParseResult<XmlEvent> {
    loop {
        let event = try!(reader.next());
        match event {
            XmlEvent::StartDocument { .. }
            | XmlEvent::Comment(_)
            | XmlEvent::Whitespace(_)
            | XmlEvent::ProcessingInstruction { .. } => {},
            XmlEvent::StartElement { .. }
            | XmlEvent::EndElement { .. }
            | XmlEvent::EndDocument
            | XmlEvent::CData(_)
            | XmlEvent::Characters(_) => return Ok(event),
        }
    }
}

/// Expects an opening tag like `<tag>` without attributes (and a local name without namespaces).
fn expect_open<R: Read>(reader: &mut EventReader<R>, tag: &str) -> ParseResult<()> {
    match try!(pull_event(reader)) {
        XmlEvent::StartElement { ref name, ref attributes, .. }
        if name == &OwnedName::local(tag) => {
            if !attributes.is_empty() {
                return unexpected(format!("unexpected attributes in <{}>", tag));
            }

            Ok(())
        }
        _ => return unexpected(format!("expected <{}>", tag)),
    }
}

/// Expects a closing tag like `</tag>` with a local name without namespaces.
fn expect_close<R: Read>(reader: &mut EventReader<R>, tag: &str) -> ParseResult<()> {
    match try!(pull_event(reader)) {
        XmlEvent::EndElement { ref name } if name == &OwnedName::local(tag) => {
            Ok(())
        }
        _ => unexpected(format!("expected </{}>", tag)),
    }
}

/// Builds and returns an `Err(UnexpectedXml)`.
fn unexpected<T, E: ToString>(err: E) -> ParseResult<T> {
    Err(ParseError::UnexpectedXml(err.to_string()))
}

/// Parses a response from an XML reader.
pub fn parse_response<R: Read>(reader: &mut EventReader<R>) -> ParseResult<Response> {
    let response: Response;

    // <methodResponse>
    try!(expect_open(reader, "methodResponse"));

    // <fault> / <params>
    match try!(pull_event(reader)) {
        XmlEvent::StartElement { ref name, ref attributes, .. } => {
            if !attributes.is_empty() {
                return unexpected("unexpected attributes");
            }

            if name == &OwnedName::local("fault") {
                let value = try!(parse_value(reader));
                let fault = try!(Fault::from_value(value).ok_or_else(|| {
                    io::Error::new(ErrorKind::Other, "malformed <fault>")
                }));
                response = Err(fault);
            } else if name == &OwnedName::local("params") {
                // <param>
                try!(expect_open(reader, "param"));

                let value = try!(parse_value(reader));
                response = Ok(value);

                // </param>
                try!(expect_close(reader, "param"));
            } else {
                return unexpected(format!("expected <fault> or <params>, got {}", name));
            }
        }
        _ => return unexpected("expected <fault> or <params>"),
    }

    Ok(response)
}

pub fn parse_value<R: Read>(reader: &mut EventReader<R>) -> ParseResult<Value> {
    let value: Value;

    // <value>
    try!(expect_open(reader, "value"));

    value = try!(parse_value_inner(reader));

    // </value>
    try!(expect_close(reader, "value"));

    Ok(value)
}

pub fn parse_value_inner<R: Read>(reader: &mut EventReader<R>) -> ParseResult<Value> {
    let value: Value;

    // Raw string or specific type tag
    value = match try!(pull_event(reader)) {
        XmlEvent::StartElement { ref name, ref attributes, .. } => {
            if !attributes.is_empty() {
                return unexpected(format!("unexpected attributes in <{}>", name));
            }

            if name == &OwnedName::local("struct") {
                let mut members = BTreeMap::new();
                loop {
                    match try!(pull_event(reader)) {
                        XmlEvent::EndElement { ref name } if name == &OwnedName::local("struct") => break,
                        XmlEvent::StartElement { ref name, ref attributes, .. } if name == &OwnedName::local("member") => {
                            // <member>
                            if !attributes.is_empty() {
                                return unexpected(format!("unexpected attributes in <{}>", name));
                            }

                            // <name>NAME</name>
                            try!(expect_open(reader, "name"));
                            let name = match try!(pull_event(reader)) {
                                XmlEvent::Characters(string) => string,
                                _ => return unexpected("expected CDATA"),
                            };
                            try!(expect_close(reader, "name"));

                            // Value
                            let value = try!(parse_value(reader));

                            // </member>
                            try!(expect_close(reader, "member"));

                            members.insert(name, value);
                        }
                        _ => return unexpected("expected </struct> or <member>"),
                    }
                }

                Value::Struct(members)
            } else if name == &OwnedName::local("array") {
                let mut elements: Vec<Value> = Vec::new();
                try!(expect_open(reader, "data"));
                loop {
                    match try!(pull_event(reader)) {
                        XmlEvent::EndElement { ref name } if name == &OwnedName::local("data") => break,
                        XmlEvent::StartElement { ref name, .. } if name == &OwnedName::local("value") => {
                            elements.push(try!(parse_value_inner(reader)));
                            try!(expect_close(reader, "value"));
                        }
                        _event => return unexpected("expected </data> or <value>")
                    }
                }
                try!(expect_close(reader, "array"));
                Value::Array(elements)
            } else {
                // All other types expect raw characters...
                let data = match try!(pull_event(reader)) {
                    XmlEvent::Characters(string) => string,
                    _ => return unexpected("expected characters"),
                };

                // ...and a corresponding close tag
                try!(expect_close(reader, &name.local_name));

                if name == &OwnedName::local("i4") || name == &OwnedName::local("int") {
                    Value::Int(try!(data.parse::<i32>().map_err(|_| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for integer: {}", data))
                    })))
                } else if name == &OwnedName::local("boolean") {
                    let val = match data.trim() {
                        "0" => false,
                        "1" => true,
                        _ => return Err(io::Error::new(ErrorKind::Other, format!("invalid value for <boolean>: {}", data)).into())
                    };

                    Value::Bool(val)
                } else if name == &OwnedName::local("string") {
                    Value::String(data.clone())
                } else if name == &OwnedName::local("double") {
                    Value::Double(try!(data.parse::<f64>().map_err(|_| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for double: {}", data))
                    })))
                } else if name == &OwnedName::local("dateTime.iso8601") {
                    Value::DateTime(try!(datetime(&data).map_err(|e| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for dateTime.iso8601: {} ({})", data, e))
                    })))
                } else if name == &OwnedName::local("base64") {
                    Value::Base64(try!(base64::decode(&data).map_err(|_| {
                        io::Error::new(ErrorKind::Other, format!("invalid value for base64: {}", data))
                    })))
                } else {
                    return unexpected("invalid <value> content");
                }
            }
        }
        XmlEvent::Characters(string) => {
            Value::String(string)
        }
        _ => return unexpected("invalid <value> content"),
    };

    Ok(value)
}

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

    use {Response, Value};
    use xml::EventReader;
    use error::Fault;
    use std::fmt::Debug;

    fn read_response(xml: &str) -> ParseResult<Response> {
        parse_response(&mut EventReader::from_str(xml))
    }

    fn read_value(xml: &str) -> ParseResult<Value> {
        parse_value(&mut EventReader::from_str(xml))
    }

    fn assert_ok<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => println!("assert_ok successful on Ok: {:?}", t),
            Err(e) => panic!("assert_ok called on Err value: {:?}", e),
        }
    }

    fn assert_err<T: Debug, E: Debug>(result: Result<T, E>) {
        match result {
            Ok(t) => panic!("assert_err called on Ok value: {:?}", t),
            Err(e) => println!("assert_err successful on Err: {:?}", e),
        }
    }

    #[test]
    fn parses_fault() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn ignores_additional_fault_fields() {
        assert_eq!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            <member>
               <name>unnecessaryParameter</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##),
        Ok(Err(Fault {
            fault_code: 4,
            fault_string: "Too many parameters.".into(),
        })));
    }

    #[test]
    fn rejects_invalid_faults() {
        // Make sure to reject type errors in <fault>s - They're specified to contain specifically
        // typed fields.
        assert!(read_response(r##"
<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><string>I'm not an int!</string></value>
               </member>
            <member>
               <name>faultString</name>
               <value><string>Too many parameters.</string></value>
               </member>
            </struct>
         </value>
      </fault>
   </methodResponse>"##).is_err());
    }

    #[test]
    fn parses_string_value_with_whitespace() {
        assert_eq!(read_value("<value><string>  I'm a string!  </string></value>"),
            Ok(Value::String("  I'm a string!  ".into())));
    }

    #[test]
    fn parses_date_values() {
        assert_ok(read_value("<value><dateTime.iso8601>2015-02-18T23:16:09Z</dateTime.iso8601></value>"));
        assert_ok(read_value("<value><dateTime.iso8601>19980717T14:08:55</dateTime.iso8601></value>"));
    }

    #[test]
    fn parses_array_values() {
      assert_eq!(read_value("<value><array><data><value><i4>5</i4></value><value><string>a</string></value></data></array></value>"), Ok(Value::Array(vec![Value::Int(5), Value::String("a".into())])));
    }

    #[test]
    fn parses_raw_value_as_string() {
        assert_eq!(read_value("<value>\t  I'm a string!  </value>"),
            Ok(Value::String("\t  I'm a string!  ".into())));
    }

    #[test]
    fn rejects_value_with_attributes() {
        // XXX we *should* reject everything with attributes
        assert_err(read_value(r#"<value name="ble">\t  I'm a string!  </value>"#));
    }
}
<|endoftext|>


<commit_before>use ast::ExprAst;
use ast::BoolNode;
use ast::CharNode;
use ast::IntNode;
use ast::StrNode;

#[allow(dead_code)]

#[deriving(Clone, Show)]
pub struct Parser {
    code: String,
    cur: uint,
    col: uint,
    line: uint
}

pub struct ParserError {
    line: uint,
    col: uint,
    desc: String
}

impl ParserError {
    pub fn new(line: uint, col: uint, desc: String) -> ParserError {
        ParserError {
            line: line,
            col: col,
            desc: desc
        }
    }
}

pub type ParseResult<T> = Result<T, ParserError>;

#[allow(dead_code)]
impl Parser {
    pub fn new() -> Parser {
        Parser{
            code: "".to_string(),
            line: 0,
            cur: 0,
            col: 0
        }
    }

    pub fn load(&mut self, code: String) -> ExprAst {
        self.code = code;
        self.line = 1;
        self.cur = 0;
        self.col = 1;
        self.skip_white();

        let mut cur = self.readc();
        if cur == '#' {
            let next = self.readc();
            match next {
                't' => return ExprAst::Bool(BoolNode::new(true)),
                'f' => return ExprAst::Bool(BoolNode::new(false)),
                '\\' => return self.read_char(),
                _ => panic!("error")
            }
        } else if UnicodeChar::is_numeric(cur) ||
            (cur == '-' && (UnicodeChar::is_numeric(self.peekc()))) {
            let mut sign = 1i;
            if cur == '-' {
                sign = -1;
            } else {
                self.unread();
            }
            let mut num = 0i;
            loop {
                cur = self.readc();
                if !UnicodeChar::is_numeric(cur) {
                    break;
                }
                num = (num * 10i) + (cur as int - 0i);
            }
            num *= sign;
            if self.is_delimiter(cur) {
                self.unread();
                return ExprAst::Int(IntNode::new(num));
            } else {
                panic!("number not followed by delimiter");
            }
        } else if cur == '\"' {
            let mut buf = String::new();
            loop {
                cur = self.readc();
                if cur == '\"' {
                    break;
                }
                buf.push(cur);
            }
            return ExprAst::Str(StrNode::new(buf));
        }
        ExprAst::Char(CharNode::new('a'))
    }

    //============= private methods =================

    fn is_delimiter(&self, ch: char) -> bool {
        if ch.is_whitespace() ||
            ch == '\"' || ch == '(' || ch == ')' ||  ch == ';' ||
            ch as u32 == 0 {
                true
            } else {
                false
            }
    }

    fn is_initial(&self, ch: char) -> bool {
        if ch.is_alphabetic() ||
            ch == '*' || ch == '/' ||
            ch == '+' || ch == '-' ||
            ch == '>' || ch == '<' ||
            ch == '=' || ch == '?' ||
            ch == '!' {
                true
            } else {
                false
            }
    }

    fn skip_white(&mut self) {
        while self.peekc().is_whitespace() {
            if self.peekc() == '\n' {
                self.line += 1;
            }
            self.cur += 1;
        }
    }

    fn peekc(&self) -> char {
        if self.cur < self.code.len() {
            self.code.char_at(self.cur)
        } else {
            0 as char
        }
    }

    fn readc(&mut self) -> char {
        if self.cur < self.code.len() {
            let res = self.code.char_at(self.cur);
            self.cur += 1;
            res
        } else {
            0 as char
        }
    }

    fn unread(&mut self) {
        if self.cur == 0 {
            panic!("error current position");
        }
        if self.peekc() == '\n' {
            assert!(self.line > 1);
            self.line -= 1;
        }
        self.cur -= 1;
    }

    fn read_char(&mut self) -> ExprAst {
        ExprAst::Char(CharNode::new('a'))
    }

}
<commit_msg>refactor<commit_after>use ast::ExprAst;
use ast::BoolNode;
use ast::CharNode;
use ast::IntNode;
use ast::StrNode;

#[allow(dead_code)]

#[deriving(Clone, Show)]
pub struct Parser {
    code: String,
    cur: uint,
    col: uint,
    line: uint
}

pub struct ParserError {
    line: uint,
    col: uint,
    desc: String
}

impl ParserError {
    pub fn new(line: uint, col: uint, desc: String) -> ParserError {
        ParserError {
            line: line,
            col: col,
            desc: desc
        }
    }
}

pub type ParseResult<T> = Result<T, ParserError>;

#[allow(dead_code)]
impl Parser {
    pub fn new() -> Parser {
        Parser{
            code: "".to_string(),
            line: 0,
            cur: 0,
            col: 0
        }
    }

    pub fn load(&mut self, code: String) -> ExprAst {
        self.code = code;
        self.line = 1;
        self.cur = 0;
        self.col = 0;
        self.skip_space();

        let mut cur = self.readc();
        if cur == '#' {
            let next = self.readc();
            match next {
                't' => return ExprAst::Bool(BoolNode::new(true)),
                'f' => return ExprAst::Bool(BoolNode::new(false)),
                '\\' => return self.read_char(),
                _ => panic!("error")
            }
        } else if UnicodeChar::is_numeric(cur) ||
            (cur == '-' && (UnicodeChar::is_numeric(self.peekc()))) {
            let mut sign = 1i;
            if cur == '-' {
                sign = -1;
            } else {
                self.unread();
            }
            let mut num = 0i;
            loop {
                cur = self.readc();
                if !UnicodeChar::is_numeric(cur) {
                    break;
                }
                num = (num * 10i) + (cur as int - 0i);
            }
            num *= sign;
            if self.is_delimiter(cur) {
                self.unread();
                return ExprAst::Int(IntNode::new(num));
            } else {
                panic!("number not followed by delimiter");
            }
        } else if cur == '\"' {
            let mut buf = String::new();
            loop {
                cur = self.readc();
                if cur == '\"' {
                    break;
                }
                buf.push(cur);
            }
            return ExprAst::Str(StrNode::new(buf));
        }
        ExprAst::Char(CharNode::new('a'))
    }

    //============= private methods =================
    fn is_delimiter(&self, ch: char) -> bool {
        if ch.is_whitespace() ||
            ch == '\"' || ch == '(' || ch == ')' ||  ch == ';' ||
            ch as u32 == 0 {
                true
            } else {
                false
            }
    }

    fn is_initial(&self, ch: char) -> bool {
        if ch.is_alphabetic() ||
            ch == '*' || ch == '/' ||
            ch == '+' || ch == '-' ||
            ch == '>' || ch == '<' ||
            ch == '=' || ch == '?' ||
            ch == '!' {
                true
            } else {
                false
            }
    }

    fn skip_space(&mut self) {
        while self.peekc().is_whitespace() {
            self.readc();
        }
    }

    fn peekc(&self) -> char {
        if self.cur >= self.code.len() {
            panic!("invalid position");
        }
        self.code.char_at(self.cur)
    }

    fn readc(&mut self) -> char {
        if self.cur < self.code.len() {
            let res = self.peekc();
            if res == '\n' {
                self.col = 0;
                self.line += 1;
            } else {
                self.col += 1;
            }
            self.cur += 1;
            res
        } else {
            0 as char
        }
    }

    fn unread(&mut self) {
        if self.cur == 0 {
            panic!("error current position");
        }
        if self.peekc() == '\n' {
            assert!(self.line > 1);
            self.line -= 1;
        }
        self.cur -= 1;
    }

    fn read_char(&mut self) -> ExprAst {
        ExprAst::Char(CharNode::new('a'))
    }

}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use regex::Regex;
use std::collections::HashMap;
use lexer;

pub type Token = lexer::Token;
pub type Lexer = lexer::Lexer;

pub struct Parser {
    lexer: Lexer,
    variables: HashMap<String, f64>,
}

impl Parser {
    pub fn new() -> Self {
        Parser{lexer: Lexer::new(), variables: HashMap::new()}
    }
    pub fn parse(&mut self, input: String) -> Result<f64, String> {
        lazy_static! {
            static ref VAR_ASSIGN_RE: Regex = Regex::new(r"\A(\w+)=").unwrap();
        }
        let temp = &(input.clone())[..];
        let mut variable: String = String::default();
        if VAR_ASSIGN_RE.is_match(temp) {
            // Unwrapping is safe because the match is known to exist
            // The second element `at(1)`, is the variable name captured by the parenthesis in the regex
            variable = String::from(VAR_ASSIGN_RE.captures(temp).unwrap().at(1).unwrap());
            self.lexer.set_input(VAR_ASSIGN_RE.replace(temp, ""));
        } else {
            self.lexer.set_input(input);
        }
        let expression_value: f64 = match self.expression() {
            Ok(v) => v,
            Err(msg) => return Err(msg)
        };
        let token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        match token.get_kind() {
            lexer::SubToken::End => {
                if variable != String::default() {
                    self.variables.insert(variable, expression_value);
                }
                return Ok(expression_value);
            }
            _ => {
                return Err(String::from("End expected"));
            }
        }
    }
    pub fn expression(&mut self) -> Result<f64, String> {
        let mut component1: f64 = match self.factor() {
            Ok(x) => x,
            Err(msg) => return Err(msg)
        };
        let mut token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        loop {
            match token.get_kind() {
                lexer::SubToken::Plus => {
                    let component2 = match self.factor() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    component1 += component2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Minus => {
                    let component2 = match self.factor() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    component1 -= component2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                _ => {
                    break;
                }
            }
        }
        self.lexer.revert();
        Ok(component1)
    }
    pub fn factor(&mut self) -> Result<f64, String> {
        let mut factor1: f64 = match self.number() {
            Ok(x) => x,
            Err(msg) => return Err(msg)
        };
        let mut token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        loop {
            match token.get_kind() {
                lexer::SubToken::Multiply => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 *= factor2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Divide => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 /= factor2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Power => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 = factor1.powf(factor2);
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Modulo => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 = factor1 % factor2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                _ => {
                    break;
                }
            }
        }
        self.lexer.revert();
        Ok(factor1)
    }
    pub fn number(&mut self) -> Result<f64, String> {
        let token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        let value: f64;
        match token.get_kind() {
            lexer::SubToken::LParen => {
                value = match self.expression() {
                    Ok(v) => v,
                    Err(msg) => return Err(msg)
                };
                match self.lexer.get_next_token() {
                    Err(msg) => return Err(msg),
                    _ => ()
                };
            }
            lexer::SubToken::Number => {
                value = match token.get_value() {
                    Some(v) => v,
                    None => return Err(String::from("No value"))
                };
            }
            lexer::SubToken::Minus => {
                value = match self.factor() {
                    Ok(x) => x*-1.0,
                    Err(msg) => return Err(msg)
                };
            }
            _ => {
                return Err(String::from("Not a number"));
            }
        }
        Ok(value)
    }
}<commit_msg>Add support for using variables in expressions<commit_after>#![allow(dead_code)]

use regex::Regex;
use std::collections::HashMap;
use lexer;

pub type Token = lexer::Token;
pub type Lexer = lexer::Lexer;

pub struct Parser {
    lexer: Lexer,
    variables: HashMap<String, f64>,
}

impl Parser {
    pub fn new() -> Self {
        Parser{lexer: Lexer::new(), variables: HashMap::new()}
    }
    pub fn parse(&mut self, input: String) -> Result<f64, String> {
        lazy_static! {
            static ref VAR_ASSIGN_RE: Regex = Regex::new(r"\A(\w+)=").unwrap();
            static ref VAR_USAGE_RE: Regex = Regex::new(r"[a-zA-Z]+").unwrap();
            // Theoretical function matching might look like [a-zA-Z]+\( etc...
        }
        let mut internal_input = input.clone();
        let mut new_variable: String = String::default();
        if VAR_ASSIGN_RE.is_match(internal_input.as_ref()) {
            new_variable = String::from(VAR_ASSIGN_RE.captures(internal_input.as_ref()).unwrap().at(1).unwrap());
            internal_input = VAR_ASSIGN_RE.replace(internal_input.as_ref(), "");
        }
        while VAR_USAGE_RE.is_match(internal_input.as_ref()) {
            let variable = String::from(VAR_USAGE_RE.captures(internal_input.as_ref()).unwrap().at(0).unwrap());
            if let Some(value) = self.variables.get(&variable) {
                internal_input = VAR_USAGE_RE.replace(internal_input.as_ref(), &(value.to_string())[..]);
            } else {
                let mut msg = String::from("Unknown variable: ");
                msg.push_str(variable.as_ref());
                return Err(msg);
            }
        }
        self.lexer.set_input(internal_input);
        let expression_value: f64 = match self.expression() {
            Ok(v) => v,
            Err(msg) => return Err(msg)
        };
        let token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        match token.get_kind() {
            lexer::SubToken::End => {
                if new_variable != String::default() {
                    self.variables.insert(new_variable, expression_value);
                }
                return Ok(expression_value);
            }
            _ => {
                return Err(String::from("End expected"));
            }
        }
    }
    pub fn expression(&mut self) -> Result<f64, String> {
        let mut component1: f64 = match self.factor() {
            Ok(x) => x,
            Err(msg) => return Err(msg)
        };
        let mut token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        loop {
            match token.get_kind() {
                lexer::SubToken::Plus => {
                    let component2 = match self.factor() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    component1 += component2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Minus => {
                    let component2 = match self.factor() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    component1 -= component2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                _ => {
                    break;
                }
            }
        }
        self.lexer.revert();
        Ok(component1)
    }
    pub fn factor(&mut self) -> Result<f64, String> {
        let mut factor1: f64 = match self.number() {
            Ok(x) => x,
            Err(msg) => return Err(msg)
        };
        let mut token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        loop {
            match token.get_kind() {
                lexer::SubToken::Multiply => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 *= factor2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Divide => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 /= factor2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Power => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 = factor1.powf(factor2);
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                lexer::SubToken::Modulo => {
                    let factor2: f64 = match self.number() {
                        Ok(x) => x,
                        Err(msg) => return Err(msg)
                    };
                    factor1 = factor1 % factor2;
                    token = match self.lexer.get_next_token() {
                        Ok(t) => t,
                        Err(msg) => return Err(msg)
                    };
                }
                _ => {
                    break;
                }
            }
        }
        self.lexer.revert();
        Ok(factor1)
    }
    pub fn number(&mut self) -> Result<f64, String> {
        let token: Token = match self.lexer.get_next_token() {
            Ok(t) => t,
            Err(msg) => return Err(msg)
        };
        let value: f64;
        match token.get_kind() {
            lexer::SubToken::LParen => {
                value = match self.expression() {
                    Ok(v) => v,
                    Err(msg) => return Err(msg)
                };
                match self.lexer.get_next_token() {
                    Err(msg) => return Err(msg),
                    _ => ()
                };
            }
            lexer::SubToken::Number => {
                value = match token.get_value() {
                    Some(v) => v,
                    None => return Err(String::from("No value"))
                };
            }
            lexer::SubToken::Minus => {
                value = match self.factor() {
                    Ok(x) => x*-1.0,
                    Err(msg) => return Err(msg)
                };
            }
            _ => {
                return Err(String::from("Not a number"));
            }
        }
        Ok(value)
    }
}<|endoftext|>