<commit_before>//! Defines the `MoveGenerator` trait.

use std::mem::uninitialized;
use std::cmp::max;
use uci::SetOption;
use board::*;
use moves::*;
use value::*;
use evaluator::Evaluator;
use bitsets::*;
use utils::BoardGeometry;

/// A trait for move generators.
///
/// A `MoveGenerator` holds a chess position and can:
///
/// * Generate all legal moves, or a subset of all legal moves in the
///   current position.
///
/// * Perform static exchange evaluation for the generated moves.
///
/// * Play a selected move and take it back.
///
/// * Provide a static evaluator bound to the current position.
///
/// * Calculate Zobrist hashes.
///
/// **Important note:** `MoveGenerator` is unaware of repeating
/// positions and rule-50.
pub trait MoveGenerator: Sized + Send + Clone + SetOption {
    /// The type of static evaluator that the implementation works
    /// with.
    type Evaluator: Evaluator;

    /// Creates a new instance, consuming the supplied `Board`
    /// instance.
    ///
    /// Returns `Err(IllegalBoard)` if the position is illegal.
    fn from_board(board: Board) -> Result<Self, IllegalBoard>;

    /// Returns the Zobrist hash value for the underlying `Board`
    /// instance.
    ///
    /// Zobrist hashing is a technique to transform a board position
    /// into a number of a fixed length, with an equal distribution
    /// over all possible numbers, invented by Albert Zobrist. The key
    /// property of this method is that two similar positions generate
    /// entirely different hash numbers.
    ///
    /// **Important note:** This method will be relatively slow if the
    /// implementation calculates the hash value "from
    /// scratch". Inspect the implementation before using `hash` in
    /// time-critical paths. (See `do_move`.)
    fn hash(&self) -> u64;

    /// Returns a reference to the underlying `Board` instance.
    fn board(&self) -> &Board;

    /// Returns a bitboard with all pieces of color `us` that attack
    /// `square`.
    fn attacks_to(&self, us: Color, square: Square) -> Bitboard;

    /// Returns a bitboard with all enemy pieces that attack the king.
    ///
    /// **Important note:** The bitboard of checkers is calculated on
    /// the first call to `checkers`, and is stored in case another
    /// call is made before doing/undoing any moves. In that case
    /// `checkers` returns the saved bitboard instead of
    /// re-calculating it, thus saving time.
    fn checkers(&self) -> Bitboard;

    /// Returns a reference to a static evaluator bound to the current
    /// position.
    fn evaluator(&self) -> &Self::Evaluator;

    /// Generates all legal moves, possibly including some
    /// pseudo-legal moves too.
    ///
    /// The moves are added to `moves`. All generated moves with
    /// pieces other than the king will be legal. Some of the
    /// generated king's moves may be illegal because the destination
    /// square is under attack. This arrangement has two important
    /// advantages:
    ///
    /// * `do_move` can do its work without knowing the set of
    ///   checkers and pinned pieces, so there is no need to keep
    ///   those around.
    ///
    /// * A beta cut-off may make the verification that king's
    ///   destination square is not under attack unnecessary, thus
    ///   saving time.
    ///
    /// **Note:** A pseudo-legal move is a move that is otherwise
    /// legal, except it might leave the king in check.
    fn generate_all<T: AddMove>(&self, moves: &mut T);

    /// Generates moves for the quiescence search.
    ///
    /// The moves are added to `moves`. This method always generates a
    /// **subset** of the moves generated by `generate_all`:
    ///
    /// * If the king is in check, all legal moves are included.
    ///
    /// * Captures and pawn promotions to queen are always included.
    ///
    /// * If `generate_checks` is `true`, moves that give check are
    ///   included too. Discovered checks and checks given by castling
    ///   can be omitted for speed.
    fn generate_forcing<T: AddMove>(&self, generate_checks: bool, moves: &mut T);

    /// Checks if `move_digest` represents a pseudo-legal move.
    ///
    /// If a move `m` exists that would be generated by
    /// `generate_all` if called for the current position on the
    /// board, and for that move `m.digest() == move_digest`, this
    /// method will return `Some(m)`. Otherwise it will return
    /// `None`. This is useful when playing moves from the
    /// transposition table, without calling `generate_all`.
    fn try_move_digest(&self, move_digest: MoveDigest) -> Option<Move>;

    /// Returns a null move.
    ///
    /// "Null move" is a pseudo-move that changes only the side to
    /// move. It is sometimes useful to include a speculative null
    /// move in the search tree so as to achieve more aggressive
    /// pruning. Null moves are represented as king's moves for which
    /// the origin and destination squares are the same.
    fn null_move(&self) -> Move;

    /// Plays a move on the board.
    ///
    /// It verifies if the move is legal. If the move is legal, the
    /// board is updated and an `u64` value is returned, which should
    /// be XOR-ed with old board's hash value to obtain new board's
    /// hash value. If the move is illegal, `None` is returned without
    /// updating the board. The move passed to this method **must**
    /// have been generated by `generate_all`, `generate_forcing`,
    /// `try_move_digest`, or `null_move` methods for the current
    /// position on the board.
    ///
    /// The moves generated by the `null_move` method are
    /// exceptions. For them `do_move` will return `None` if and only
    /// if the king is in check.
    fn do_move(&mut self, m: Move) -> Option<u64>;

    /// Takes back last played move.
    ///
    /// The move passed to this method **must** be the last move passed
    /// to `do_move`.
    fn undo_move(&mut self, m: Move);

    /// Returns the likely evaluation change (material) to be lost or
    /// gained as a result of a given move.
    ///
    /// This method performs static exchange evaluation (SEE). It
    /// examines the consequence of a series of exchanges on the
    /// destination square after a given move. A positive returned
    /// value indicates a "winning" move. For example, "PxQ" will
    /// always be a win, since the pawn side can choose to stop the
    /// exchange after its pawn is recaptured, and still be ahead. SEE
    /// is just an evaluation calculated without actually trying moves
    /// on the board, and therefore the returned value might be
    /// incorrect.
    ///
    /// The move passed to this method must have been generated by
    /// `generate_all`, `generate_forcing`, `try_move_digest`, or
    /// `null_move` methods for the current position on the board.
    fn evaluate_move(&self, m: Move) -> Value {
        debug_assert!(m.played_piece() < PIECE_NONE);
        debug_assert!(m.captured_piece() <= PIECE_NONE);
        const PIECE_VALUES: [Value; 8] = [10000, 975, 500, 325, 325, 100, 0, 0];

        // This is the square on which all the action takes place.
        let exchange_square = m.dest_square();

        unsafe {
            let geometry = BoardGeometry::get();
            let behind_blocker: &[Bitboard; 64] = geometry.squares_behind_blocker
                                                          .get_unchecked(exchange_square);
            let piece_type: &[Bitboard; 6] = &self.board().pieces.piece_type;
            let color: &[Bitboard; 2] = &self.board().pieces.color;
            let straight_sliders = piece_type[QUEEN] | piece_type[ROOK];
            let diag_sliders = piece_type[QUEEN] | piece_type[BISHOP];

            // These variables will be updated on each capture:
            let mut us = self.board().to_move;
            let mut depth = 0;
            let mut piece;
            let mut orig_square_bb = 1 << m.orig_square();
            let mut attackers_and_defenders = self.attacks_to(WHITE, exchange_square) |
                                              self.attacks_to(BLACK, exchange_square);

            // The `gain` array will hold the total material gained at
            // each `depth`, from the viewpoint of the side that made the
            // last capture (`us`).
            let mut gain: [Value; 34] = uninitialized();
            gain[0] = if m.move_type() == MOVE_PROMOTION {
                piece = Move::piece_from_aux_data(m.aux_data());
                PIECE_VALUES[m.captured_piece()] + PIECE_VALUES[piece] - PIECE_VALUES[PAWN]
            } else {
                piece = m.played_piece();
                *PIECE_VALUES.get_unchecked(m.captured_piece())
            };

            // Examine the possible exchanges, fill the `gain` array.
            'exchange: while orig_square_bb != 0 {
                let current_gain = *gain.get_unchecked(depth);

                // Store a speculative value that will be used if the
                // captured piece happens to be defended.
                let speculative_gain: &mut Value = gain.get_unchecked_mut(depth + 1);
                *speculative_gain = *PIECE_VALUES.get_unchecked(piece) - current_gain;

                if max(-current_gain, *speculative_gain) < 0 {
                    // The side that made the last capture wins even if
                    // the captured piece happens to be defended. So, we
                    // stop here to save precious CPU cycles. Note that
                    // here we may happen to return an incorrect SEE
                    // value, but the sign will be correct, which is by
                    // far the most important information.
                    break;
                }

                // Register that capturing piece's origin square is now vacant.
                attackers_and_defenders &= !orig_square_bb;

                // Consider adding new attackers/defenders, now that
                // capturing piece's origin square is vacant.
                let behind = self.board().occupied &
                             *behind_blocker.get_unchecked(bsf(orig_square_bb));
                if behind & (straight_sliders | diag_sliders) != 0 && piece != KING {
                    attackers_and_defenders |=
                        match behind & straight_sliders &
                              geometry.attacks_from_unsafe(ROOK, exchange_square, behind) {
                            0 => {
                                // Not a straight slider, possibly a diagonal slider.
                                behind & diag_sliders &
                                geometry.attacks_from_unsafe(BISHOP, exchange_square, behind)
                            }
                            bb => {
                                // A straight slider.
                                bb
                            }
                        };
                }

                // Change the side to move.
                us ^= 1;

                // Find the next piece to enter the exchange. (The least
                // valuable piece belonging to the side to move.)
                let candidates = attackers_and_defenders & *color.get_unchecked(us);
                if candidates != 0 {
                    for p in (KING..PIECE_NONE).rev() {
                        let bb = candidates & piece_type[p];
                        if bb != 0 {
                            depth += 1;
                            piece = p;
                            orig_square_bb = lsb(bb);
                            continue 'exchange;
                        }
                    }
                }
                break 'exchange;
            }

            // Negamax the `gain` array for the final static exchange
            // evaluation. (The `gain` array actually represents an unary
            // tree, at each node of which the player can either continue
            // the exchange or back off.)
            while depth > 0 {
                *gain.get_unchecked_mut(depth - 1) = -max(-*gain.get_unchecked(depth - 1),
                                                          *gain.get_unchecked(depth));
                depth -= 1;
            }
            gain[0]
        }
    }
}
<commit_msg>clarify docs on initial move scores<commit_after>//! Defines the `MoveGenerator` trait.

use std::mem::uninitialized;
use std::cmp::max;
use uci::SetOption;
use board::*;
use moves::*;
use value::*;
use evaluator::Evaluator;
use bitsets::*;
use utils::BoardGeometry;

/// A trait for move generators.
///
/// A `MoveGenerator` holds a chess position and can:
///
/// * Generate all legal moves, or a subset of all legal moves in the
///   current position.
///
/// * Perform static exchange evaluation for the generated moves.
///
/// * Play a selected move and take it back.
///
/// * Provide a static evaluator bound to the current position.
///
/// * Calculate Zobrist hashes.
///
/// **Important note:** `MoveGenerator` is unaware of repeating
/// positions and rule-50.
pub trait MoveGenerator: Sized + Send + Clone + SetOption {
    /// The type of static evaluator that the implementation works
    /// with.
    type Evaluator: Evaluator;

    /// Creates a new instance, consuming the supplied `Board`
    /// instance.
    ///
    /// Returns `Err(IllegalBoard)` if the position is illegal.
    fn from_board(board: Board) -> Result<Self, IllegalBoard>;

    /// Returns the Zobrist hash value for the underlying `Board`
    /// instance.
    ///
    /// Zobrist hashing is a technique to transform a board position
    /// into a number of a fixed length, with an equal distribution
    /// over all possible numbers, invented by Albert Zobrist. The key
    /// property of this method is that two similar positions generate
    /// entirely different hash numbers.
    ///
    /// **Important note:** This method will be relatively slow if the
    /// implementation calculates the hash value "from
    /// scratch". Inspect the implementation before using `hash` in
    /// time-critical paths. (See `do_move`.)
    fn hash(&self) -> u64;

    /// Returns a reference to the underlying `Board` instance.
    fn board(&self) -> &Board;

    /// Returns a bitboard with all pieces of color `us` that attack
    /// `square`.
    fn attacks_to(&self, us: Color, square: Square) -> Bitboard;

    /// Returns a bitboard with all enemy pieces that attack the king.
    ///
    /// **Important note:** The bitboard of checkers is calculated on
    /// the first call to `checkers`, and is stored in case another
    /// call is made before doing/undoing any moves. In that case
    /// `checkers` returns the saved bitboard instead of
    /// re-calculating it, thus saving time.
    fn checkers(&self) -> Bitboard;

    /// Returns a reference to a static evaluator bound to the current
    /// position.
    fn evaluator(&self) -> &Self::Evaluator;

    /// Generates all legal moves, possibly including some
    /// pseudo-legal moves too.
    ///
    /// The moves are added to `moves`. All generated moves with
    /// pieces other than the king will be legal. Some of the
    /// generated king's moves may be illegal because the destination
    /// square is under attack. This arrangement has two important
    /// advantages:
    ///
    /// * `do_move` can do its work without knowing the set of
    ///   checkers and pinned pieces, so there is no need to keep
    ///   those around.
    ///
    /// * A beta cut-off may make the verification that king's
    ///   destination square is not under attack unnecessary, thus
    ///   saving time.
    ///
    /// The initial move score for the generated moves is
    /// *unspecified*.
    ///
    /// **Note:** A pseudo-legal move is a move that is otherwise
    /// legal, except it might leave the king in check.
    fn generate_all<T: AddMove>(&self, moves: &mut T);

    /// Generates moves for the quiescence search.
    ///
    /// The moves are added to `moves`. This method always generates a
    /// **subset** of the moves generated by `generate_all`:
    ///
    /// * If the king is in check, all legal moves are included.
    ///
    /// * Captures and pawn promotions to queen are always included.
    ///
    /// * If `generate_checks` is `true`, moves that give check are
    ///   included too. Discovered checks and checks given by castling
    ///   can be omitted for speed.
    fn generate_forcing<T: AddMove>(&self, generate_checks: bool, moves: &mut T);

    /// Checks if `move_digest` represents a pseudo-legal move.
    ///
    /// If a move `m` exists that would be generated by
    /// `generate_all` if called for the current position on the
    /// board, and for that move `m.digest() == move_digest`, this
    /// method will return `Some(m)`. Otherwise it will return
    /// `None`. This is useful when playing moves from the
    /// transposition table, without calling `generate_all`.
    fn try_move_digest(&self, move_digest: MoveDigest) -> Option<Move>;

    /// Returns a null move.
    ///
    /// "Null move" is a pseudo-move that changes only the side to
    /// move. It is sometimes useful to include a speculative null
    /// move in the search tree so as to achieve more aggressive
    /// pruning. Null moves are represented as king's moves for which
    /// the origin and destination squares are the same.
    fn null_move(&self) -> Move;

    /// Plays a move on the board.
    ///
    /// It verifies if the move is legal. If the move is legal, the
    /// board is updated and an `u64` value is returned, which should
    /// be XOR-ed with old board's hash value to obtain new board's
    /// hash value. If the move is illegal, `None` is returned without
    /// updating the board. The move passed to this method **must**
    /// have been generated by `generate_all`, `generate_forcing`,
    /// `try_move_digest`, or `null_move` methods for the current
    /// position on the board.
    ///
    /// The moves generated by the `null_move` method are
    /// exceptions. For them `do_move` will return `None` if and only
    /// if the king is in check.
    fn do_move(&mut self, m: Move) -> Option<u64>;

    /// Takes back last played move.
    ///
    /// The move passed to this method **must** be the last move passed
    /// to `do_move`.
    fn undo_move(&mut self, m: Move);

    /// Returns the likely evaluation change (material) to be lost or
    /// gained as a result of a given move.
    ///
    /// This method performs static exchange evaluation (SEE). It
    /// examines the consequence of a series of exchanges on the
    /// destination square after a given move. A positive returned
    /// value indicates a "winning" move. For example, "PxQ" will
    /// always be a win, since the pawn side can choose to stop the
    /// exchange after its pawn is recaptured, and still be ahead. SEE
    /// is just an evaluation calculated without actually trying moves
    /// on the board, and therefore the returned value might be
    /// incorrect.
    ///
    /// The move passed to this method must have been generated by
    /// `generate_all`, `generate_forcing`, `try_move_digest`, or
    /// `null_move` methods for the current position on the board.
    fn evaluate_move(&self, m: Move) -> Value {
        debug_assert!(m.played_piece() < PIECE_NONE);
        debug_assert!(m.captured_piece() <= PIECE_NONE);
        const PIECE_VALUES: [Value; 8] = [10000, 975, 500, 325, 325, 100, 0, 0];

        // This is the square on which all the action takes place.
        let exchange_square = m.dest_square();

        unsafe {
            let geometry = BoardGeometry::get();
            let behind_blocker: &[Bitboard; 64] = geometry.squares_behind_blocker
                                                          .get_unchecked(exchange_square);
            let piece_type: &[Bitboard; 6] = &self.board().pieces.piece_type;
            let color: &[Bitboard; 2] = &self.board().pieces.color;
            let straight_sliders = piece_type[QUEEN] | piece_type[ROOK];
            let diag_sliders = piece_type[QUEEN] | piece_type[BISHOP];

            // These variables will be updated on each capture:
            let mut us = self.board().to_move;
            let mut depth = 0;
            let mut piece;
            let mut orig_square_bb = 1 << m.orig_square();
            let mut attackers_and_defenders = self.attacks_to(WHITE, exchange_square) |
                                              self.attacks_to(BLACK, exchange_square);

            // The `gain` array will hold the total material gained at
            // each `depth`, from the viewpoint of the side that made the
            // last capture (`us`).
            let mut gain: [Value; 34] = uninitialized();
            gain[0] = if m.move_type() == MOVE_PROMOTION {
                piece = Move::piece_from_aux_data(m.aux_data());
                PIECE_VALUES[m.captured_piece()] + PIECE_VALUES[piece] - PIECE_VALUES[PAWN]
            } else {
                piece = m.played_piece();
                *PIECE_VALUES.get_unchecked(m.captured_piece())
            };

            // Examine the possible exchanges, fill the `gain` array.
            'exchange: while orig_square_bb != 0 {
                let current_gain = *gain.get_unchecked(depth);

                // Store a speculative value that will be used if the
                // captured piece happens to be defended.
                let speculative_gain: &mut Value = gain.get_unchecked_mut(depth + 1);
                *speculative_gain = *PIECE_VALUES.get_unchecked(piece) - current_gain;

                if max(-current_gain, *speculative_gain) < 0 {
                    // The side that made the last capture wins even if
                    // the captured piece happens to be defended. So, we
                    // stop here to save precious CPU cycles. Note that
                    // here we may happen to return an incorrect SEE
                    // value, but the sign will be correct, which is by
                    // far the most important information.
                    break;
                }

                // Register that capturing piece's origin square is now vacant.
                attackers_and_defenders &= !orig_square_bb;

                // Consider adding new attackers/defenders, now that
                // capturing piece's origin square is vacant.
                let behind = self.board().occupied &
                             *behind_blocker.get_unchecked(bsf(orig_square_bb));
                if behind & (straight_sliders | diag_sliders) != 0 && piece != KING {
                    attackers_and_defenders |=
                        match behind & straight_sliders &
                              geometry.attacks_from_unsafe(ROOK, exchange_square, behind) {
                            0 => {
                                // Not a straight slider, possibly a diagonal slider.
                                behind & diag_sliders &
                                geometry.attacks_from_unsafe(BISHOP, exchange_square, behind)
                            }
                            bb => {
                                // A straight slider.
                                bb
                            }
                        };
                }

                // Change the side to move.
                us ^= 1;

                // Find the next piece to enter the exchange. (The least
                // valuable piece belonging to the side to move.)
                let candidates = attackers_and_defenders & *color.get_unchecked(us);
                if candidates != 0 {
                    for p in (KING..PIECE_NONE).rev() {
                        let bb = candidates & piece_type[p];
                        if bb != 0 {
                            depth += 1;
                            piece = p;
                            orig_square_bb = lsb(bb);
                            continue 'exchange;
                        }
                    }
                }
                break 'exchange;
            }

            // Negamax the `gain` array for the final static exchange
            // evaluation. (The `gain` array actually represents an unary
            // tree, at each node of which the player can either continue
            // the exchange or back off.)
            while depth > 0 {
                *gain.get_unchecked_mut(depth - 1) = -max(-*gain.get_unchecked(depth - 1),
                                                          *gain.get_unchecked(depth));
                depth -= 1;
            }
            gain[0]
        }
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use std::sync::mpsc::{channel, Sender, TryRecvError};
use std::thread;

/// Emum used to xrap messages.
///   * If the message is normal data it is in a Data variant.
///   * If the message is a Command it has its own variant.
pub enum Message {
    /// Variant used to pass real data around (done by passing a Box to it).
    Data(Box<Any + Send>),
    /// Dummy command.
    Command,
}

/// Utility type to wrap `Actor`s in a thread safe manner.
pub type ActorRef = Arc<Mutex<Actor>>;

/// Trait used for actors, implementing this trait is enough to be an Actor.
pub trait Actor: Send {
    /// Method to call on an `Actor` for him to put a message in his message queue.
    fn receive(&self, Message);
    /// Method to call on an `Actor` for him to handle a message from his message queue.
    fn handle_message(&self);
    /// Sends a `Message` to the given `ActorRef`
    fn send_message(&self, actor_ref: ActorRef, message: Message);
    /// DEV ONLY: Sends a message to the first ActorRef in known_actors.
    // Used on dev, to be removed afterwards.
    fn send_to_first(&self, message: Message);
}

/// This is a very basic `Actor` which can deal with `String` messages by printing them,
/// any other type of `Data` will have the `Printer` do nothing.
pub struct Printer {
    name: Arc<String>,
    message_queue: Arc<Mutex<VecDeque<Message>>>,
    actor_system: Arc<Weak<ActorSystem>>,
    known_actors: Arc<Mutex<Vec<ActorRef>>>,
}

impl Printer {
    fn new(name: String, actor_system: Weak<ActorSystem>, known_actors: Vec<ActorRef>) -> Printer {
        Printer {
            name: Arc::new(name),
            message_queue: Arc::new(Mutex::new(VecDeque::new())),
            actor_system: Arc::new(actor_system),
            known_actors: Arc::new(Mutex::new(known_actors)),
        }
    }
}

impl Actor for Printer {
    fn receive(&self, message: Message) {
        self.message_queue.lock().unwrap().push_back(message);
    }

