[ create a new paste ] login | about

Link: http://codepad.org/8IIrzR4d    [ raw code | fork ]

k4st - PHP, pasted on May 27:
<?php

require_once dirname(__FILE__). '/__include__.php';

/**
 * A state machine-like class for validating the order of states. It is linear
 * insofar as a states move in only one direction: forward. The state machine
 * works as a stack and a queue. States are added to the stack, then as they
 * are popped off (a transition), they are put onto the transitions queue. Each
 * state is also a queue, thus allowing for nested states and branching.
 * @author Peter Goodman
 */
class LinearStateMachine extends Stack {
    
    public $transitions; // the currently available transitions
    private $full = FALSE; // are we done adding states?
    
    /**
     * Constructor, set up the state transitions.
     */
    public function __construct() {
        $this->transitions = new Queue;
        $this->operators = new Stack;
    }
    
    /**
     * Add a state.
     * @internal
     */
    private function pushState($state, $type) {
        
        if($this->full)
            throw new InvalidStateException("State machine is full.");
        
        $s = new State;
        $s->type = $type;
        $s->state = $state;
        
        $this->push($s);
        $this->states[(string)$state] = TRUE;
        
        return $this;
    }
    
    /**
     * Pop the current state off the stack and then add it into one of the
     * queues.
     */
    public function pop($expected = NULL) {
        try {
            $state = parent::pop(NULL);
            
            if($expected && $state->state != $expected)
                throw new InvalidStateException("Invalid state was popped off ".
                                                "stack. Found [{$state->state}] ".
                                                "but expected [{$expected}].");
            
            $where = (0 == count($this)) ? $this->transitions : $this->top();
            $where->push($state);
        
        // we ignore the exception because, when using optional functions that
        // take in multiple parameters, we won't even be adding to the stack
        // and so pop might return an exception.
        } catch(StackException $e) { }
        
        return $this;
    }
    
    /**
     * We're done adding a state, pop it off the stack and place it into the
     * progression.
     */
    public function to() {
        return $this->pop();
    }
    public function __get($operator) {
        return $this->pop();
    }
    
    /**
     * We're done.
     */
    public function end() {
        
        // pop anything left off the stack
        while(count($this) > 0)
            $this->pop();
        
        $this->full = TRUE;
        return $this;
    }
    
    /**
     * Set the optional states.
     */
    public function optional() {
        $states = func_get_args();
        return $this->pushStates($states, LinearState::OPTIONAL);
    }
    
    /**
     * Set the required states.
     */
    public function required() {
        $states = func_get_args();
        return $this->pushStates($states, LinearState::REQUIRED);
    }
    
    /**
     * Push a set of states onto the stack.
     */
    private function pushStates(array $states, $type) {
                
        // make sure we have at least one optional state to add
        if(empty($states))
            throw new InvalidStateException("Expected state list for ".
                                            "optional states.");
        
        // we want to do one fewer pops than the number of states we have
        // put on the stack.
        $this->pushState(array_shift($states), $type);
        foreach($states as $state) {
            $this->pop();
            $this->pushState($state, $type);
        }
        
        return $this;
    }

    /**
     * Create a branch.
     */
    private function pushBranch(array $states, 
                                $branch_type = LinearState::OPTIONAL, 
                                $state_type = LinearState::OPTIONAL) {
        
        // make sure we have at least one optional state to add
        if(empty($states))
            throw new InvalidStateException("Expected state list for ".
                                            "optional states.");
        
        // push a new branch onto the stack, then add the items to the
        // branch
        $this->pushState('__branch__', LinearState::BRANCH | $branch_type);
        
        // push the states onto the stack then queue them into the branch
        foreach($states as $state)
            $this->pushState($state, $state_type)->pop();
        
        return $this;
    }

    /**
     * A set of optional states that are exclusive. We don't pop the branch
     * off of the stack because it ought be popped off by the programmer with
     * 
     */
    public function optionalXor() {
        $states = func_get_args();
        return $this->pushBranch($states, LinearState::OPTIONAL,
                                          LinearState::OPTIONAL);
    }
    
    /**
     * Singular branch to, for symmetry.
     */
    public function requiredXor($state) {
        $states = func_get_args();
        return $this->pushBranch($states, LinearState::REQUIRED,
                                          LinearState::OPTIONAL);
    }
    
    /**
     * If the top item on the stack is a branch, add this state to the branch.
     * If it's not, throw an invalid state exception.
     */
    public function orBranchTo($state, $type = LinearState::OPTIONAL) {
        // whoops, stack is empty
        if(0 === count($this))
            throw new InvalidStateException("Expected to find branch on " .
                                            "stack. Instead stack is empty.");
        
        // we expect the top state on the stack to be a branch
        $branch = $this->top();
        
        // we didn't actually get a branch (as we expecte to)
        if(!($branch->type & LinearState::BRANCH))
            throw new InvalidStateException("Top element on state stack is ".
                                            "not Branch state.");
        
        return $this->pushState($state, $type);
    }
}

/**
 * A class representing a single state in the state machine.
 * @author Peter Goodman
 * @internal
 */
class State extends Queue {
    
    // different types of states available. Note: no end/terminal state type
    // is required. Note: the validity of a series of states is contingent on
    // all base level required transitions being executed. 
    const OPTIONAL = 1,
          REQUIRED = 2,
          BRANCH = 4,
          EXCLUSIVE = 8,
          CONTINUES = 16;
    
    public $type,
           $state,
           $used = 0,
           $max_uses = -1;
}


Create a new paste based on this one


Comments: