<commit_before>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone,Copy)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        if let Some(mut index) = self.instruction_pointer {
            match self.status {
                ProgramStatus::Normal => {
                    while index < self.instructions.len() {
                        match self.instructions[index] {
                            Command::JumpForward => {
                                let (tmp_index, status) = self.start_jump_forward(vm, index);
                                self.status = status;
                                index = tmp_index;
                            },
                            Command::JumpBackward => index = self.jump_backward(&vm, index),
                            command => {
                                vm.apply(command);
                                index += 1;
                            }
                        }
                        self.instruction_pointer = Some(index);
                    }
                },
                ProgramStatus::Seeking(goal_depth) => {
                    let (new_index, new_status) = self.seek_forward(index, goal_depth);
                    self.instruction_pointer = Some(new_index);
                    self.status = new_status;
                    self.execute(vm);
                }
            }
        }
    }

    fn jump_backward (&mut self, vm: &VM, starting_index: usize) -> usize {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth = self.current_depth - 1;
                starting_index + 1
            },
            _ => {
                let goal_depth = self.current_depth;
                for index in (0..starting_index).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth { return index + 1 }
                            self.current_depth -= 1;
                        },
                        _ => {},
                    }
                }
                panic!("No starting brace found!");
            },
        }
    }

    fn seek_forward(&mut self, starting_index: usize, goal_depth: u64) -> (usize, ProgramStatus) {
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth += 1,
                Command::JumpBackward => self.current_depth -= 1,
                _ => {},
            }
            if self.current_depth == goal_depth {
                return (index + 1, ProgramStatus::Normal)
            }
        }
        return (self.instructions.len(), self.status)
    }

    fn start_jump_forward(&mut self, vm: &VM, index: usize) -> (usize, ProgramStatus) {
        match vm.cells[vm.data_pointer] {
            0 => {
                let goal_depth = self.current_depth;
                return self.seek_forward(index, goal_depth);
            },
            _ => {
                self.current_depth += 1;
                return (index + 1, ProgramStatus::Normal);
            }
        }
    }
}
<commit_msg>Simplify execute body further<commit_after>use command::Command;
use vm::VM;

pub struct Program {
    instructions : Vec<Command>,
    instruction_pointer: Option<usize>,
    current_depth: u64,
    pub status: ProgramStatus,
}

#[derive(Clone,Copy)]
pub enum ProgramStatus {
    Normal,
    Seeking(u64),
}

impl Program {
    pub fn new () -> Program {
        Program {
            instructions: Vec::new(),
            instruction_pointer: None,
            current_depth: 0,
            status: ProgramStatus::Normal,
        }
    }

    pub fn append(&mut self, instructions: &[Command]) {
        self.instructions.extend(instructions.iter().cloned());
        if self.instruction_pointer.is_none() { self.instruction_pointer = Some(0); }
    }

    pub fn execute(&mut self, vm: &mut VM) {
        match (self.instruction_pointer, self.status) {
            (Some(mut index), ProgramStatus::Normal) => {
                while index < self.instructions.len() {
                    match self.instructions[index] {
                        Command::JumpForward => {
                            let jump_report = self.start_jump_forward(vm, index);
                            index = jump_report.0;
                            self.status = jump_report.1;
                        },
                        Command::JumpBackward => index = self.jump_backward(&vm, index),
                        command => {
                            vm.apply(command);
                            index += 1;
                        }
                    }
                    self.instruction_pointer = Some(index);
                }
            },

            (Some(index), ProgramStatus::Seeking(goal_depth)) => {
                let (new_index, new_status) = self.seek_forward(index, goal_depth);
                self.instruction_pointer = Some(new_index);
                self.status = new_status;
                self.execute(vm);
            },

            _ => {}
        }
    }

    fn jump_backward (&mut self, vm: &VM, starting_index: usize) -> usize {
        match vm.cells[vm.data_pointer] {
            0 => {
                self.current_depth = self.current_depth - 1;
                starting_index + 1
            },
            _ => {
                let goal_depth = self.current_depth;
                for index in (0..starting_index).rev() {
                    match self.instructions[index] {
                        Command::JumpBackward => self.current_depth += 1,
                        Command::JumpForward => {
                            if self.current_depth == goal_depth { return index + 1 }
                            self.current_depth -= 1;
                        },
                        _ => {},
                    }
                }
                panic!("No starting brace found!");
            },
        }
    }

    fn seek_forward(&mut self, starting_index: usize, goal_depth: u64) -> (usize, ProgramStatus) {
        for index in starting_index..self.instructions.len() {
            match self.instructions[index] {
                Command::JumpForward => self.current_depth += 1,
                Command::JumpBackward => self.current_depth -= 1,
                _ => {},
            }
            if self.current_depth == goal_depth {
                return (index + 1, ProgramStatus::Normal)
            }
        }
        return (self.instructions.len(), self.status)
    }

    fn start_jump_forward(&mut self, vm: &VM, index: usize) -> (usize, ProgramStatus) {
        match vm.cells[vm.data_pointer] {
            0 => {
                let goal_depth = self.current_depth;
                return self.seek_forward(index, goal_depth);
            },
            _ => {
                self.current_depth += 1;
                return (index + 1, ProgramStatus::Normal);
            }
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(any(target_os = "macos", target_os = "linux"))]
mod common;

#[cfg(any(target_os = "macos", target_os = "linux"))]
mod tests {
    const TEST_PLATFORM: &str = "wasm32";

    use super::common::check_request_received_using;
    use std::fs;
    use std::path::PathBuf;

    // to run this test, run it as:
    // cargo test --test test_wasm32 -- --ignored
    //
    #[ignore]
    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
    async fn test_wasm32() {
        let uri = &format!("/{}", TEST_PLATFORM);
        let ipv4 = "127.0.0.1";
        check_request_received_using(uri.into(), ipv4, |url| {
            // modify html to use the correct url
            let mut app_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
            app_dir.push("tests/test-wasm-app");
            let mut src_html = PathBuf::from(&app_dir);
            src_html.push("test.html");
            let mut dst_html = PathBuf::from(&app_dir);
            dst_html.push("pkg/test.html");
            let old_html = fs::read_to_string(&src_html).expect("failed to read test.html");
            let new_html = old_html.replace("DYNAMIC_URL_TBD", url);
            fs::write(&dst_html, &new_html).expect("failed to update dst test.html");

            // open browser
            let html_url = url.replace(uri, "/static/wasm/test.html");
            //println!("URL: {}", html_url);
            let status = webbrowser::open(&html_url);

            // check for browser run status
            status.expect("browser open failed");
        })
        .await;
    }
}
<commit_msg>fix wasm test<commit_after>#[cfg(any(target_os = "macos", target_os = "linux"))]
mod common;

#[cfg(any(target_os = "macos", target_os = "linux"))]
mod tests {
    const TEST_PLATFORM: &str = "wasm32";

    use super::common::check_request_received_using;
    use std::fs;
    use std::path::PathBuf;

    // to run this test, run it as:
    // cargo test --test test_wasm32 -- --ignored
    //
    #[ignore]
    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
    async fn test_wasm32() {
        let uri = &format!("/{}", TEST_PLATFORM);
        let ipv4 = "127.0.0.1";
        check_request_received_using(uri.into(), ipv4, |url| {
            // modify html to use the correct url
            let mut app_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
            app_dir.push("tests/test-wasm-app");
            let mut src_html = PathBuf::from(&app_dir);
            src_html.push("test.html");
            let mut dst_html = PathBuf::from(&app_dir);
            dst_html.push("pkg/test.html");
            let old_html = fs::read_to_string(&src_html).expect("failed to read test.html");
            let new_html = old_html.replace("DYNAMIC_URL_TBD", url);
            fs::write(&dst_html, &new_html).expect("failed to update dst test.html");

            // ensure favicon is present
            let mut favicon = PathBuf::from(&app_dir);
            favicon.push("pkg/favicon.ico");
            fs::write(&favicon, "").expect("failed to create favicon.ico");

            // open browser
            let html_url = url.replace(uri, "/static/wasm/test.html");
            //println!("URL: {}", html_url);
            let status = webbrowser::open(&html_url);

            // check for browser run status
            status.expect("browser open failed");
        })
        .await;
    }
}
<|endoftext|>


<commit_before>// not sure why needed ....
extern crate anne;

use anne::blocks::{LogisticBlock , BlockData};
use anne::core::{Block};
// , BlockData};

    #[test]
     fn fullmesh_integration ()
     {
         unsafe
         {
             static input_buf: &'static [f32] = &[1f32, 2f32, 3f32, 4f32, 5f32];
             static mut output_buf: & 'static mut [f32] = & mut [1f32, 2f32, 3f32, 4f32, 5f32];
             static  weights: & 'static  [f32] = & [ 0f32  ; 5];

             let mut block_data = BlockData::new(5);
             block_data.neuron_count = 5;
             block_data.synapse_count = 5;

             let mut block  =  LogisticBlock::new(block_data
                     , weights
                     , output_buf
                     , input_buf
             );

             block.process_buffers();
             // assert output is correct

             assert_eq!(output_buf, & [0.5f32 ;5]);

         }// unsafe
     }
<commit_msg>working on more tests<commit_after>// not sure why needed ....
extern crate anne;

use anne::blocks::{LogisticBlock , BlockData};
use anne::core::{Block};
// , BlockData};

    #[test]
     fn fullmesh_integration_w0 ()
     {
         unsafe
         {
             static INPUT_BUF: &'static [f32] = &[1f32, 2f32, 3f32, 4f32, 5f32];
             static mut OUTPUT_BUF: & 'static mut [f32] = & mut [1f32, 2f32, 3f32, 4f32, 5f32];
             static  WEIGHTS: & 'static  [f32] = & [ 0f32  ; 5];

             let mut block_data = BlockData::new(5);
             block_data.neuron_count = 5;
             block_data.synapse_count = 5;

             let mut block = LogisticBlock::new(block_data, WEIGHTS, OUTPUT_BUF, INPUT_BUF);
             block.process_buffers();

             assert_eq!(OUTPUT_BUF, & [0.5f32 ;5]);
         }// unsafe
     }

     #[test]
      fn fullmesh_integration_w05 ()
      {
          unsafe
          {
              static INPUT_BUF: &'static [f32] = &[1f32, 2f32, 3f32, 4f32, 5f32];
              static mut OUTPUT_BUF: & 'static mut [f32] = & mut [1f32, 2f32, 3f32, 4f32, 5f32];
              static  WEIGHTS: & 'static  [f32] = & [ 0.5f32  ; 5];

              let mut block_data = BlockData::new(5);
              block_data.neuron_count = 5;
              block_data.synapse_count = 5;

              let mut block = LogisticBlock::new(block_data, WEIGHTS, OUTPUT_BUF, INPUT_BUF);
              block.process_buffers();

              assert_eq!(OUTPUT_BUF, & [0.99944717f32 ;5]);
          }// unsafe
      }
<|endoftext|>


<commit_before>use bitflags::bitflags;
use std::borrow::Cow;

mod barchart;
mod block;
pub mod canvas;
mod chart;
mod gauge;
mod list;
mod paragraph;
mod reflow;
mod sparkline;
mod table;
mod tabs;

pub use self::barchart::BarChart;
pub use self::block::Block;
pub use self::chart::{Axis, Chart, Dataset, Marker};
pub use self::gauge::Gauge;
pub use self::list::{List, SelectableList};
pub use self::paragraph::Paragraph;
pub use self::sparkline::Sparkline;
pub use self::table::{Row, Table};
pub use self::tabs::Tabs;

use crate::backend::Backend;
use crate::buffer::Buffer;
use crate::layout::Rect;
use crate::style::{Color, Style};
use crate::terminal::Frame;

bitflags! {
    /// Bitflags that can be composed to set the visible borders essentially on the block widget.
    pub struct Borders: u32 {
        /// Show no border (default)
        const NONE  = 0b0000_0001;
        /// Show the top border
        const TOP   = 0b0000_0010;
        /// Show the right border
        const RIGHT = 0b0000_0100;
        /// Show the bottom border
        const BOTTOM = 0b000_1000;
        /// Show the left border
        const LEFT = 0b0001_0000;
        /// Show all borders
        const ALL = Self::TOP.bits | Self::RIGHT.bits | Self::BOTTOM.bits | Self::LEFT.bits;
    }
}

#[derive(Clone, Debug)]
pub enum Text<'b> {
    Raw(Cow<'b, str>),
    Styled(Cow<'b, str>, Style),
}

impl<'b> Text<'b> {
    pub fn raw<D: Into<Cow<'b, str>>>(data: D) -> Text<'b> {
        Text::Raw(data.into())
    }

    pub fn styled<D: Into<Cow<'b, str>>>(data: D, style: Style) -> Text<'b> {
        Text::Styled(data.into(), style)
    }
}

/// Base requirements for a Widget
pub trait Widget {
    /// Draws the current state of the widget in the given buffer. That the only method required to
    /// implement a custom widget.
    fn draw(&mut self, area: Rect, buf: &mut Buffer);
    /// Helper method to quickly set the background of all cells inside the specified area.
    fn background(&self, area: Rect, buf: &mut Buffer, color: Color) {
        for y in area.top()..area.bottom() {
            for x in area.left()..area.right() {
                buf.get_mut(x, y).set_bg(color);
            }
        }
    }
    /// Helper method that can be chained with a widget's builder methods to render it.
    fn render<B>(&mut self, f: &mut Frame<B>, area: Rect)
    where
        Self: Sized,
        B: Backend,
    {
        f.render(self, area);
    }
}
<commit_msg>Add PartialEq to Text<commit_after>use bitflags::bitflags;
use std::borrow::Cow;

mod barchart;
mod block;
pub mod canvas;
mod chart;
mod gauge;
mod list;
mod paragraph;
mod reflow;
mod sparkline;
mod table;
mod tabs;

pub use self::barchart::BarChart;
pub use self::block::Block;
pub use self::chart::{Axis, Chart, Dataset, Marker};
pub use self::gauge::Gauge;
pub use self::list::{List, SelectableList};
pub use self::paragraph::Paragraph;
pub use self::sparkline::Sparkline;
pub use self::table::{Row, Table};
pub use self::tabs::Tabs;

use crate::backend::Backend;
use crate::buffer::Buffer;
use crate::layout::Rect;
use crate::style::{Color, Style};
use crate::terminal::Frame;

bitflags! {
    /// Bitflags that can be composed to set the visible borders essentially on the block widget.
    pub struct Borders: u32 {
        /// Show no border (default)
        const NONE  = 0b0000_0001;
        /// Show the top border
        const TOP   = 0b0000_0010;
        /// Show the right border
        const RIGHT = 0b0000_0100;
        /// Show the bottom border
        const BOTTOM = 0b000_1000;
        /// Show the left border
        const LEFT = 0b0001_0000;
        /// Show all borders
        const ALL = Self::TOP.bits | Self::RIGHT.bits | Self::BOTTOM.bits | Self::LEFT.bits;
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum Text<'b> {
    Raw(Cow<'b, str>),
    Styled(Cow<'b, str>, Style),
}

impl<'b> Text<'b> {
    pub fn raw<D: Into<Cow<'b, str>>>(data: D) -> Text<'b> {
        Text::Raw(data.into())
    }

    pub fn styled<D: Into<Cow<'b, str>>>(data: D, style: Style) -> Text<'b> {
        Text::Styled(data.into(), style)
    }
}

/// Base requirements for a Widget
pub trait Widget {
    /// Draws the current state of the widget in the given buffer. That the only method required to
    /// implement a custom widget.
    fn draw(&mut self, area: Rect, buf: &mut Buffer);
    /// Helper method to quickly set the background of all cells inside the specified area.
    fn background(&self, area: Rect, buf: &mut Buffer, color: Color) {
        for y in area.top()..area.bottom() {
            for x in area.left()..area.right() {
                buf.get_mut(x, y).set_bg(color);
            }
        }
    }
    /// Helper method that can be chained with a widget's builder methods to render it.
    fn render<B>(&mut self, f: &mut Frame<B>, area: Rect)
    where
        Self: Sized,
        B: Backend,
    {
        f.render(self, area);
    }
}
<|endoftext|>


<commit_before>use error::Error;
use pos::Pos;
use token::{Token, TokenType};

use std::str::Chars;
use std::iter::{Fuse, Peekable};

pub struct Scanner<'a> {
    data: Peekable<Fuse<Chars<'a>>>,
    start_pos: Pos,
    curr_pos: Pos,
}

impl<'a> Scanner<'a> {
    // Create a new scanner from a given program represented as a
    // String.
    pub fn new<'b>(data: &'b str) -> Scanner<'b> {
        Scanner {
            data: data.chars().fuse().peekable(),
            start_pos: Pos { line: 1, col: 1 },
            curr_pos: Pos { line: 1, col: 1 },
        }
    }

    // Internal function: return the character at the current index.
    fn peek(&mut self) -> char {
        self.data.peek().map(|&c|c).unwrap_or('\x00')
    }

    // Internal function: return the character at the current index
    // and increment the index by one.
    fn advance(&mut self) -> char {
        let c = self.peek();
        if c == '\n' {
            self.curr_pos.line += 1;
            self.curr_pos.col = 1;
        } else {
            self.curr_pos.col += 1
        }
        c
    }

    // Internal function: verify if the end of the program has been
    // reached.
    fn is_eof(&mut self) -> bool {
        self.data.peek().is_some()
    }

    pub fn next_token(&mut self) -> Result<Token, Error> {
        // Discard blanks and comments.
        self.skip_comments_and_whitespace();

        // Set start_pos (starting position of the next token) to the
        // value of curr_pos (current position in the text stream).
        self.start_pos = self.curr_pos;

        // Return Eof if the end of the file has been reached.
        if self.is_eof() {
            return Ok(self.empty_tok(TokenType::Eof));
        }

        // Scanning dispatch.
        match self.peek() {
            '+' => { Ok(self.single_char_tok(TokenType::Plus)) }
            '-' => { Ok(self.single_char_tok(TokenType::Minus)) }
            '*' => { Ok(self.single_char_tok(TokenType::Star)) }
            '/' => { Ok(self.single_char_tok(TokenType::Slash)) }
            '=' => { Ok(self.single_char_tok(TokenType::Equal)) }
            '(' => { Ok(self.single_char_tok(TokenType::LParen)) }
            ')' => { Ok(self.single_char_tok(TokenType::RParen)) }
            ':' => { Ok(self.single_char_tok(TokenType::Colon)) }
            ';' => { Ok(self.single_char_tok(TokenType::Semicolon)) }
            c if c.is_digit(10) => { self.scan_int_or_float() }
            c if is_id_start(c) => { self.scan_id_or_keyword() }
            c   => { Err(Error::IllegalCharacter(self.curr_pos, c)) }
        }
    }

    // Scan digits into an Int or Float token.
    fn scan_int_or_float(&mut self) -> Result<Token, Error> {
        let mut val = String::new();
        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        if self.peek() != '.' {
            return Ok(self.lexeme_tok(TokenType::Int, val));
        }

        val.push(self.advance()); // Add decimal point.

        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        Ok(self.lexeme_tok(TokenType::Float, val))
    }

    // Scan alpha-numeric characters into an Id or a keyword token.
    fn scan_id_or_keyword(&mut self) -> Result<Token, Error> {
        let mut lexeme = String::new();
        while is_id_char(self.peek()) {
            lexeme.push(self.advance());
        }

        let token_type = match &*lexeme {
            "if" => TokenType::If,
            "then" => TokenType::Then,
            "else" => TokenType::Else,
            "end" => TokenType::End,
            "while" => TokenType::While,
            "do" => TokenType::Do,
            "done" => TokenType::Done,
            "read" => TokenType::Read,
            "print" => TokenType::Print,
            "var" => TokenType::Var,
            "int" => TokenType::TypeInt,
            "float" => TokenType::TypeFloat,
            _ => TokenType::Id,
        };

        let token = if token_type == TokenType::Id {
            self.lexeme_tok(token_type, lexeme)
        } else {
            self.empty_tok(token_type)
        };

        Ok(token)
    }

    fn skip_comments_and_whitespace(&mut self) {
        loop {
            self.skip_whitespace();
            if self.peek() == '#' {
                self.skip_comment();
            } else {
                break;
            }
        }
    }

    fn skip_whitespace(&mut self) {
        while self.peek().is_whitespace() {
            self.advance();
        }
    }

    fn skip_comment(&mut self) {
        while self.peek() != '\n' {
            self.advance();
        }
    }

    fn empty_tok(&self, t: TokenType) -> Token {
        Token {
            typ: t,
            lexeme: None,
            pos: self.start_pos,
        }
    }

    fn lexeme_tok(&self, t: TokenType, lexeme: String) -> Token {
        Token {
            typ: t,
            lexeme: Some(lexeme),
            pos: self.start_pos,
        }
    }

    fn single_char_tok(&mut self, t: TokenType) -> Token {
        let t = self.empty_tok(t);
        self.advance();
        t
    }
}

fn is_id_start(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    (c >= 'A' && c <= 'Z')
}

fn is_id_char(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    c.is_digit(10) ||
    (c >= 'A' && c <= 'Z')
}
<commit_msg>Fix two bugs<commit_after>use error::Error;
use pos::Pos;
use token::{Token, TokenType};

use std::str::Chars;
use std::iter::{Fuse, Peekable};

pub struct Scanner<'a> {
    data: Peekable<Fuse<Chars<'a>>>,
    start_pos: Pos,
    curr_pos: Pos,
}

impl<'a> Scanner<'a> {
    // Create a new scanner from a given program represented as a
    // String.
    pub fn new<'b>(data: &'b str) -> Scanner<'b> {
        Scanner {
            data: data.chars().fuse().peekable(),
            start_pos: Pos { line: 1, col: 1 },
            curr_pos: Pos { line: 1, col: 1 },
        }
    }