    fn handle_message(&self) {
        let message = self.message_queue.lock().unwrap().pop_front().unwrap();

        println!("({}) treats a message", self.name);
        match message {
            Message::Data(ref data) => {
                match data.downcast_ref::<String>() {
                    Some(s) => println!("Received data: ({})", s),
                    None => println!("Message is dropped"),
                }
            },
            Message::Command => println!("Receiced a command"),
        }
    }

    fn send_message(&self, actor_ref: ActorRef, message: Message) {
        self.actor_system.upgrade().unwrap().send_to_actor(actor_ref, message);
    }

    fn send_to_first(&self, message: Message) {
        let actor_ref = self.known_actors.lock().unwrap()[0].clone();
        self.send_message(actor_ref, message);
    }
}

/// Wrapper around the threads handle and termination sender.
type ConsumerThread = (thread::JoinHandle<()>, Sender<()>);

/// A basic actor system, that handles the creation, distribution and handling of messages.
/// `Actor`s are simply put in a FIFO when they receive a message, they are poped from it when
/// `handle_actor_message` is called.
/// `ActorSystem::spawn_thread` allows to create simple threads that will handle `Actor`s messages.
pub struct ActorSystem {
    // TODO(gamazeps): Use an unordered container instead.
    // There is currently an issue with having an ActorRef as a Arc<Mutex<Actor + Eq + Hash>>.
    actors_table: Arc<Mutex<Vec<ActorRef>>>,
    actors_queue: Arc<Mutex<VecDeque<ActorRef>>>,
    consumer_threads: Arc<Mutex<Vec<ConsumerThread>>>,
    myself: Arc<Mutex<Option<Weak<ActorSystem>>>>,
}

unsafe impl Send for ActorSystem {}

impl ActorSystem {
    /// Creates a new `ActorSystem` and gives an `Arc` to it.
    pub fn new() -> Arc<ActorSystem> {
        let actor_system = Arc::new(ActorSystem {
            actors_table: Arc::new(Mutex::new(Vec::new())),
            actors_queue: Arc::new(Mutex::new(VecDeque::new())),
            consumer_threads: Arc::new(Mutex::new(Vec::new())),
            myself: Arc::new(Mutex::new(None)),
        });
        ActorSystem::init(actor_system.clone());
        actor_system
    }

    fn init(me: Arc<ActorSystem>) {
        *me.myself.lock().unwrap() = Some(Arc::downgrade(&me));
    }

    fn myself(&self) -> Option<Weak<ActorSystem>> {
        self.myself.lock().unwrap().clone()
    }

    /// Creates an `Actor` (currently just a `Printer`), adds it to the `actors_table` and gives an
    /// `ActorRef` to it.
    pub fn spawn_actor(&self, name: String, known_actors: Vec<ActorRef>) -> ActorRef {
        let actor_ref = Arc::new(Mutex::new(Printer::new(name, self.myself().unwrap(), known_actors)));
        {
            let mut actors_table = self.actors_table.lock().unwrap();
            actors_table.push(actor_ref.clone());
        }
        actor_ref
    }

    /// Sends `message` to the actor corresponding to `actor_ref`.
    pub fn send_to_actor(&self, actor_ref: ActorRef, message: Message) {
        {
            let actor = actor_ref.lock().unwrap();
            actor.receive(message);
        }
        {
            let mut actors_queue = self.actors_queue.lock().unwrap();
            actors_queue.push_back(actor_ref);
        }
    }

    /// Method to call on an `ActorSystem` to have him pop an `ActorRef` from his `actors_queue`
    /// and handle one of his messages.
    pub fn handle_actor_message(&self) {
        let actor_ref = {self.actors_queue.lock().unwrap().pop_front()};
        if let Some(actor) = actor_ref {
            actor.lock().unwrap().handle_message();
        }
    }

    /// Spawns a thread that will consume messages from the `ActorRef` in `actors_queue`.
    /// This thread can be terminated by calling `terminate_thread`.
    pub fn spawn_thread(actor_system: Arc<ActorSystem>) {
        let (tx, rx) = channel();
        let thread_actor_system =  actor_system.clone();
        let handle = thread::spawn(move || {
            loop {
                // If we received a () we kill the thread.
                match rx.try_recv() {
                    Ok(_) | Err(TryRecvError::Disconnected) => {
                        println!("Terminating a consumer thread.");
                        break;
                    },
                    Err(TryRecvError::Empty) => {}
                };
                // Else we try to prcess a message.
                thread_actor_system.handle_actor_message();
            }
        });
        actor_system.add_thread(handle, tx);
    }

    fn add_thread(&self, handle: thread::JoinHandle<()>, sender: Sender<()>) {
        self.consumer_threads.lock().unwrap().push((handle, sender));
    }

    /// Kills a consumer thread of the `ActorSystem`.
    pub fn terminate_thread(&self) {
        let (handle, tx) = {self.consumer_threads.lock().unwrap().pop().unwrap()};
        let _res = tx.send(());
        let _res = handle.join();
    }
}
<commit_msg>Makes the Actors have an Arc instead of a Weak to the ActorSytem.<commit_after>use std::any::Any;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use std::sync::mpsc::{channel, Sender, TryRecvError};
use std::thread;

/// Emum used to xrap messages.
///   * If the message is normal data it is in a Data variant.
///   * If the message is a Command it has its own variant.
pub enum Message {
    /// Variant used to pass real data around (done by passing a Box to it).
    Data(Box<Any + Send>),
    /// Dummy command.
    Command,
}

/// Utility type to wrap `Actor`s in a thread safe manner.
pub type ActorRef = Arc<Mutex<Actor>>;

/// Trait used for actors, implementing this trait is enough to be an Actor.
pub trait Actor: Send {
    /// Gets the `Actor`'s `ActorSystem`.
    fn actor_system(&self) -> Arc<ActorSystem>;
    /// Method to call on an `Actor` for him to put a message in his message queue.
    fn receive(&self, Message);
    /// Method to call on an `Actor` for him to handle a message from his message queue.
    fn handle_message(&self);
    /// Sends a `Message` to the given `ActorRef`
    fn send_message(&self, actor_ref: ActorRef, message: Message) {
        self.actor_system().send_to_actor(actor_ref, message);
    }
    /// DEV ONLY: Sends a message to the first ActorRef in known_actors.
    // Used on dev, to be removed afterwards.
    fn send_to_first(&self, message: Message);
}

/// This is a very basic `Actor` which can deal with `String` messages by printing them,
/// any other type of `Data` will have the `Printer` do nothing.
pub struct Printer {
    name: Arc<String>,
    message_queue: Arc<Mutex<VecDeque<Message>>>,
    actor_system: Arc<ActorSystem>,
    known_actors: Arc<Mutex<Vec<ActorRef>>>,
}

impl Printer {
    fn new(name: String, actor_system: Arc<ActorSystem>, known_actors: Vec<ActorRef>) -> Printer {
        Printer {
            name: Arc::new(name),
            message_queue: Arc::new(Mutex::new(VecDeque::new())),
            actor_system: actor_system,
            known_actors: Arc::new(Mutex::new(known_actors)),
        }
    }
}

impl Actor for Printer {
    fn actor_system(&self) -> Arc<ActorSystem> {
        self.actor_system.clone()
    }

    fn receive(&self, message: Message) {
        self.message_queue.lock().unwrap().push_back(message);
    }

    fn handle_message(&self) {
        let message = self.message_queue.lock().unwrap().pop_front().unwrap();

        println!("({}) treats a message", self.name);
        match message {
            Message::Data(ref data) => {
                match data.downcast_ref::<String>() {
                    Some(s) => println!("Received data: ({})", s),
                    None => println!("Message is dropped"),
                }
            },
            Message::Command => println!("Receiced a command"),
        }
    }

