codepad
[
create a new paste
]
login
|
about
Language:
C
C++
D
Haskell
Lua
OCaml
PHP
Perl
Plain Text
Python
Ruby
Scheme
Tcl
<?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; }
Private
[
?
]
Run code