    // Internal function: return the character at the current index.
    fn peek(&mut self) -> char {
        self.data.peek().map(|&c|c).unwrap_or('\x00')
    }

    // Internal function: return the character at the current index
    // and increment the index by one.
    fn advance(&mut self) -> char {
        let c = self.peek();
        if c == '\n' {
            self.curr_pos.line += 1;
            self.curr_pos.col = 1;
        } else {
            self.curr_pos.col += 1
        }
        let _ = self.data.next();
        c
    }

    // Internal function: verify if the end of the program has been
    // reached.
    fn is_eof(&mut self) -> bool {
        !self.data.peek().is_some()
    }

    pub fn next_token(&mut self) -> Result<Token, Error> {
        // Discard blanks and comments.
        self.skip_comments_and_whitespace();

        // Set start_pos (starting position of the next token) to the
        // value of curr_pos (current position in the text stream).
        self.start_pos = self.curr_pos;

        // Return Eof if the end of the file has been reached.
        if self.is_eof() {
            return Ok(self.empty_tok(TokenType::Eof));
        }

        // Scanning dispatch.
        match self.peek() {
            '+' => { Ok(self.single_char_tok(TokenType::Plus)) }
            '-' => { Ok(self.single_char_tok(TokenType::Minus)) }
            '*' => { Ok(self.single_char_tok(TokenType::Star)) }
            '/' => { Ok(self.single_char_tok(TokenType::Slash)) }
            '=' => { Ok(self.single_char_tok(TokenType::Equal)) }
            '(' => { Ok(self.single_char_tok(TokenType::LParen)) }
            ')' => { Ok(self.single_char_tok(TokenType::RParen)) }
            ':' => { Ok(self.single_char_tok(TokenType::Colon)) }
            ';' => { Ok(self.single_char_tok(TokenType::Semicolon)) }
            c if c.is_digit(10) => { self.scan_int_or_float() }
            c if is_id_start(c) => { self.scan_id_or_keyword() }
            c   => { Err(Error::IllegalCharacter(self.curr_pos, c)) }
        }
    }

    // Scan digits into an Int or Float token.
    fn scan_int_or_float(&mut self) -> Result<Token, Error> {
        let mut val = String::new();
        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        if self.peek() != '.' {
            return Ok(self.lexeme_tok(TokenType::Int, val));
        }

        val.push(self.advance()); // Add decimal point.

        while self.peek().is_digit(10) {
            val.push(self.advance());
        }

        Ok(self.lexeme_tok(TokenType::Float, val))
    }