    fn send_to_first(&self, message: Message) {
        let actor_ref = self.known_actors.lock().unwrap()[0].clone();
        self.send_message(actor_ref, message);
    }
}

/// Wrapper around the threads handle and termination sender.
type ConsumerThread = (thread::JoinHandle<()>, Sender<()>);

/// A basic actor system, that handles the creation, distribution and handling of messages.
/// `Actor`s are simply put in a FIFO when they receive a message, they are poped from it when
/// `handle_actor_message` is called.
/// `ActorSystem::spawn_thread` allows to create simple threads that will handle `Actor`s messages.
pub struct ActorSystem {
    // TODO(gamazeps): Use an unordered container instead.
    // There is currently an issue with having an ActorRef as a Arc<Mutex<Actor + Eq + Hash>>.
    actors_table: Arc<Mutex<Vec<ActorRef>>>,
    actors_queue: Arc<Mutex<VecDeque<ActorRef>>>,
    consumer_threads: Arc<Mutex<Vec<ConsumerThread>>>,
    myself: Arc<Mutex<Option<Weak<ActorSystem>>>>,
}

unsafe impl Send for ActorSystem {}

impl ActorSystem {
    /// Creates a new `ActorSystem` and gives an `Arc` to it.
    pub fn new() -> Arc<ActorSystem> {
        let actor_system = Arc::new(ActorSystem {
            actors_table: Arc::new(Mutex::new(Vec::new())),
            actors_queue: Arc::new(Mutex::new(VecDeque::new())),
            consumer_threads: Arc::new(Mutex::new(Vec::new())),
            myself: Arc::new(Mutex::new(None)),
        });
        ActorSystem::init(actor_system.clone());
        actor_system
    }

    fn init(me: Arc<ActorSystem>) {
        *me.myself.lock().unwrap() = Some(Arc::downgrade(&me));
    }

    fn myself(&self) -> Option<Weak<ActorSystem>> {
        self.myself.lock().unwrap().clone()
    }

    /// Creates an `Actor` (currently just a `Printer`), adds it to the `actors_table` and gives an
    /// `ActorRef` to it.
    pub fn spawn_actor(&self, name: String, known_actors: Vec<ActorRef>) -> ActorRef {
        let actor_ref = Arc::new(Mutex::new(Printer::new(
                    name, self.myself().unwrap().upgrade().unwrap(), known_actors)));
        {
            let mut actors_table = self.actors_table.lock().unwrap();
            actors_table.push(actor_ref.clone());
        }
        actor_ref
    }

    /// Sends `message` to the actor corresponding to `actor_ref`.
    pub fn send_to_actor(&self, actor_ref: ActorRef, message: Message) {
        {
            let actor = actor_ref.lock().unwrap();
            actor.receive(message);
        }
        {
            let mut actors_queue = self.actors_queue.lock().unwrap();
            actors_queue.push_back(actor_ref);
        }
    }

    /// Method to call on an `ActorSystem` to have him pop an `ActorRef` from his `actors_queue`
    /// and handle one of his messages.
    pub fn handle_actor_message(&self) {
        let actor_ref = {self.actors_queue.lock().unwrap().pop_front()};
        if let Some(actor) = actor_ref {
            actor.lock().unwrap().handle_message();
        }
    }

    /// Spawns a thread that will consume messages from the `ActorRef` in `actors_queue`.
    /// This thread can be terminated by calling `terminate_thread`.
    pub fn spawn_thread(actor_system: Arc<ActorSystem>) {
        let (tx, rx) = channel();
        let thread_actor_system =  actor_system.clone();
        let handle = thread::spawn(move || {
            loop {
                // If we received a () we kill the thread.
                match rx.try_recv() {
                    Ok(_) | Err(TryRecvError::Disconnected) => {
                        println!("Terminating a consumer thread.");
                        break;
                    },
                    Err(TryRecvError::Empty) => {}
                };
                // Else we try to prcess a message.
                thread_actor_system.handle_actor_message();
            }
        });
        actor_system.add_thread(handle, tx);
    }

    fn add_thread(&self, handle: thread::JoinHandle<()>, sender: Sender<()>) {
        self.consumer_threads.lock().unwrap().push((handle, sender));
    }

    /// Kills a consumer thread of the `ActorSystem`.
    pub fn terminate_thread(&self) {
        let (handle, tx) = {self.consumer_threads.lock().unwrap().pop().unwrap()};
        let _res = tx.send(());
        let _res = handle.join();
    }
}
<|endoftext|>


<commit_before>extern crate system;

use std::cmp;
use std::fs::File;
use std::io::*;
use std::mem;
use std::slice;
use std::thread;

use self::system::graphics::fast_set;

use super::FONT;
use super::Event;
use super::Color;

/// A window
pub struct Window {
    /// The x coordinate of the window
    x: i32,
    /// The y coordinate of the window
    y: i32,
    /// The width of the window
    w: u32,
    /// The height of the window
    h: u32,
    /// The title of the window
    t: String,
    /// True if the window should not wait for events
    async: bool,
    /// The input scheme
    file: File,
    /// Window data
    data: Box<[Color]>,
}

impl Window {
    /// Create a new window
    pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Box<Self>> {
        Window::new_flags(x, y, w, h, title, false)
    }

    /// Create a new window with flags
    pub fn new_flags(x: i32, y: i32, w: u32, h: u32, title: &str, async: bool) -> Option<Box<Self>> {
        match File::open(&format!("orbital:{}/{}/{}/{}/{}/{}", if async { "a" } else { "" }, x, y, w, h, title)) {
            Ok(file) => {
                Some(box Window {
                    x: x,
                    y: y,
                    w: w,
                    h: h,
                    t: title.to_string(),
                    async: async,
                    file: file,
                    data: vec![Color::rgb(0, 0, 0); (w * h * 4) as usize].into_boxed_slice(),
                })
            }
            Err(_) => None,
        }
    }

    // TODO: Replace with smarter mechanism, maybe a move event?
    pub fn sync_path(&mut self) {
        if let Ok(path) = self.file.path() {
            // orbital:/x/y/w/h/t
            if let Some(path_str) = path.to_str() {
                let mut parts = path_str.split('/').skip(1);
                if let Some(x) = parts.next() {
                    self.x = x.parse::<i32>().unwrap_or(0);
                }
                if let Some(y) = parts.next() {
                    self.y = y.parse::<i32>().unwrap_or(0);
                }
                if let Some(w) = parts.next() {
                    self.w = w.parse::<u32>().unwrap_or(0);
                }
                if let Some(h) = parts.next() {
                    self.h = h.parse::<u32>().unwrap_or(0);
                }
            }
        }
    }

    /// Get x
    // TODO: Sync with window movements
    pub fn x(&self) -> i32 {
        self.x
    }

    /// Get y
    // TODO: Sync with window movements
    pub fn y(&self) -> i32 {
        self.y
    }

    /// Get width
    pub fn width(&self) -> u32 {
        self.w
    }

    /// Get height
    pub fn height(&self) -> u32 {
        self.h
    }

    /// Get title
    pub fn title(&self) -> String {
        self.t.clone()
    }

    /// Set title
    pub fn set_title(&mut self, _: &str) {
        // TODO
    }

    pub fn data(&self) -> &[Color] {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut [Color] {
        &mut self.data
    }

    /// Draw a pixel
    pub fn pixel(&mut self, x: i32, y: i32, color: Color) {
        if x >= 0 && y >= 0 && x < self.w as i32 && y < self.h as i32 {
            let new = color.data;

            let alpha = (new >> 24) & 0xFF;
            if alpha > 0 {
                let old = &mut self.data[y as usize * self.w as usize + x as usize].data;
                if alpha >= 255 {
                    *old = new;
                } else {
                    let n_r = (((new >> 16) & 0xFF) * alpha) >> 8;
                    let n_g = (((new >> 8) & 0xFF) * alpha) >> 8;
                    let n_b = ((new & 0xFF) * alpha) >> 8;

                    let n_alpha = 255 - alpha;
                    let o_a = (((*old >> 24) & 0xFF) * n_alpha) >> 8;
                    let o_r = (((*old >> 16) & 0xFF) * n_alpha) >> 8;
                    let o_g = (((*old >> 8) & 0xFF) * n_alpha) >> 8;
                    let o_b = ((*old & 0xFF) * n_alpha) >> 8;

                    *old = ((o_a << 24) | (o_r << 16) | (o_g << 8) | o_b) + ((alpha << 24) | (n_r << 16) | (n_g << 8) | n_b);
                }
            }
        }
    }

    /// Draw a line
    pub fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) {
        let mut x1 = argx1;
        let mut y1 = argy1;
        let mut x2 = argx2;
        let mut y2 = argy2;

        if x2 < x1 {
            x1 = argx2;
            y1 = argy2;
            x2 = argx1;
            y2 = argy1;
        }

        let dx = x2 - x1;
        let dy = y2 - y1;

        //let ratio = dy as f32 / dx as f32;
        for x in x1..x2 {
            let y = y1 + ((dy * (x - x1)) as f32 / dx as f32) as i32;
            self.pixel(x, y, color);
        }
    }

    pub fn lines(&mut self, points: &[[i32; 2]], color: Color) {
        if points.len() == 0 {
            // when no points given, do nothing
        } else if points.len() == 1 {
            self.pixel(points[0][0], points[0][1], color);
        } else {
            for i in 0..points.len() - 1 {
                self.line(points[i][0], points[i][1], points[i+1][0], points[i+1][1], color);
            }
        }
    }

    /// Draw a character, using the loaded font
    pub fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
        let mut offset = (c as usize) * 16;
        for row in 0..16 {
            let row_data;
            if offset < FONT.len() {
                row_data = FONT[offset];
            } else {
                row_data = 0;
            }

            for col in 0..8 {
                let pixel = (row_data >> (7 - col)) & 1;
                if pixel > 0 {
                    self.pixel(x + col as i32, y + row as i32, color);
                }
            }
            offset += 1;
        }
    }

    // TODO move, resize, set_title

    /// Set entire window to a color
    // TODO: Improve speed
    #[allow(unused_variables)]
    pub fn set(&mut self, color: Color) {
        unsafe {
            fast_set(self.data.as_mut_ptr() as *mut u32, color.data, self.data.len());
        }
    }

    /// Sets the whole window to black
    pub fn clear(&mut self) {
        self.set(Color::rgb(0,0,0));
    }

    /// Draw rectangle
    #[allow(unused_variables)]
    pub fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
        let data = color.data;

        let start_y = cmp::max(0, cmp::min(self.h as i32 - 1, y));
        let end_y = cmp::max(start_y, cmp::min(self.h as i32 - 1, y + h as i32));

        let start_x = cmp::max(0, cmp::min(self.w as i32 - 1, x));
        let len = cmp::max(start_x, cmp::min(self.w as i32 - 1, x + w as i32)) - start_x;

        for y in start_y..end_y {
            unsafe {
                fast_set(self.data.as_mut_ptr().offset((y * self.w as i32 + start_x) as isize) as *mut u32, data, len as usize);
            }
        }
    }

    /// Display an image
    // TODO: Improve speed
    pub fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
        let mut i = 0;
        for y in start_y..start_y + h as i32 {
            for x in start_x..start_x + w as i32 {
                if i < data.len() {
                    self.pixel(x, y, data[i])
                }
                i += 1;
            }
        }
    }

    /// Blocking iterator over events
    pub fn events(&mut self) -> EventIter {
        let mut iter = EventIter {
            events: [Event::new(); 128],
            i: 0,
            count: 0,
        };

        'blocking: loop {
            //Should it be cleared? iter.events = [Event::new(); 128];
            match self.file.read(unsafe {
                slice::from_raw_parts_mut(iter.events.as_mut_ptr() as *mut u8, iter.events.len() * mem::size_of::<Event>())
            }){
                Ok(0) => if ! self.async {
                    thread::yield_now();
                } else {
                    break 'blocking;
                },
                Ok(count) => {
                    iter.count = count/mem::size_of::<Event>();
                    break 'blocking;
                },
                Err(_) => break 'blocking,
            }
        }

        iter
    }

    /// Flip the window buffer
    pub fn sync(&mut self) -> bool {
        self.file.write(unsafe {
            slice::from_raw_parts(self.data.as_ptr() as *const u8, self.data.len() * mem::size_of::<Color>())
        }).is_ok()
    }
}

/// Event iterator
pub struct EventIter {
    events: [Event; 128],
    i: usize,
    count: usize,
}

impl Iterator for EventIter {
    type Item = Event;
    fn next(&mut self) -> Option<Event> {
        if self.i < self.count {
            if let Some(event) = self.events.get(self.i) {
                self.i += 1;
                Some(*event)
            } else {
                None
            }
        } else {
            None
        }
    }
}
<commit_msg>Add bmp drawing function to orbital code.<commit_after>extern crate system;

use std::cmp;
use std::fs::File;
use std::io::*;
use std::mem;
use std::slice;
use std::thread;

use self::system::graphics::fast_set;

use super::FONT;
use super::Event;
use super::Color;

/// A window
pub struct Window {
    /// The x coordinate of the window
    x: i32,
    /// The y coordinate of the window
    y: i32,
    /// The width of the window
    w: u32,
    /// The height of the window
    h: u32,
    /// The title of the window
    t: String,
    /// True if the window should not wait for events
    async: bool,
    /// The input scheme
    file: File,
    /// Window data
    data: Box<[Color]>,
}

impl Window {
    /// Create a new window
    pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Box<Self>> {
        Window::new_flags(x, y, w, h, title, false)
    }

    /// Create a new window with flags
    pub fn new_flags(x: i32, y: i32, w: u32, h: u32, title: &str, async: bool) -> Option<Box<Self>> {
        match File::open(&format!("orbital:{}/{}/{}/{}/{}/{}", if async { "a" } else { "" }, x, y, w, h, title)) {
            Ok(file) => {
                Some(box Window {
                    x: x,
                    y: y,
                    w: w,
                    h: h,
                    t: title.to_string(),
                    async: async,
                    file: file,
                    data: vec![Color::rgb(0, 0, 0); (w * h * 4) as usize].into_boxed_slice(),
                })
            }
            Err(_) => None,
        }
    }

    // TODO: Replace with smarter mechanism, maybe a move event?
    pub fn sync_path(&mut self) {
        if let Ok(path) = self.file.path() {
            // orbital:/x/y/w/h/t
            if let Some(path_str) = path.to_str() {
                let mut parts = path_str.split('/').skip(1);
                if let Some(x) = parts.next() {
                    self.x = x.parse::<i32>().unwrap_or(0);
                }
                if let Some(y) = parts.next() {
                    self.y = y.parse::<i32>().unwrap_or(0);
                }
                if let Some(w) = parts.next() {
                    self.w = w.parse::<u32>().unwrap_or(0);
                }
                if let Some(h) = parts.next() {
                    self.h = h.parse::<u32>().unwrap_or(0);
                }
            }
        }
    }

    /// Get x
    // TODO: Sync with window movements
    pub fn x(&self) -> i32 {
        self.x
    }

    /// Get y
    // TODO: Sync with window movements
    pub fn y(&self) -> i32 {
        self.y
    }

    /// Get width
    pub fn width(&self) -> u32 {
        self.w
    }

    /// Get height
    pub fn height(&self) -> u32 {
        self.h
    }

    /// Get title
    pub fn title(&self) -> String {
        self.t.clone()
    }

    /// Set title
    pub fn set_title(&mut self, _: &str) {
        // TODO
    }

    pub fn data(&self) -> &[Color] {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut [Color] {
        &mut self.data
    }

    /// Draw a pixel
    pub fn pixel(&mut self, x: i32, y: i32, color: Color) {
        if x >= 0 && y >= 0 && x < self.w as i32 && y < self.h as i32 {
            let new = color.data;

            let alpha = (new >> 24) & 0xFF;
            if alpha > 0 {
                let old = &mut self.data[y as usize * self.w as usize + x as usize].data;
                if alpha >= 255 {
                    *old = new;
                } else {
                    let n_r = (((new >> 16) & 0xFF) * alpha) >> 8;
                    let n_g = (((new >> 8) & 0xFF) * alpha) >> 8;
                    let n_b = ((new & 0xFF) * alpha) >> 8;

                    let n_alpha = 255 - alpha;
                    let o_a = (((*old >> 24) & 0xFF) * n_alpha) >> 8;
                    let o_r = (((*old >> 16) & 0xFF) * n_alpha) >> 8;
                    let o_g = (((*old >> 8) & 0xFF) * n_alpha) >> 8;
                    let o_b = ((*old & 0xFF) * n_alpha) >> 8;

                    *old = ((o_a << 24) | (o_r << 16) | (o_g << 8) | o_b) + ((alpha << 24) | (n_r << 16) | (n_g << 8) | n_b);
                }
            }
        }
    }

    /// Draw a line
    pub fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) {
        let mut x1 = argx1;
        let mut y1 = argy1;
        let mut x2 = argx2;
        let mut y2 = argy2;

        if x2 < x1 {
            x1 = argx2;
            y1 = argy2;
            x2 = argx1;
            y2 = argy1;
        }

        let dx = x2 - x1;
        let dy = y2 - y1;

        //let ratio = dy as f32 / dx as f32;
        for x in x1..x2 {
            let y = y1 + ((dy * (x - x1)) as f32 / dx as f32) as i32;
            self.pixel(x, y, color);
        }
    }

    pub fn lines(&mut self, points: &[[i32; 2]], color: Color) {
        if points.len() == 0 {
            // when no points given, do nothing
        } else if points.len() == 1 {
            self.pixel(points[0][0], points[0][1], color);
        } else {
            for i in 0..points.len() - 1 {
                self.line(points[i][0], points[i][1], points[i+1][0], points[i+1][1], color);
            }
        }
    }

    /// Draw a character, using the loaded font
    pub fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
        let mut offset = (c as usize) * 16;
        for row in 0..16 {
            let row_data;
            if offset < FONT.len() {
                row_data = FONT[offset];
            } else {
                row_data = 0;
            }

            for col in 0..8 {
                let pixel = (row_data >> (7 - col)) & 1;
                if pixel > 0 {
                    self.pixel(x + col as i32, y + row as i32, color);
                }
            }
            offset += 1;
        }
    }

    // TODO move, resize, set_title

    /// Set entire window to a color
    // TODO: Improve speed
    #[allow(unused_variables)]
    pub fn set(&mut self, color: Color) {
        unsafe {
            fast_set(self.data.as_mut_ptr() as *mut u32, color.data, self.data.len());
        }
    }

    /// Sets the whole window to black
    pub fn clear(&mut self) {
        self.set(Color::rgb(0,0,0));
    }

    /// Draw rectangle
    #[allow(unused_variables)]
    pub fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
        let data = color.data;

        let start_y = cmp::max(0, cmp::min(self.h as i32 - 1, y));
        let end_y = cmp::max(start_y, cmp::min(self.h as i32 - 1, y + h as i32));

        let start_x = cmp::max(0, cmp::min(self.w as i32 - 1, x));
        let len = cmp::max(start_x, cmp::min(self.w as i32 - 1, x + w as i32)) - start_x;

        for y in start_y..end_y {
            unsafe {
                fast_set(self.data.as_mut_ptr().offset((y * self.w as i32 + start_x) as isize) as *mut u32, data, len as usize);
            }
        }
    }

    /// Display an image
    // TODO: Improve speed
    pub fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
        let mut i = 0;
        for y in start_y..start_y + h as i32 {
            for x in start_x..start_x + w as i32 {
                if i < data.len() {
                    self.pixel(x, y, data[i])
                }
                i += 1;
            }
        }
    }

    /// Display an image stored in a bmp::BmpFile
    pub fn image_bmp(&mut self, start_x: i32, start_y: i32, bmp: &super::bmp::BmpFile) {
        use std::ops::Deref;

        let w = bmp.width() as u32;
        let h = bmp.height() as u32;

        let data = bmp.deref();

        self.image(start_x, start_y, w, h, data);
    }

    /// Blocking iterator over events
    pub fn events(&mut self) -> EventIter {
        let mut iter = EventIter {
            events: [Event::new(); 128],
            i: 0,
            count: 0,
        };

        'blocking: loop {
            //Should it be cleared? iter.events = [Event::new(); 128];
            match self.file.read(unsafe {
                slice::from_raw_parts_mut(iter.events.as_mut_ptr() as *mut u8, iter.events.len() * mem::size_of::<Event>())
            }){
                Ok(0) => if ! self.async {
                    thread::yield_now();
                } else {
                    break 'blocking;
                },
                Ok(count) => {
                    iter.count = count/mem::size_of::<Event>();
                    break 'blocking;
                },
                Err(_) => break 'blocking,
            }
        }

        iter
    }

    /// Flip the window buffer
    pub fn sync(&mut self) -> bool {
        self.file.write(unsafe {
            slice::from_raw_parts(self.data.as_ptr() as *const u8, self.data.len() * mem::size_of::<Color>())
        }).is_ok()
    }
}