    // Scan alpha-numeric characters into an Id or a keyword token.
    fn scan_id_or_keyword(&mut self) -> Result<Token, Error> {
        let mut lexeme = String::new();
        while is_id_char(self.peek()) {
            lexeme.push(self.advance());
        }

        let token_type = match &*lexeme {
            "if" => TokenType::If,
            "then" => TokenType::Then,
            "else" => TokenType::Else,
            "end" => TokenType::End,
            "while" => TokenType::While,
            "do" => TokenType::Do,
            "done" => TokenType::Done,
            "read" => TokenType::Read,
            "print" => TokenType::Print,
            "var" => TokenType::Var,
            "int" => TokenType::TypeInt,
            "float" => TokenType::TypeFloat,
            _ => TokenType::Id,
        };

        let token = if token_type == TokenType::Id {
            self.lexeme_tok(token_type, lexeme)
        } else {
            self.empty_tok(token_type)
        };

        Ok(token)
    }

    fn skip_comments_and_whitespace(&mut self) {
        loop {
            self.skip_whitespace();
            if self.peek() == '#' {
                self.skip_comment();
            } else {
                break;
            }
        }
    }

    fn skip_whitespace(&mut self) {
        while self.peek().is_whitespace() {
            self.advance();
        }
    }

    fn skip_comment(&mut self) {
        while self.peek() != '\n' {
            self.advance();
        }
    }

    fn empty_tok(&self, t: TokenType) -> Token {
        Token {
            typ: t,
            lexeme: None,
            pos: self.start_pos,
        }
    }

    fn lexeme_tok(&self, t: TokenType, lexeme: String) -> Token {
        Token {
            typ: t,
            lexeme: Some(lexeme),
            pos: self.start_pos,
        }
    }

    fn single_char_tok(&mut self, t: TokenType) -> Token {
        let t = self.empty_tok(t);
        self.advance();
        t
    }
}

fn is_id_start(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    (c >= 'A' && c <= 'Z')
}

fn is_id_char(c: char) -> bool {
    (c >= 'a' && c <= 'z') ||
    c == '_' ||
    c.is_digit(10) ||
    (c >= 'A' && c <= 'Z')
}
<|endoftext|>


<commit_before>pub mod exit_dialogue;
pub mod messaging;
pub mod msg_area;
pub mod tabbed;
pub mod termbox;
pub mod text_field;
pub mod widget;

use std::fs;
use std::str;

use config::Colors;
use self::tabbed::{MsgSource, TabStyle, Tabbed, TabbedRet};
pub use self::messaging::Timestamp;

use term_input::{Event, Key};
use termbox_simple::{OutputMode, Termbox};
use trie::Trie;

pub struct TUI {
    /// Termbox instance
    termbox: Termbox,

    /// Color scheme
    colors: Colors,

    /// A tab for every server + channel
    ui: Tabbed,
}

#[derive(Debug)]
pub enum TUIRet {
    Abort,
    KeyHandled,
    KeyIgnored(Key),
    EventIgnored(Event),

    /// INVARIANT: The vec will have at least one char.
    // Can't make MsgSource a ref because of this weird error:
    // https://users.rust-lang.org/t/borrow-checker-bug/5165
    Input {
        msg: Vec<char>,
        from: MsgSource,
    },
}

impl TUI {
    pub fn new(colors: Colors) -> TUI {
        let mut tui = Termbox::init().unwrap(); // TODO: check errors
        tui.set_output_mode(OutputMode::Output256);
        tui.set_clear_attributes(colors.clear.fg, colors.clear.bg);

        let _ = fs::create_dir("logs");

        TUI {
            ui: Tabbed::new(tui.width() as i32, tui.height() as i32),
            colors: colors,
            termbox: tui,
        }
    }

    pub fn set_colors(&mut self, colors: Colors) {
        self.termbox
            .set_clear_attributes(colors.clear.fg, colors.clear.bg);
        self.colors = colors;
    }
}

////////////////////////////////////////////////////////////////////////////////
// Tab stuff

impl TUI {
    pub fn new_server_tab(&mut self, serv_name: &str) {
        self.ui.new_server_tab(serv_name);
    }

    pub fn close_server_tab(&mut self, serv_name: &str) {
        self.ui.close_server_tab(serv_name);
    }

    pub fn new_chan_tab(&mut self, serv_name: &str, chan_name: &str) {
        self.ui.new_chan_tab(serv_name, chan_name);
    }

    pub fn close_chan_tab(&mut self, serv_name: &str, chan_name: &str) {
        self.ui.close_chan_tab(serv_name, chan_name);
    }

    pub fn new_user_tab(&mut self, serv_name: &str, nick: &str) {
        self.ui.new_user_tab(serv_name, nick);
    }

    pub fn close_user_tab(&mut self, serv_name: &str, nick: &str) {
        self.ui.close_user_tab(serv_name, nick);
    }

    pub fn set_tab_style(&mut self, style: TabStyle, target: &MsgTarget) {
        self.ui.set_tab_style(style, target);
    }

    pub fn set_nick(&mut self, serv_name: &str, nick: &str) {
        self.ui.set_nick(
            nick,
            &MsgTarget::AllServTabs {
                serv_name: serv_name,
            },
        );
    }

    pub fn get_nicks(&self, serv_name: &str, chan_name: &str) -> Option<&Trie> {
        self.ui.get_nicks(serv_name, chan_name)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Event handling

impl TUI {
    pub fn handle_input_event(&mut self, ev: Event) -> TUIRet {
        match ev {
            Event::Resize => {
                // This never happens, probably because the our select() loop,
                // termbox can't really get resize signals.
                self.resize();
                TUIRet::KeyHandled
            }

            Event::Key(key) =>
                match self.ui.keypressed(key) {
                    TabbedRet::KeyHandled =>
                        TUIRet::KeyHandled,
                    TabbedRet::KeyIgnored =>
                        TUIRet::KeyIgnored(key),
                    TabbedRet::Input { msg, from } =>
                        TUIRet::Input {
                            msg: msg,
                            from: from.clone(),
                        },
                    TabbedRet::Abort =>
                        TUIRet::Abort,
                },

            Event::String(str) => {
                // For some reason on my terminal newlines in text are
                // translated to carriage returns when pasting so we check for
                // both just to make sure
                if !str.contains('\n') && !str.contains('\r') {
                    // TODO this may be too slow for pasting long single lines
                    for ch in str.chars() {
                        self.handle_input_event(Event::Key(Key::Char(ch)));
                    }
                    TUIRet::KeyHandled
                } else {
                    // TODO: Paste with newlines
                    TUIRet::EventIgnored(Event::String(str))
                }
            }

            ev =>
                TUIRet::EventIgnored(ev),
        }
    }

    pub fn resize(&mut self) {
        self.termbox.resize();
        self.termbox.clear();
        let w = self.termbox.width();
        let h = self.termbox.height();
        self.ui.resize(w, h);
    }

    pub fn draw(&mut self) {
        self.termbox.clear();
        self.ui.draw(&mut self.termbox, &self.colors, 0, 0);
        self.termbox.present();
    }
}

////////////////////////////////////////////////////////////////////////////////
// Showing messages

/// Target of a message coming from an IRC server.
/// `size_of::<MsgTarget>() == 40`. Good idea to pass by reference.
pub enum MsgTarget<'a> {
    Server {
        serv_name: &'a str,
    },
    Chan {
        serv_name: &'a str,
        chan_name: &'a str,
    },
    User {
        serv_name: &'a str,
        nick: &'a str,
    },

    /// Show the message in all tabs of a server.
    AllServTabs {
        serv_name: &'a str,
    },

    /// Show the message all server tabs that have the user. (i.e. channels,
    /// privmsg tabs)
    AllUserTabs {
        serv_name: &'a str,
        nick: &'a str,
    },

    /// Show the message in currently active tab.
    CurrentTab,
}

impl TUI {
    /// An error message coming from Tiny, probably because of a command error
    /// etc. Those are not timestamped and not logged.
    pub fn add_client_err_msg(&mut self, msg: &str, target: &MsgTarget) {
        self.ui.add_client_err_msg(msg, target);
    }

    /// A message from client, usually just to indidate progress, e.g.
    /// "Connecting...". Not timestamed and not logged.
    pub fn add_client_msg(&mut self, msg: &str, target: &MsgTarget) {
        self.ui.add_client_msg(msg, target);
    }

    /// privmsg is a message coming from a server or client. Shown with sender's
    /// nick/name and receive time and logged.
    pub fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        target: &MsgTarget,
        ctcp_action: bool,
    ) {
        self.ui.add_privmsg(sender, msg, ts, target, ctcp_action);
    }