/// Event iterator
pub struct EventIter {
    events: [Event; 128],
    i: usize,
    count: usize,
}

impl Iterator for EventIter {
    type Item = Event;
    fn next(&mut self) -> Option<Event> {
        if self.i < self.count {
            if let Some(event) = self.events.get(self.i) {
                self.i += 1;
                Some(*event)
            } else {
                None
            }
        } else {
            None
        }
    }
}
<|endoftext|>


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

#[cfg(windows)]
pub mod windows_child;

#[allow(unused_variables)]
#[cfg(windows)]
#[path = "windows.rs"]
mod imp;

#[cfg(not(windows))]
#[path = "linux.rs"]
mod imp;

pub use self::imp::*;

#[allow(non_snake_case)]
#[derive(Clone, Copy, Debug)]
pub enum Signal {
    INT,
    ILL,
    ABRT,
    FPE,
    KILL,
    SEGV,
    TERM,
    HUP,
    QUIT,
    ALRM,
    USR1,
    USR2,
    CHLD,
}
<commit_msg>Be more explicit about what the process module re-exports<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[cfg(windows)]
pub mod windows_child;

#[allow(unused_variables)]
#[cfg(windows)]
mod windows;

#[cfg(not(windows))]
mod linux;

#[cfg(windows)]
pub use self::windows::{become_command,
                        current_pid,
                        handle_from_pid,
                        is_alive,
                        Pid};

#[cfg(not(windows))]
pub(crate) use self::linux::SignalCode;
#[cfg(not(windows))]
pub use self::linux::{become_command,
                      current_pid,
                      is_alive,
                      signal,
                      Pid};

#[allow(non_snake_case)]
#[derive(Clone, Copy, Debug)]
pub enum Signal {
    INT,
    ILL,
    ABRT,
    FPE,
    KILL,
    SEGV,
    TERM,
    HUP,
    QUIT,
    ALRM,
    USR1,
    USR2,
    CHLD,
}
<|endoftext|>


<commit_before>
use std::clone::Clone;
use std::slice::Iter;
use std::fmt;
use std::collections::hash_map::HashMap;
use parser::ast::*;
use parser::store::*;
use parser::api::*;
use parser::util::allocate_element_key;
use output::structs::*;

use super::client_html::*;
use super::client_js::*;
use super::client_misc::*;

pub struct ProcessDocument<'input> {
    ast: &'input Template,
    processing: DocumentProcessingState<'input>,
}