    /// Similar to `add_privmsg`, except the whole message is highlighted.
    pub fn add_privmsg_highlight(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        target: &MsgTarget,
        ctcp_action: bool,
    ) {
        self.ui
            .add_privmsg_highlight(sender, msg, ts, target, ctcp_action);
    }

    /// A message without any explicit sender info. Useful for e.g. in server
    /// and debug log tabs. Timestamped and logged.
    pub fn add_msg(&mut self, msg: &str, ts: Timestamp, target: &MsgTarget) {
        self.ui.add_msg(msg, ts, target);
    }

    /// Error messages related with the protocol - e.g. can't join a channel,
    /// nickname is in use etc. Timestamped and logged.
    pub fn add_err_msg(&mut self, msg: &str, ts: Timestamp, target: &MsgTarget) {
        self.ui.add_err_msg(msg, ts, target);
    }

    pub fn show_topic(&mut self, msg: &str, ts: Timestamp, target: &MsgTarget) {
        self.ui.show_topic(msg, ts, target);
    }

    pub fn clear_nicks(&mut self, target: &MsgTarget) {
        self.ui.clear_nicks(target);
    }

    pub fn add_nick(&mut self, nick: &str, ts: Option<Timestamp>, target: &MsgTarget) {
        self.ui.add_nick(nick, ts, target);
    }

    pub fn toggle_ignore(&mut self, target: &MsgTarget) {
        self.ui.toggle_ignore(target);
    }

    pub fn remove_nick(&mut self, nick: &str, ts: Option<Timestamp>, target: &MsgTarget) {
        self.ui.remove_nick(nick, ts, target);
    }

    pub fn rename_nick(
        &mut self,
        old_nick: &str,
        new_nick: &str,
        ts: Timestamp,
        target: &MsgTarget,
    ) {
        self.ui.rename_nick(old_nick, new_nick, ts, target);
    }

    pub fn clear(&mut self, target: &MsgTarget) {
        self.ui.clear(target);
    }

    pub fn switch(&mut self, string: &str) {
        self.ui.switch(string)
    }
}
<commit_msg>Do not create ~/logs directory (fixes #82)<commit_after>pub mod exit_dialogue;
pub mod messaging;
pub mod msg_area;
pub mod tabbed;
pub mod termbox;
pub mod text_field;
pub mod widget;

use std::str;

use config::Colors;
use self::tabbed::{MsgSource, TabStyle, Tabbed, TabbedRet};
pub use self::messaging::Timestamp;

use term_input::{Event, Key};
use termbox_simple::{OutputMode, Termbox};
use trie::Trie;

pub struct TUI {
    /// Termbox instance
    termbox: Termbox,

    /// Color scheme
    colors: Colors,

    /// A tab for every server + channel
    ui: Tabbed,
}

#[derive(Debug)]
pub enum TUIRet {
    Abort,
    KeyHandled,
    KeyIgnored(Key),
    EventIgnored(Event),

    /// INVARIANT: The vec will have at least one char.
    // Can't make MsgSource a ref because of this weird error:
    // https://users.rust-lang.org/t/borrow-checker-bug/5165
    Input {
        msg: Vec<char>,
        from: MsgSource,
    },
}

impl TUI {
    pub fn new(colors: Colors) -> TUI {
        let mut tui = Termbox::init().unwrap(); // TODO: check errors
        tui.set_output_mode(OutputMode::Output256);
        tui.set_clear_attributes(colors.clear.fg, colors.clear.bg);

        TUI {
            ui: Tabbed::new(tui.width() as i32, tui.height() as i32),
            colors: colors,
            termbox: tui,
        }
    }

    pub fn set_colors(&mut self, colors: Colors) {
        self.termbox
            .set_clear_attributes(colors.clear.fg, colors.clear.bg);
        self.colors = colors;
    }
}

////////////////////////////////////////////////////////////////////////////////
// Tab stuff

impl TUI {
    pub fn new_server_tab(&mut self, serv_name: &str) {
        self.ui.new_server_tab(serv_name);
    }

    pub fn close_server_tab(&mut self, serv_name: &str) {
        self.ui.close_server_tab(serv_name);
    }

    pub fn new_chan_tab(&mut self, serv_name: &str, chan_name: &str) {
        self.ui.new_chan_tab(serv_name, chan_name);
    }

    pub fn close_chan_tab(&mut self, serv_name: &str, chan_name: &str) {
        self.ui.close_chan_tab(serv_name, chan_name);
    }

    pub fn new_user_tab(&mut self, serv_name: &str, nick: &str) {
        self.ui.new_user_tab(serv_name, nick);
    }

    pub fn close_user_tab(&mut self, serv_name: &str, nick: &str) {
        self.ui.close_user_tab(serv_name, nick);
    }

    pub fn set_tab_style(&mut self, style: TabStyle, target: &MsgTarget) {
        self.ui.set_tab_style(style, target);
    }

    pub fn set_nick(&mut self, serv_name: &str, nick: &str) {
        self.ui.set_nick(
            nick,
            &MsgTarget::AllServTabs {
                serv_name: serv_name,
            },
        );
    }

    pub fn get_nicks(&self, serv_name: &str, chan_name: &str) -> Option<&Trie> {
        self.ui.get_nicks(serv_name, chan_name)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Event handling

impl TUI {
    pub fn handle_input_event(&mut self, ev: Event) -> TUIRet {
        match ev {
            Event::Resize => {
                // This never happens, probably because the our select() loop,
                // termbox can't really get resize signals.
                self.resize();
                TUIRet::KeyHandled
            }

            Event::Key(key) =>
                match self.ui.keypressed(key) {
                    TabbedRet::KeyHandled =>
                        TUIRet::KeyHandled,
                    TabbedRet::KeyIgnored =>
                        TUIRet::KeyIgnored(key),
                    TabbedRet::Input { msg, from } =>
                        TUIRet::Input {
                            msg: msg,
                            from: from.clone(),
                        },
                    TabbedRet::Abort =>
                        TUIRet::Abort,
                },

            Event::String(str) => {
                // For some reason on my terminal newlines in text are
                // translated to carriage returns when pasting so we check for
                // both just to make sure
                if !str.contains('\n') && !str.contains('\r') {
                    // TODO this may be too slow for pasting long single lines
                    for ch in str.chars() {
                        self.handle_input_event(Event::Key(Key::Char(ch)));
                    }
                    TUIRet::KeyHandled
                } else {
                    // TODO: Paste with newlines
                    TUIRet::EventIgnored(Event::String(str))
                }
            }

            ev =>
                TUIRet::EventIgnored(ev),
        }
    }

    pub fn resize(&mut self) {
        self.termbox.resize();
        self.termbox.clear();
        let w = self.termbox.width();
        let h = self.termbox.height();
        self.ui.resize(w, h);
    }

    pub fn draw(&mut self) {
        self.termbox.clear();
        self.ui.draw(&mut self.termbox, &self.colors, 0, 0);
        self.termbox.present();
    }
}

////////////////////////////////////////////////////////////////////////////////
// Showing messages

/// Target of a message coming from an IRC server.
/// `size_of::<MsgTarget>() == 40`. Good idea to pass by reference.
pub enum MsgTarget<'a> {
    Server {
        serv_name: &'a str,
    },
    Chan {
        serv_name: &'a str,
        chan_name: &'a str,
    },
    User {
        serv_name: &'a str,
        nick: &'a str,
    },

    /// Show the message in all tabs of a server.
    AllServTabs {
        serv_name: &'a str,
    },

    /// Show the message all server tabs that have the user. (i.e. channels,
    /// privmsg tabs)
    AllUserTabs {
        serv_name: &'a str,
        nick: &'a str,
    },

    /// Show the message in currently active tab.
    CurrentTab,
}

impl TUI {
    /// An error message coming from Tiny, probably because of a command error
    /// etc. Those are not timestamped and not logged.
    pub fn add_client_err_msg(&mut self, msg: &str, target: &MsgTarget) {
        self.ui.add_client_err_msg(msg, target);
    }

    /// A message from client, usually just to indidate progress, e.g.
    /// "Connecting...". Not timestamed and not logged.
    pub fn add_client_msg(&mut self, msg: &str, target: &MsgTarget) {
        self.ui.add_client_msg(msg, target);
    }

    /// privmsg is a message coming from a server or client. Shown with sender's
    /// nick/name and receive time and logged.
    pub fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        target: &MsgTarget,
        ctcp_action: bool,
    ) {
        self.ui.add_privmsg(sender, msg, ts, target, ctcp_action);
    }

    /// Similar to `add_privmsg`, except the whole message is highlighted.
    pub fn add_privmsg_highlight(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Timestamp,
        target: &MsgTarget,
        ctcp_action: bool,
    ) {
        self.ui
            .add_privmsg_highlight(sender, msg, ts, target, ctcp_action);
    }

    /// A message without any explicit sender info. Useful for e.g. in server
    /// and debug log tabs. Timestamped and logged.
    pub fn add_msg(&mut self, msg: &str, ts: Timestamp, target: &MsgTarget) {
        self.ui.add_msg(msg, ts, target);
    }

    /// Error messages related with the protocol - e.g. can't join a channel,
    /// nickname is in use etc. Timestamped and logged.
    pub fn add_err_msg(&mut self, msg: &str, ts: Timestamp, target: &MsgTarget) {
        self.ui.add_err_msg(msg, ts, target);
    }

    pub fn show_topic(&mut self, msg: &str, ts: Timestamp, target: &MsgTarget) {
        self.ui.show_topic(msg, ts, target);
    }

    pub fn clear_nicks(&mut self, target: &MsgTarget) {
        self.ui.clear_nicks(target);
    }

    pub fn add_nick(&mut self, nick: &str, ts: Option<Timestamp>, target: &MsgTarget) {
        self.ui.add_nick(nick, ts, target);
    }

    pub fn toggle_ignore(&mut self, target: &MsgTarget) {
        self.ui.toggle_ignore(target);
    }

    pub fn remove_nick(&mut self, nick: &str, ts: Option<Timestamp>, target: &MsgTarget) {
        self.ui.remove_nick(nick, ts, target);
    }

    pub fn rename_nick(
        &mut self,
        old_nick: &str,
        new_nick: &str,
        ts: Timestamp,
        target: &MsgTarget,
    ) {
        self.ui.rename_nick(old_nick, new_nick, ts, target);
    }

    pub fn clear(&mut self, target: &MsgTarget) {
        self.ui.clear(target);
    }

    pub fn switch(&mut self, string: &str) {
        self.ui.switch(string)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers.
// Copyright 2015 Joe Neeman.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp;
use std::usize;
use super::Searcher;

/// Searches for a substring using the "two-way" algorithm of Crochemore and Perrin, D.
///
/// This implementation is basically copied from rust's standard library.
#[derive(Clone, Debug)]
pub struct TwoWaySearcher<'a> {
    needle: &'a [u8],

    /// critical factorization index
    crit_pos: usize,
    /// critical factorization index for reversed needle
    crit_pos_back: usize,
    period: usize,
    /// `byteset` is an extension (not part of the two way algorithm);
    /// it's a 64-bit "fingerprint" where each set bit `j` corresponds
    /// to a (byte & 63) == j present in the needle.
    byteset: u64,
    /// TODO: docme
    is_long: bool,
}

/// Mutable state of the searcher.
struct TwoWayState {
    position: usize,
    /// index into needle before which we have already matched
    memory: usize,
}

impl<'a> Searcher for TwoWaySearcher<'a> {
    fn search_in(&self, haystack: &[u8]) -> Option<usize> {
        // write out `true` and `false` cases to encourage the compiler
        // to specialize the two cases separately.
        if self.is_long {
            let state = TwoWayState {
                position: 0,
                memory: usize::MAX,
            };

            self.next(haystack, state, true)
        } else {
            let state = TwoWayState {
                position: 0,
                memory: 0,
            };

            self.next(haystack, state, false)
        }
    }
}

/*
    This is the Two-Way search algorithm, which was introduced in the paper:
    Crochemore, M., Perrin, D., 1991, Two-way string-matching, Journal of the ACM 38(3):651-675.

    Here's some background information.

    A *word* is a string of symbols. The *length* of a word should be a familiar
    notion, and here we denote it for any word x by |x|.
    (We also allow for the possibility of the *empty word*, a word of length zero).

    If x is any non-empty word, then an integer p with 0 < p <= |x| is said to be a
    *period* for x iff for all i with 0 <= i <= |x| - p - 1, we have x[i] == x[i+p].
    For example, both 1 and 2 are periods for the string "aa". As another example,
    the only period of the string "abcd" is 4.

    We denote by period(x) the *smallest* period of x (provided that x is non-empty).
    This is always well-defined since every non-empty word x has at least one period,
    |x|. We sometimes call this *the period* of x.

    If u, v and x are words such that x = uv, where uv is the concatenation of u and
    v, then we say that (u, v) is a *factorization* of x.

    Let (u, v) be a factorization for a word x. Then if w is a non-empty word such
    that both of the following hold

      - either w is a suffix of u or u is a suffix of w
      - either w is a prefix of v or v is a prefix of w

    then w is said to be a *repetition* for the factorization (u, v).

    Just to unpack this, there are four possibilities here. Let w = "abc". Then we
    might have:

      - w is a suffix of u and w is a prefix of v. ex: ("lolabc", "abcde")
      - w is a suffix of u and v is a prefix of w. ex: ("lolabc", "ab")
      - u is a suffix of w and w is a prefix of v. ex: ("bc", "abchi")
      - u is a suffix of w and v is a prefix of w. ex: ("bc", "a")

    Note that the word vu is a repetition for any factorization (u,v) of x = uv,
    so every factorization has at least one repetition.

    If x is a string and (u, v) is a factorization for x, then a *local period* for
    (u, v) is an integer r such that there is some word w such that |w| = r and w is
    a repetition for (u, v).

    We denote by local_period(u, v) the smallest local period of (u, v). We sometimes
    call this *the local period* of (u, v). Provided that x = uv is non-empty, this
    is well-defined (because each non-empty word has at least one factorization, as
    noted above).

    It can be proven that the following is an equivalent definition of a local period
    for a factorization (u, v): any positive integer r such that x[i] == x[i+r] for
    all i such that |u| - r <= i <= |u| - 1 and such that both x[i] and x[i+r] are
    defined. (i.e. i > 0 and i + r < |x|).

    Using the above reformulation, it is easy to prove that

        1 <= local_period(u, v) <= period(uv)

    A factorization (u, v) of x such that local_period(u,v) = period(x) is called a
    *critical factorization*.

    The algorithm hinges on the following theorem, which is stated without proof:

    **Critical Factorization Theorem** Any word x has at least one critical
    factorization (u, v) such that |u| < period(x).

    The purpose of maximal_suffix is to find such a critical factorization.

    If the period is short, compute another factorization x = u' v' to use
    for reverse search, chosen instead so that |v'| < period(x).

*/
impl<'a> TwoWaySearcher<'a> {
    /// Creates a new `TwoWaySearcher` that can be used to search for `needle`.
    pub fn new<'b>(needle: &'b [u8]) -> TwoWaySearcher<'b> {
        let (crit_pos_false, period_false) = TwoWaySearcher::maximal_suffix(needle, false);
        let (crit_pos_true, period_true) = TwoWaySearcher::maximal_suffix(needle, true);

        let (crit_pos, period) =
            if crit_pos_false > crit_pos_true {
                (crit_pos_false, period_false)
            } else {
                (crit_pos_true, period_true)
            };

        // A particularly readable explanation of what's going on here can be found
        // in Crochemore and Rytter's book "Text Algorithms", ch 13. Specifically
        // see the code for "Algorithm CP" on p. 323.
        //
        // What's going on is we have some critical factorization (u, v) of the
        // needle, and we want to determine whether u is a suffix of
        // &v[..period]. If it is, we use "Algorithm CP1". Otherwise we use
        // "Algorithm CP2", which is optimized for when the period of the needle
        // is large.
        if &needle[..crit_pos] == &needle[period.. period + crit_pos] {
            // short period case -- the period is exact
            // compute a separate critical factorization for the reversed needle
            // x = u' v' where |v'| < period(x).
            //
            // This is sped up by the period being known already.
            // Note that a case like x = "acba" may be factored exactly forwards
            // (crit_pos = 1, period = 3) while being factored with approximate
            // period in reverse (crit_pos = 2, period = 2). We use the given
            // reverse factorization but keep the exact period.
            let crit_pos_back = needle.len() - cmp::max(
                TwoWaySearcher::reverse_maximal_suffix(needle, period, false),
                TwoWaySearcher::reverse_maximal_suffix(needle, period, true));

            TwoWaySearcher {
                needle: needle,
                crit_pos: crit_pos,
                crit_pos_back: crit_pos_back,
                period: period,
                byteset: Self::byteset_create(&needle[..period]),
                is_long: false,
            }
        } else {
            // long period case -- we have an approximation to the actual period,
            // and don't use memorization.
            //
            // Approximate the period by lower bound max(|u|, |v|) + 1.
            // The critical factorization is efficient to use for both forward and
            // reverse search.

            TwoWaySearcher {
                needle: needle,
                crit_pos: crit_pos,
                crit_pos_back: crit_pos,
                period: cmp::max(crit_pos, needle.len() - crit_pos) + 1,
                byteset: Self::byteset_create(needle),
                is_long: true,
            }
        }
    }

    #[inline]
    fn byteset_create(bytes: &[u8]) -> u64 {
        bytes.iter().fold(0, |a, &b| (1 << (b & 0x3f)) | a)
    }

    #[inline(always)]
    fn byteset_contains(&self, byte: u8) -> bool {
        (self.byteset >> ((byte & 0x3f) as usize)) & 1 != 0
    }

    // One of the main ideas of Two-Way is that we factorize the needle into
    // two halves, (u, v), and begin trying to find v in the haystack by scanning
    // left to right. If v matches, we try to match u by scanning right to left.
    // How far we can jump when we encounter a mismatch is all based on the fact
    // that (u, v) is a critical factorization for the needle.
    #[inline(always)]
    fn next(&self, haystack: &[u8], mut state: TwoWayState, long_period: bool) -> Option<usize> {
        let needle_last = self.needle.len() - 1;
        'search: loop {
            // Check that we have room to search in
            // position + needle_last can not overflow if we assume slices
            // are bounded by isize's range.
            let tail_byte = match haystack.get(state.position + needle_last) {
                Some(&b) => b,
                None => { return None; }
            };

            // Quickly skip by large portions unrelated to our substring
            if !self.byteset_contains(tail_byte) {
                state.position += self.needle.len();
                if !long_period {
                    state.memory = 0;
                }
                continue 'search;
            }

            // See if the right part of the needle matches
            let start = if long_period { self.crit_pos }
                        else { cmp::max(self.crit_pos, state.memory) };
            for i in start..self.needle.len() {
                if self.needle[i] != haystack[state.position + i] {
                    state.position += i - self.crit_pos + 1;
                    if !long_period {
                        state.memory = 0;
                    }
                    continue 'search;
                }
            }

            // See if the left part of the needle matches
            let start = if long_period { 0 } else { state.memory };
            for i in (start..self.crit_pos).rev() {
                if self.needle[i] != haystack[state.position + i] {
                    state.position += self.period;
                    if !long_period {
                        state.memory = self.needle.len() - self.period;
                    }
                    continue 'search;
                }
            }

            // We have found a match!
            let match_pos = state.position;

            state.position += self.needle.len();
            if !long_period {
                state.memory = 0; // set to needle.len() - self.period for overlapping matches
            }

            return Some(match_pos);
        }
    }

    // Compute the maximal suffix of `arr`.
    //
    // The maximal suffix is a possible critical factorization (u, v) of `arr`.
    //
    // Returns (`i`, `p`) where `i` is the starting index of v and `p` is the
    // period of v.
    //
    // `order_greater` determines if lexical order is `<` or `>`. Both
    // orders must be computed -- the ordering with the largest `i` gives
    // a critical factorization.
    //
    // For long period cases, the resulting period is not exact (it is too short).
    #[inline]
    fn maximal_suffix(arr: &[u8], order_greater: bool) -> (usize, usize) {
        let mut left = 0; // Corresponds to i in the paper
        let mut right = 1; // Corresponds to j in the paper
        let mut offset = 0; // Corresponds to k in the paper, but starting at 0
                            // to match 0-based indexing.
        let mut period = 1; // Corresponds to p in the paper

        while let Some(&a) = arr.get(right + offset) {
            // `left` will be inbounds when `right` is.
            let b = arr[left + offset];
            if (a < b && !order_greater) || (a > b && order_greater) {
                // Suffix is smaller, period is entire prefix so far.
                right += offset + 1;
                offset = 0;
                period = right - left;
            } else if a == b {
                // Advance through repetition of the current period.
                if offset + 1 == period {
                    right += offset + 1;
                    offset = 0;
                } else {
                    offset += 1;
                }
            } else {
                // Suffix is larger, start over from current location.
                left = right;
                right += 1;
                offset = 0;
                period = 1;
            }
        }
        (left, period)
    }

    // Compute the maximal suffix of the reverse of `arr`.
    //
    // The maximal suffix is a possible critical factorization (u', v') of `arr`.
    //
    // Returns `i` where `i` is the starting index of v', from the back;
    // returns immedately when a period of `known_period` is reached.
    //
    // `order_greater` determines if lexical order is `<` or `>`. Both
    // orders must be computed -- the ordering with the largest `i` gives
    // a critical factorization.
    //
    // For long period cases, the resulting period is not exact (it is too short).
    fn reverse_maximal_suffix(arr: &[u8], known_period: usize,
                              order_greater: bool) -> usize
    {
        let mut left = 0; // Corresponds to i in the paper
        let mut right = 1; // Corresponds to j in the paper
        let mut offset = 0; // Corresponds to k in the paper, but starting at 0
                            // to match 0-based indexing.
        let mut period = 1; // Corresponds to p in the paper
        let n = arr.len();

        while right + offset < n {
            let a = arr[n - (1 + right + offset)];
            let b = arr[n - (1 + left + offset)];
            if (a < b && !order_greater) || (a > b && order_greater) {
                // Suffix is smaller, period is entire prefix so far.
                right += offset + 1;
                offset = 0;
                period = right - left;
            } else if a == b {
                // Advance through repetition of the current period.
                if offset + 1 == period {
                    right += offset + 1;
                    offset = 0;
                } else {
                    offset += 1;
                }
            } else {
                // Suffix is larger, start over from current location.
                left = right;
                right += 1;
                offset = 0;
                period = 1;
            }
            if period == known_period {
                break;
            }
        }
        debug_assert!(period <= known_period);
        left
    }
}