impl<'input> ProcessDocument<'input> {
    pub fn from_template<'inp>(ast: &'inp Template,
                               processing: DocumentProcessingState<'inp>)
                               -> ProcessDocument<'inp> {
        ProcessDocument {
            ast: ast,
            processing: processing,
        }
    }

    pub fn collect_js_store_child_scope(&mut self,
                                        reducer_key: &'input str,
                                        nodes: &'input Vec<ScopeNodeType>,
                                        reducer_key_prefix: Option<&str>)
                                        -> fmt::Result {
        //let ref reducer_key_data = self.processing.reducer_key_data;

        for ref node in nodes {
            match *node {
                &ScopeNodeType::LetNode(ref var_name, ref expr) => {
                    let reducer_entry = self.processing.reducer_key_data.entry(var_name)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", var_name)));

                    if let &Some(ref expr) = expr {
                        reducer_entry.default_expr = Some(expr.clone());
                    };
                }
                &ScopeNodeType::ActionNode(ref action_name, ref simple_expr) => {
                    let reducer_entry = self.processing.reducer_key_data.entry(reducer_key)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", reducer_key)));

                    let action_path = format!("{}{}",
                                              reducer_key_prefix.and_then(|prefix| {
                                                      Some(format!("{}", prefix.to_uppercase()))
                                                  })
                                                  .and_then(|prefix| {
                                                      Some(format!("{}.", prefix.to_uppercase()))
                                                  })
                                                  .unwrap_or_default(),
                                              action_name);

                    let mut action = ReducerActionData::from_name(&action_path);
                    if let &Some(ref simple_expr) = simple_expr {
                        action.state_expr = Some(simple_expr.clone());
                    };
                    if let Some(ref mut actions) = reducer_entry.actions {
                        actions.push(action);
                    };
                }
                &ScopeNodeType::ScopeNode(ref scope_name, ref scope_nodes) => {
                    self.collect_js_store_child_scope(scope_name,
                                                      scope_nodes,
                                                      reducer_key_prefix)?;
                }
                _ => {}
            }
        }
        Ok(())
    }

    #[allow(unused_variables)]
    pub fn collect_js_store_api_scope(&mut self,
                                      scope_name: &'input str,
                                      nodes: &'input Vec<ApiNodeType>)
                                      -> fmt::Result {
        //let ref reducer_key_data = self.processing.reducer_key_data;

        for ref node in nodes {
            match *node {
                &ApiNodeType::ResourceNode(ref resource_data) => {
                    let reducer_name: &'input str = &resource_data.resource_name;

                    self.processing.reducer_key_data.entry(scope_name)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", scope_name)));
                }
                _ => {}
            }
        }
        Ok(())
    }

    #[inline]
    fn peek_var_ty(expr: &ExprValue) -> Option<VarType> {
        match *expr {
            ExprValue::LiteralNumber(..) => {
                return Some(VarType::Primitive(PrimitiveVarType::Number));
            }
            ExprValue::LiteralString(..) => {
                return Some(VarType::Primitive(PrimitiveVarType::StringVar));
            }
            ExprValue::LiteralArray(Some(ref items)) => {
                if !items.is_empty() {
                    if let Some(ref first_item) = items.get(0) {
                        if let Some(var_ty) = Self::peek_var_ty(first_item) {
                            return Some(VarType::ArrayVar(Some(Box::new(var_ty))));
                        }
                        return Some(VarType::ArrayVar(None));
                    };
                };
                return Some(VarType::ArrayVar(None));
            }
            _ => {}
        };
        None
    }

    pub fn collect_js_store_default_scope(&mut self,
                                          nodes: &'input Vec<DefaultScopeNodeType>)
                                          -> fmt::Result {
        //let ref reducer_key_data = self.processing.reducer_key_data;
        //let ref default_state_map = self.processing.default_state_map;

        for ref node in nodes {
            match *node {
                &DefaultScopeNodeType::LetNode(ref var_name, ref expr) => {
                    // Within the default scope let defines a new scope and it's default expression
                    let reducer_entry = self.processing.reducer_key_data.entry(var_name)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", var_name)));

                    if let &Some(ref expr) = expr {
                        reducer_entry.default_expr = Some(expr.clone());

                        let var_ty = Self::peek_var_ty(expr);

                        if var_ty.is_some() {
                            self.processing.default_state_map.entry(var_name)
                                .or_insert_with(|| (var_ty.clone(), Some(expr.clone())));
                        }
                    };
                }

                &DefaultScopeNodeType::ApiRootNode(ref scope_name, ref api_nodes) => {
                    if let &Some(ref api_nodes) = api_nodes {
                        self.collect_js_store_api_scope(scope_name, api_nodes)?;
                    }
                }
                &DefaultScopeNodeType::ScopeNode(ref scope_name, ref scope_nodes) => {
                    self.collect_js_store_child_scope(scope_name, scope_nodes, None)?;
                }
            }
        }
        Ok(())
    }

    #[inline]
    fn process_expr(&mut self,
                    expr: &'input ExprValue,
                    block: &mut BlockProcessingState,
                    resolve: &'input ResolveVars)
                    -> fmt::Result {
        match expr {
            &ExprValue::Expr(ExprOp::Add,
                             box ExprValue::ContentNode(ref l),
                             box ExprValue::ContentNode(ref r)) => {
                self.process_content_node(l, block, resolve)?;
                self.process_content_node(r, block, resolve)?;
            }

            &ExprValue::Expr(ExprOp::Add, box ExprValue::ContentNode(ref l), box ref r) => {
                self.process_content_node(l, block, resolve)?;
                self.process_expr(r, block, resolve)?;
            }

            &ExprValue::Expr(ExprOp::Add, box ref l, box ExprValue::ContentNode(ref r)) => {
                self.process_expr(l, block, resolve)?;
                self.process_content_node(r, block, resolve)?;
            }

            &ExprValue::Expr(ref op, ref l, ref r) => {
                // Write left expression
                self.process_expr(l, block, resolve)?;

                // Write operator
                let expr_str = match op {
                    &ExprOp::Add => "+",
                    &ExprOp::Sub => "-",
                    &ExprOp::Mul => "*",
                    &ExprOp::Div => "/",
                };
                // self.write_computed_expr_value(&mut expr_str, op, var_prefix, default_var)?;
                block.ops_vec.push(ElementOp::WriteValue(ExprValue::LiteralString(String::from(expr_str)),
                                                Some(allocate_element_key())));

                // Write right expression
                self.process_expr(r, block, resolve)?;
            }

            &ExprValue::ContentNode(ref node) => {
                self.process_content_node(node, block, resolve)?
            }

            _ => {
                block.ops_vec.push(ElementOp::WriteValue(expr.clone(), Some(allocate_element_key())));

            }
        };
        // ops_vec.push(ElementOp::WriteValue(expr.clone(), Some(allocate_element_key())));
        Ok(())
    }

    #[inline]
    fn process_content_node(&mut self,
                            node: &'input ContentNodeType,
                            block: &mut BlockProcessingState,
                            resolve: &ResolveVars<'input>)
                            -> fmt::Result {
        //let ref mut ops_vec = block.ops_vec;
        //let ref mut events_vec = block.events_vec;
        //let ref mut comp_map = self.processing.comp_map;

        match node {
            &ContentNodeType::ElementNode(ref element_data) => {
                let element_tag = element_data.element_ty.to_lowercase();
                let element_key =
                    element_data.element_key.as_ref().map_or(String::from(""), Clone::clone);

                let attrs = element_data.attrs.as_ref().map(Clone::clone);
                let lens = element_data.lens.as_ref().map(Clone::clone);

                let events = element_data.events
                    .as_ref()
                    .map(|attrs| attrs.iter().map(Clone::clone).collect());

                // Try to locate a matching component
                if let Some(..) = self.processing.comp_map.get(element_data.element_ty.as_str()) {

                    // Render a component during render
                    block.ops_vec.push(ElementOp::InstanceComponent(element_tag,
                                                              Some(element_key),
                                                              attrs,
                                                              lens));

                } else {
                    // Treat this as an HTML element
                    // TODO: Support imported elements

                    // Process events
                    if let Some(ref events) = element_data.events {
                        for &(ref event_name, ref event_params, ref action_ops) in events {
                            let event_name = event_name.as_ref().map(Clone::clone);
                            let event_params = event_params.as_ref().map(Clone::clone);
                            let action_ops = action_ops.as_ref().map(Clone::clone);
                            block.events_vec.push((element_key.clone(),
                                             event_name,
                                             event_params,
                                             action_ops,
                                             None));
                        }
                    }

                    if let Some(ref children) = element_data.children {
                        // Push element open
                        block.ops_vec.push(ElementOp::ElementOpen(element_tag.clone(),
                                                            Some(element_key),
                                                            attrs,
                                                            events));

                        // Iterate over children
                        for ref child in children {
                            self.process_content_node(child,
                                                      block,
                                                      resolve)?;
                        }

                        // Push element close
                        block.ops_vec.push(ElementOp::ElementClose(element_tag.clone()));
                    } else {
                        block.ops_vec.push(ElementOp::ElementVoid(element_tag.clone(),
                                                            Some(element_key),
                                                            attrs,
                                                            events));
                    }
                }
            }
            &ContentNodeType::ExpressionValueNode(ref expr) => {
                // FIXME
                // self.process_expr(expr, block, resolve)?
            }
            &ContentNodeType::ForNode(ref ele, ref coll_expr, ref nodes) => {
                let block_id = allocate_element_key().replace("-", "_");
                block.ops_vec.push(ElementOp::StartBlock(block_id.clone()));

                let forvar_default = &format!("__forvar_{}", block_id);
                let forvar_prefix =
                    &format!("__forvar_{}{}", block_id, ele.as_ref().map_or("", |s| s));

                if let &Some(ref nodes) = nodes {
                    for ref node in nodes {
                        // FIXME: forvar resolve
                        self.process_content_node(node, block, resolve)?;
                    }
                };

                block.ops_vec.push(ElementOp::EndBlock(block_id.clone()));
                block.ops_vec.push(ElementOp::MapCollection(block_id.clone(),
                                                      ele.as_ref().map(Clone::clone),
                                                      coll_expr.clone()));
            }
        }
        (Ok(()))
    }

    #[allow(dead_code)]
    pub fn process_component_definition(&mut self,
                                        component_data: &'input ComponentDefinitionType)
                                        -> fmt::Result {
        let name: &'input str = component_data.name.as_str();
        let mut block = BlockProcessingState::default();
        let resolve = ResolveVars::default();

        if let Some(ref children) = component_data.children {
            for ref child in children {
                match *child {
                    &NodeType::ContentNode(ref content) => {
                        self.process_content_node(content, &mut block, &resolve)?;
                    }
                    _ => {}
                }
            }
        }

        let ops: OpsVec = Default::default();
        let comp = Component {
            name: name,
            ops: Some(ops),
            uses: None,
            child_map: Default::default(),
        };

        self.processing.comp_map.insert(name, comp);

        Ok(())
    }

    pub fn process_nodes(&mut self,
                         resolve: &ResolveVars<'input>,
                         block: &mut BlockProcessingState)
                         -> fmt::Result {
        let mut processed_store = false;

        for ref loc in self.ast.children.iter() {
            match &loc.inner {
                &NodeType::StoreNode(ref scope_nodes) => {
                    // TODO: Allow more than one store?
                    if !processed_store {
                        self.collect_js_store_default_scope(scope_nodes)?;
                        processed_store = true;
                    }
                }
                &NodeType::ComponentDefinitionNode(ref component_data) => {
                    self.process_component_definition(component_data)?;
                }
                &NodeType::ContentNode(ref content) => {
                    self.process_content_node(content, block, resolve)?;
                }
                _ => {}
            }
        }
        Ok(())
    }

    #[allow(unused_variables)]
    pub fn write_js_event_bindings(&self,
                                   w: &mut fmt::Write,
                                   events_vec: &EventsVec,
                                   action_prefix: Option<&str>)
                                   -> fmt::Result {
        writeln!(w, "      // Bind actions")?;
        for &(ref element_key, ref event_name, ref params, ref action_ops, ref scope) in
            events_vec {
            let event_name = event_name.as_ref().map(String::as_str).map_or("click", |s| s);
            writeln!(w,
                     "  document.querySelector(\"[key='{}']\").addEventListener(\"{}\", \
                      function(event) {{",
                     element_key,
                     event_name)
                ?;
            if let &Some(ref action_ops) = action_ops {
                for ref action_op in action_ops {
                    match *action_op {
                        &ActionOpNode::DispatchAction(ref action_key, ref action_params) => {
                            // TODO: Fix type
                            let action_prefix = scope.as_ref()
                                .map_or("".into(), |s| s.to_uppercase());
                            let action_ty =
                                format!("{}.{}", action_prefix, action_key.to_uppercase());
                            writeln!(w, " store.dispatch({{\"type\": \"{}\"}}); ", action_ty)?;
                        }
                    }
                }
            }
            writeln!(w, "  }});")?;
        }
        Ok(())
    }

    #[allow(dead_code)]
    #[allow(unused_variables)]
    pub fn process_document(&mut self) -> fmt::Result {
        let mut root_block = BlockProcessingState::default();
        let resolve = ResolveVars::default();

        self.process_nodes(&resolve, &mut root_block)?;
        Ok(())
    }
}
<commit_msg>Fix lifetimes on process_expr<commit_after>
use std::clone::Clone;
use std::slice::Iter;
use std::fmt;
use std::collections::hash_map::HashMap;
use parser::ast::*;
use parser::store::*;
use parser::api::*;
use parser::util::allocate_element_key;
use output::structs::*;

use super::client_html::*;
use super::client_js::*;
use super::client_misc::*;

pub struct ProcessDocument<'input> {
    ast: &'input Template,
    processing: DocumentProcessingState<'input>,
}

impl<'input> ProcessDocument<'input> {
    pub fn from_template<'inp>(ast: &'inp Template,
                               processing: DocumentProcessingState<'inp>)
                               -> ProcessDocument<'inp> {
        ProcessDocument {
            ast: ast,
            processing: processing,
        }
    }

    pub fn collect_js_store_child_scope(&mut self,
                                        reducer_key: &'input str,
                                        nodes: &'input Vec<ScopeNodeType>,
                                        reducer_key_prefix: Option<&str>)
                                        -> fmt::Result {
        //let ref reducer_key_data = self.processing.reducer_key_data;

        for ref node in nodes {
            match *node {
                &ScopeNodeType::LetNode(ref var_name, ref expr) => {
                    let reducer_entry = self.processing.reducer_key_data.entry(var_name)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", var_name)));

                    if let &Some(ref expr) = expr {
                        reducer_entry.default_expr = Some(expr.clone());
                    };
                }
                &ScopeNodeType::ActionNode(ref action_name, ref simple_expr) => {
                    let reducer_entry = self.processing.reducer_key_data.entry(reducer_key)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", reducer_key)));

                    let action_path = format!("{}{}",
                                              reducer_key_prefix.and_then(|prefix| {
                                                      Some(format!("{}", prefix.to_uppercase()))
                                                  })
                                                  .and_then(|prefix| {
                                                      Some(format!("{}.", prefix.to_uppercase()))
                                                  })
                                                  .unwrap_or_default(),
                                              action_name);

                    let mut action = ReducerActionData::from_name(&action_path);
                    if let &Some(ref simple_expr) = simple_expr {
                        action.state_expr = Some(simple_expr.clone());
                    };
                    if let Some(ref mut actions) = reducer_entry.actions {
                        actions.push(action);
                    };
                }
                &ScopeNodeType::ScopeNode(ref scope_name, ref scope_nodes) => {
                    self.collect_js_store_child_scope(scope_name,
                                                      scope_nodes,
                                                      reducer_key_prefix)?;
                }
                _ => {}
            }
        }
        Ok(())
    }

    #[allow(unused_variables)]
    pub fn collect_js_store_api_scope(&mut self,
                                      scope_name: &'input str,
                                      nodes: &'input Vec<ApiNodeType>)
                                      -> fmt::Result {
        //let ref reducer_key_data = self.processing.reducer_key_data;

        for ref node in nodes {
            match *node {
                &ApiNodeType::ResourceNode(ref resource_data) => {
                    let reducer_name: &'input str = &resource_data.resource_name;

                    self.processing.reducer_key_data.entry(scope_name)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", scope_name)));
                }
                _ => {}
            }
        }
        Ok(())
    }

    #[inline]
    fn peek_var_ty(expr: &ExprValue) -> Option<VarType> {
        match *expr {
            ExprValue::LiteralNumber(..) => {
                return Some(VarType::Primitive(PrimitiveVarType::Number));
            }
            ExprValue::LiteralString(..) => {
                return Some(VarType::Primitive(PrimitiveVarType::StringVar));
            }
            ExprValue::LiteralArray(Some(ref items)) => {
                if !items.is_empty() {
                    if let Some(ref first_item) = items.get(0) {
                        if let Some(var_ty) = Self::peek_var_ty(first_item) {
                            return Some(VarType::ArrayVar(Some(Box::new(var_ty))));
                        }
                        return Some(VarType::ArrayVar(None));
                    };
                };
                return Some(VarType::ArrayVar(None));
            }
            _ => {}
        };
        None
    }

    pub fn collect_js_store_default_scope(&mut self,
                                          nodes: &'input Vec<DefaultScopeNodeType>)
                                          -> fmt::Result {
        //let ref reducer_key_data = self.processing.reducer_key_data;
        //let ref default_state_map = self.processing.default_state_map;

        for ref node in nodes {
            match *node {
                &DefaultScopeNodeType::LetNode(ref var_name, ref expr) => {
                    // Within the default scope let defines a new scope and it's default expression
                    let reducer_entry = self.processing.reducer_key_data.entry(var_name)
                        .or_insert_with(|| ReducerKeyData::from_name(&format!("{}", var_name)));

                    if let &Some(ref expr) = expr {
                        reducer_entry.default_expr = Some(expr.clone());

                        let var_ty = Self::peek_var_ty(expr);

                        if var_ty.is_some() {
                            self.processing.default_state_map.entry(var_name)
                                .or_insert_with(|| (var_ty.clone(), Some(expr.clone())));
                        }
                    };
                }

                &DefaultScopeNodeType::ApiRootNode(ref scope_name, ref api_nodes) => {
                    if let &Some(ref api_nodes) = api_nodes {
                        self.collect_js_store_api_scope(scope_name, api_nodes)?;
                    }
                }
                &DefaultScopeNodeType::ScopeNode(ref scope_name, ref scope_nodes) => {
                    self.collect_js_store_child_scope(scope_name, scope_nodes, None)?;
                }
            }
        }
        Ok(())
    }

    #[inline]
    fn process_expr(&mut self,
                    expr: &'input ExprValue,
                    block: &mut BlockProcessingState,
                    resolve: &ResolveVars<'input>)
                    -> fmt::Result {
        match expr {
            &ExprValue::Expr(ExprOp::Add,
                             box ExprValue::ContentNode(ref l),
                             box ExprValue::ContentNode(ref r)) => {
                self.process_content_node(l, block, resolve)?;
                self.process_content_node(r, block, resolve)?;
            }

            &ExprValue::Expr(ExprOp::Add, box ExprValue::ContentNode(ref l), box ref r) => {
                self.process_content_node(l, block, resolve)?;
                self.process_expr(r, block, resolve)?;
            }

            &ExprValue::Expr(ExprOp::Add, box ref l, box ExprValue::ContentNode(ref r)) => {
                self.process_expr(l, block, resolve)?;
                self.process_content_node(r, block, resolve)?;
            }

            &ExprValue::Expr(ref op, ref l, ref r) => {
                // Write left expression
                self.process_expr(l, block, resolve)?;

                // Write operator
                let expr_str = match op {
                    &ExprOp::Add => "+",
                    &ExprOp::Sub => "-",
                    &ExprOp::Mul => "*",
                    &ExprOp::Div => "/",
                };
                // self.write_computed_expr_value(&mut expr_str, op, var_prefix, default_var)?;
                block.ops_vec.push(ElementOp::WriteValue(ExprValue::LiteralString(String::from(expr_str)),
                                                Some(allocate_element_key())));

                // Write right expression
                self.process_expr(r, block, resolve)?;
            }

            &ExprValue::ContentNode(ref node) => {
                self.process_content_node(node, block, resolve)?
            }

            _ => {
                block.ops_vec.push(ElementOp::WriteValue(expr.clone(), Some(allocate_element_key())));

            }
        };
        // ops_vec.push(ElementOp::WriteValue(expr.clone(), Some(allocate_element_key())));
        Ok(())
    }

    #[inline]
    fn process_content_node(&mut self,
                            node: &'input ContentNodeType,
                            block: &mut BlockProcessingState,
                            resolve: &ResolveVars<'input>)
                            -> fmt::Result {
        //let ref mut ops_vec = block.ops_vec;
        //let ref mut events_vec = block.events_vec;
        //let ref mut comp_map = self.processing.comp_map;

        match node {
            &ContentNodeType::ElementNode(ref element_data) => {
                let element_tag = element_data.element_ty.to_lowercase();
                let element_key =
                    element_data.element_key.as_ref().map_or(String::from(""), Clone::clone);

                let attrs = element_data.attrs.as_ref().map(Clone::clone);
                let lens = element_data.lens.as_ref().map(Clone::clone);

                let events = element_data.events
                    .as_ref()
                    .map(|attrs| attrs.iter().map(Clone::clone).collect());

                // Try to locate a matching component
                if let Some(..) = self.processing.comp_map.get(element_data.element_ty.as_str()) {

                    // Render a component during render
                    block.ops_vec.push(ElementOp::InstanceComponent(element_tag,
                                                              Some(element_key),
                                                              attrs,
                                                              lens));

                } else {
                    // Treat this as an HTML element
                    // TODO: Support imported elements

                    // Process events
                    if let Some(ref events) = element_data.events {
                        for &(ref event_name, ref event_params, ref action_ops) in events {
                            let event_name = event_name.as_ref().map(Clone::clone);
                            let event_params = event_params.as_ref().map(Clone::clone);
                            let action_ops = action_ops.as_ref().map(Clone::clone);
                            block.events_vec.push((element_key.clone(),
                                             event_name,
                                             event_params,
                                             action_ops,
                                             None));
                        }
                    }

                    if let Some(ref children) = element_data.children {
                        // Push element open
                        block.ops_vec.push(ElementOp::ElementOpen(element_tag.clone(),
                                                            Some(element_key),
                                                            attrs,
                                                            events));

                        // Iterate over children
                        for ref child in children {
                            self.process_content_node(child,
                                                      block,
                                                      resolve)?;
                        }

                        // Push element close
                        block.ops_vec.push(ElementOp::ElementClose(element_tag.clone()));
                    } else {
                        block.ops_vec.push(ElementOp::ElementVoid(element_tag.clone(),
                                                            Some(element_key),
                                                            attrs,
                                                            events));
                    }
                }
            }
            &ContentNodeType::ExpressionValueNode(ref expr) => {
                // FIXME
                self.process_expr(expr, block, resolve)?
            }
            &ContentNodeType::ForNode(ref ele, ref coll_expr, ref nodes) => {
                let block_id = allocate_element_key().replace("-", "_");
                block.ops_vec.push(ElementOp::StartBlock(block_id.clone()));

                let forvar_default = &format!("__forvar_{}", block_id);
                let forvar_prefix =
                    &format!("__forvar_{}{}", block_id, ele.as_ref().map_or("", |s| s));

                if let &Some(ref nodes) = nodes {
                    for ref node in nodes {
                        // FIXME: forvar resolve
                        self.process_content_node(node, block, resolve)?;
                    }
                };

                block.ops_vec.push(ElementOp::EndBlock(block_id.clone()));
                block.ops_vec.push(ElementOp::MapCollection(block_id.clone(),
                                                      ele.as_ref().map(Clone::clone),
                                                      coll_expr.clone()));
            }
        }
        (Ok(()))
    }

    #[allow(dead_code)]
    pub fn process_component_definition(&mut self,
                                        component_data: &'input ComponentDefinitionType)
                                        -> fmt::Result {
        let name: &'input str = component_data.name.as_str();
        let mut block = BlockProcessingState::default();
        let resolve = ResolveVars::default();

        if let Some(ref children) = component_data.children {
            for ref child in children {
                match *child {
                    &NodeType::ContentNode(ref content) => {
                        self.process_content_node(content, &mut block, &resolve)?;
                    }
                    _ => {}
                }
            }
        }

        let ops: OpsVec = Default::default();
        let comp = Component {
            name: name,
            ops: Some(ops),
            uses: None,
            child_map: Default::default(),
        };

        self.processing.comp_map.insert(name, comp);

        Ok(())
    }

    pub fn process_nodes(&mut self,
                         resolve: &ResolveVars<'input>,
                         block: &mut BlockProcessingState)
                         -> fmt::Result {
        let mut processed_store = false;

        for ref loc in self.ast.children.iter() {
            match &loc.inner {
                &NodeType::StoreNode(ref scope_nodes) => {
                    // TODO: Allow more than one store?
                    if !processed_store {
                        self.collect_js_store_default_scope(scope_nodes)?;
                        processed_store = true;
                    }
                }
                &NodeType::ComponentDefinitionNode(ref component_data) => {
                    self.process_component_definition(component_data)?;
                }
                &NodeType::ContentNode(ref content) => {
                    self.process_content_node(content, block, resolve)?;
                }
                _ => {}
            }
        }
        Ok(())
    }

    #[allow(unused_variables)]
    pub fn write_js_event_bindings(&self,
                                   w: &mut fmt::Write,
                                   events_vec: &EventsVec,
                                   action_prefix: Option<&str>)
                                   -> fmt::Result {
        writeln!(w, "      // Bind actions")?;
        for &(ref element_key, ref event_name, ref params, ref action_ops, ref scope) in
            events_vec {
            let event_name = event_name.as_ref().map(String::as_str).map_or("click", |s| s);
            writeln!(w,
                     "  document.querySelector(\"[key='{}']\").addEventListener(\"{}\", \
                      function(event) {{",
                     element_key,
                     event_name)
                ?;
            if let &Some(ref action_ops) = action_ops {
                for ref action_op in action_ops {
                    match *action_op {
                        &ActionOpNode::DispatchAction(ref action_key, ref action_params) => {
                            // TODO: Fix type
                            let action_prefix = scope.as_ref()
                                .map_or("".into(), |s| s.to_uppercase());
                            let action_ty =
                                format!("{}.{}", action_prefix, action_key.to_uppercase());
                            writeln!(w, " store.dispatch({{\"type\": \"{}\"}}); ", action_ty)?;
                        }
                    }
                }
            }
            writeln!(w, "  }});")?;
        }
        Ok(())
    }

    #[allow(dead_code)]
    #[allow(unused_variables)]
    pub fn process_document(&mut self) -> fmt::Result {
        let mut root_block = BlockProcessingState::default();
        let resolve = ResolveVars::default();

        self.process_nodes(&resolve, &mut root_block)?;
        Ok(())
    }
}
<|endoftext|>