<commit_msg>Get rid of some more backward-search code.<commit_after>// Copyright 2015 The Rust Project Developers.
// Copyright 2015 Joe Neeman.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::cmp;
use std::usize;
use super::Searcher;

/// Searches for a substring using the "two-way" algorithm of Crochemore and Perrin, D.
///
/// This implementation is basically copied from rust's standard library.
#[derive(Clone, Debug)]
pub struct TwoWaySearcher<'a> {
    needle: &'a [u8],

    /// critical factorization index
    crit_pos: usize,
    period: usize,
    /// `byteset` is an extension (not part of the two way algorithm);
    /// it's a 64-bit "fingerprint" where each set bit `j` corresponds
    /// to a (byte & 63) == j present in the needle.
    byteset: u64,
    /// TODO: docme
    is_long: bool,
}

/// Mutable state of the searcher.
struct TwoWayState {
    position: usize,
    /// index into needle before which we have already matched
    memory: usize,
}

impl<'a> Searcher for TwoWaySearcher<'a> {
    fn search_in(&self, haystack: &[u8]) -> Option<usize> {
        // write out `true` and `false` cases to encourage the compiler
        // to specialize the two cases separately.
        if self.is_long {
            let state = TwoWayState {
                position: 0,
                memory: usize::MAX,
            };

            self.next(haystack, state, true)
        } else {
            let state = TwoWayState {
                position: 0,
                memory: 0,
            };

            self.next(haystack, state, false)
        }
    }
}

/*
    This is the Two-Way search algorithm, which was introduced in the paper:
    Crochemore, M., Perrin, D., 1991, Two-way string-matching, Journal of the ACM 38(3):651-675.

    Here's some background information.

    A *word* is a string of symbols. The *length* of a word should be a familiar
    notion, and here we denote it for any word x by |x|.
    (We also allow for the possibility of the *empty word*, a word of length zero).

    If x is any non-empty word, then an integer p with 0 < p <= |x| is said to be a
    *period* for x iff for all i with 0 <= i <= |x| - p - 1, we have x[i] == x[i+p].
    For example, both 1 and 2 are periods for the string "aa". As another example,
    the only period of the string "abcd" is 4.

    We denote by period(x) the *smallest* period of x (provided that x is non-empty).
    This is always well-defined since every non-empty word x has at least one period,
    |x|. We sometimes call this *the period* of x.

    If u, v and x are words such that x = uv, where uv is the concatenation of u and
    v, then we say that (u, v) is a *factorization* of x.

    Let (u, v) be a factorization for a word x. Then if w is a non-empty word such
    that both of the following hold

      - either w is a suffix of u or u is a suffix of w
      - either w is a prefix of v or v is a prefix of w

    then w is said to be a *repetition* for the factorization (u, v).

    Just to unpack this, there are four possibilities here. Let w = "abc". Then we
    might have:

      - w is a suffix of u and w is a prefix of v. ex: ("lolabc", "abcde")
      - w is a suffix of u and v is a prefix of w. ex: ("lolabc", "ab")
      - u is a suffix of w and w is a prefix of v. ex: ("bc", "abchi")
      - u is a suffix of w and v is a prefix of w. ex: ("bc", "a")

    Note that the word vu is a repetition for any factorization (u,v) of x = uv,
    so every factorization has at least one repetition.

    If x is a string and (u, v) is a factorization for x, then a *local period* for
    (u, v) is an integer r such that there is some word w such that |w| = r and w is
    a repetition for (u, v).

    We denote by local_period(u, v) the smallest local period of (u, v). We sometimes
    call this *the local period* of (u, v). Provided that x = uv is non-empty, this
    is well-defined (because each non-empty word has at least one factorization, as
    noted above).

    It can be proven that the following is an equivalent definition of a local period
    for a factorization (u, v): any positive integer r such that x[i] == x[i+r] for
    all i such that |u| - r <= i <= |u| - 1 and such that both x[i] and x[i+r] are
    defined. (i.e. i > 0 and i + r < |x|).

    Using the above reformulation, it is easy to prove that

        1 <= local_period(u, v) <= period(uv)

    A factorization (u, v) of x such that local_period(u,v) = period(x) is called a
    *critical factorization*.

    The algorithm hinges on the following theorem, which is stated without proof:

    **Critical Factorization Theorem** Any word x has at least one critical
    factorization (u, v) such that |u| < period(x).

    The purpose of maximal_suffix is to find such a critical factorization.

    If the period is short, compute another factorization x = u' v' to use
    for reverse search, chosen instead so that |v'| < period(x).

*/
impl<'a> TwoWaySearcher<'a> {
    /// Creates a new `TwoWaySearcher` that can be used to search for `needle`.
    pub fn new<'b>(needle: &'b [u8]) -> TwoWaySearcher<'b> {
        let (crit_pos_false, period_false) = TwoWaySearcher::maximal_suffix(needle, false);
        let (crit_pos_true, period_true) = TwoWaySearcher::maximal_suffix(needle, true);

        let (crit_pos, period) =
            if crit_pos_false > crit_pos_true {
                (crit_pos_false, period_false)
            } else {
                (crit_pos_true, period_true)
            };

        // A particularly readable explanation of what's going on here can be found
        // in Crochemore and Rytter's book "Text Algorithms", ch 13. Specifically
        // see the code for "Algorithm CP" on p. 323.
        //
        // What's going on is we have some critical factorization (u, v) of the
        // needle, and we want to determine whether u is a suffix of
        // &v[..period]. If it is, we use "Algorithm CP1". Otherwise we use
        // "Algorithm CP2", which is optimized for when the period of the needle
        // is large.
        if &needle[..crit_pos] == &needle[period.. period + crit_pos] {
            // short period case -- the period is exact
            // compute a separate critical factorization for the reversed needle
            // x = u' v' where |v'| < period(x).
            //
            // This is sped up by the period being known already.

            TwoWaySearcher {
                needle: needle,
                crit_pos: crit_pos,
                period: period,
                byteset: Self::byteset_create(&needle[..period]),
                is_long: false,
            }
        } else {
            // long period case -- we have an approximation to the actual period,
            // and don't use memorization.
            //
            // Approximate the period by lower bound max(|u|, |v|) + 1.

            TwoWaySearcher {
                needle: needle,
                crit_pos: crit_pos,
                period: cmp::max(crit_pos, needle.len() - crit_pos) + 1,
                byteset: Self::byteset_create(needle),
                is_long: true,
            }
        }
    }

    #[inline]
    fn byteset_create(bytes: &[u8]) -> u64 {
        bytes.iter().fold(0, |a, &b| (1 << (b & 0x3f)) | a)
    }

    #[inline(always)]
    fn byteset_contains(&self, byte: u8) -> bool {
        (self.byteset >> ((byte & 0x3f) as usize)) & 1 != 0
    }

    // One of the main ideas of Two-Way is that we factorize the needle into
    // two halves, (u, v), and begin trying to find v in the haystack by scanning
    // left to right. If v matches, we try to match u by scanning right to left.
    // How far we can jump when we encounter a mismatch is all based on the fact
    // that (u, v) is a critical factorization for the needle.
    #[inline(always)]
    fn next(&self, haystack: &[u8], mut state: TwoWayState, long_period: bool) -> Option<usize> {
        let needle_last = self.needle.len() - 1;
        'search: loop {
            // Check that we have room to search in
            // position + needle_last can not overflow if we assume slices
            // are bounded by isize's range.
            let tail_byte = match haystack.get(state.position + needle_last) {
                Some(&b) => b,
                None => { return None; }
            };

            // Quickly skip by large portions unrelated to our substring
            if !self.byteset_contains(tail_byte) {
                state.position += self.needle.len();
                if !long_period {
                    state.memory = 0;
                }
                continue 'search;
            }

            // See if the right part of the needle matches
            let start = if long_period { self.crit_pos }
                        else { cmp::max(self.crit_pos, state.memory) };
            for i in start..self.needle.len() {
                if self.needle[i] != haystack[state.position + i] {
                    state.position += i - self.crit_pos + 1;
                    if !long_period {
                        state.memory = 0;
                    }
                    continue 'search;
                }
            }

            // See if the left part of the needle matches
            let start = if long_period { 0 } else { state.memory };
            for i in (start..self.crit_pos).rev() {
                if self.needle[i] != haystack[state.position + i] {
                    state.position += self.period;
                    if !long_period {
                        state.memory = self.needle.len() - self.period;
                    }
                    continue 'search;
                }
            }

            // We have found a match!
            let match_pos = state.position;

            state.position += self.needle.len();
            if !long_period {
                state.memory = 0; // set to needle.len() - self.period for overlapping matches
            }

            return Some(match_pos);
        }
    }

    // Compute the maximal suffix of `arr`.
    //
    // The maximal suffix is a possible critical factorization (u, v) of `arr`.
    //
    // Returns (`i`, `p`) where `i` is the starting index of v and `p` is the
    // period of v.
    //
    // `order_greater` determines if lexical order is `<` or `>`. Both
    // orders must be computed -- the ordering with the largest `i` gives
    // a critical factorization.
    //
    // For long period cases, the resulting period is not exact (it is too short).
    #[inline]
    fn maximal_suffix(arr: &[u8], order_greater: bool) -> (usize, usize) {
        let mut left = 0; // Corresponds to i in the paper
        let mut right = 1; // Corresponds to j in the paper
        let mut offset = 0; // Corresponds to k in the paper, but starting at 0
                            // to match 0-based indexing.
        let mut period = 1; // Corresponds to p in the paper

        while let Some(&a) = arr.get(right + offset) {
            // `left` will be inbounds when `right` is.
            let b = arr[left + offset];
            if (a < b && !order_greater) || (a > b && order_greater) {
                // Suffix is smaller, period is entire prefix so far.
                right += offset + 1;
                offset = 0;
                period = right - left;
            } else if a == b {
                // Advance through repetition of the current period.
                if offset + 1 == period {
                    right += offset + 1;
                    offset = 0;
                } else {
                    offset += 1;
                }
            } else {
                // Suffix is larger, start over from current location.
                left = right;
                right += 1;
                offset = 0;
                period = 1;
            }
        }
        (left, period)
    }
}

<|endoftext|>