Exemplo n.º 1
0
 public function testInsertAndRemove()
 {
     $Stack = new Stack();
     $Stack->push('Radig');
     $this->assertIdentical($Stack->size(), 1);
     $this->assertIdentical($Stack->nth(), 'Radig');
     $Stack->push('CakePHP');
     $this->assertIdentical($Stack->size(), 2);
     // testa com 1-lookahead
     $this->assertIdentical($Stack->nth(), 'CakePHP');
     // teste com 2-lookahead
     $this->assertIdentical($Stack->nth(2), 'Radig');
     $this->assertIdentical($Stack->pop(), 'CakePHP');
     $this->assertIdentical($Stack->pop(), 'Radig');
     $this->assertIdentical($Stack->pop(), null);
     unset($Stack);
 }
Exemplo n.º 2
0
 /**
  * Convert infix to postfix notation
  *
  * @param string $expr
  */
 protected function nfx($expr)
 {
     $index = 0;
     $stack = new Stack();
     $output = array();
     // postfix form of expression, to be passed to pfx()
     $expr = trim($expr);
     $ops = array('+', '-', '*', '/', '^', '_');
     $ops_r = array('+' => 0, '-' => 0, '*' => 0, '/' => 0, '^' => 1);
     // right-associative operator?
     $ops_p = array('+' => 0, '-' => 0, '*' => 1, '/' => 1, '_' => 1, '^' => 2);
     // operator precedence
     // we use this in syntax-checking the expression
     // and determining when a - is a negation
     $expecting_op = false;
     // make sure the characters are all good
     if (preg_match("/[^\\w\\s+\\*\\^\\/\\(\\)\\.,\\-]/", $expr, $matches)) {
         return $this->error("Illegal character '{$matches[0]}'");
     }
     // 1 Infinite Loop ;)
     while (1) {
         // get the first character at the current index
         $op = substr($expr, $index, 1);
         // find out if we're currently at the beginning of a number/variable/function/parenthesis/operand
         $ex = preg_match('/^([a-zA-Z]\\w*\\(?|\\d+(?:\\.\\d*)?|\\.\\d+|\\()/', substr($expr, $index), $match);
         // is it a negation instead of a minus?
         if ($op == '-' and !$expecting_op) {
             // put a negation on the stack
             $stack->push('_');
             $index++;
         } elseif ($op == '_') {
             // but not in the input expression
             return $this->error("Illegal character '_'");
         } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
             // are we expecting an operator but have a number/variable/function/opening parethesis?
             if ($ex) {
                 // it's an implicit multiplication
                 $op = '*';
                 $index--;
             }
             // heart of the algorithm:
             while ($stack->size() > 0 && ($o2 = $stack->nth()) && in_array($o2, $ops) && ($ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2])) {
                 // pop stuff off the stack into the output
                 $output[] = $stack->pop();
             }
             // many thanks: http://en.wikipedia.org/wiki/Reverse_Polish_notation#The_algorithm_in_detail
             // finally put OUR operator onto the stack
             $stack->push($op);
             $index++;
             $expecting_op = false;
         } elseif ($op == ')' and $expecting_op) {
             // pop off the stack back to the last (
             while (($o2 = $stack->pop()) != '(') {
                 if (is_null($o2)) {
                     return $this->error("Unexpected ')'");
                 } else {
                     $output[] = $o2;
                 }
             }
             // did we just close a function?
             if (preg_match("/^([a-zA-Z]\\w*)\\(\$/", $stack->nth(2), $matches)) {
                 // get the function name
                 $fnn = $matches[1];
                 // see how many arguments there were (cleverly stored on the stack, thank you)
                 $arg_count = $stack->pop();
                 // pop the function and push onto the output
                 $output[] = $stack->pop();
                 // check the argument count
                 if (in_array($fnn, $this->fb)) {
                     if ($arg_count > 1) {
                         return $this->error("Too many arguments ({$arg_count} given, 1 expected)");
                     }
                 } elseif (array_key_exists($fnn, $this->f)) {
                     if ($arg_count != count($this->f[$fnn]['args'])) {
                         return $this->error("Wrong number of arguments ({$arg_count} given, " . count($this->f[$fnn]['args']) . " expected)");
                     }
                 } else {
                     return $this->error("Internal error");
                 }
             }
             $index++;
         } elseif ($op == ',' and $expecting_op) {
             while (($o2 = $stack->pop()) != '(') {
                 if (is_null($o2)) {
                     // oops, never had a (
                     return $this->error("Unexpected ','");
                 } else {
                     // pop the argument expression stuff and push onto the output
                     $output[] = $o2;
                 }
             }
             // make sure there was a function
             if (!preg_match("/^([a-zA-Z]\\w*)\\(\$/", $stack->nth(2), $matches)) {
                 return $this->error("Unexpected ','");
             }
             // increment the argument count
             $stack->push($stack->pop() + 1);
             // put the ( back on, we'll need to pop back to it again
             $stack->push('(');
             $index++;
             $expecting_op = false;
         } elseif ($op == '(' and !$expecting_op) {
             // that was easy
             $stack->push('(');
             $index++;
             $allow_neg = true;
         } elseif ($ex and !$expecting_op) {
             $expecting_op = true;
             $val = $match[1];
             // may be func, or variable w/ implicit multiplication against parentheses...
             if (preg_match("/^([a-zA-Z]\\w*)\\(\$/", $val, $matches)) {
                 // it's a func
                 if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) {
                     $stack->push($val);
                     $stack->push(1);
                     $stack->push('(');
                     $expecting_op = false;
                 } else {
                     $val = $matches[1];
                     $output[] = $val;
                 }
             } else {
                 $output[] = $val;
             }
             $index += strlen($val);
         } elseif ($op == ')') {
             return $this->error("Unexpected ')'");
         } elseif (in_array($op, $ops) and !$expecting_op) {
             return $this->error("Unexpected operator '{$op}'");
         } else {
             return $this->error("An unexpected error occured");
         }
         if ($index == strlen($expr)) {
             // did we end with an operator? bad.
             if (in_array($op, $ops)) {
                 return $this->error("Operator '{$op}' lacks operand");
             } else {
                 break;
             }
         }
         // step the index past whitespace (pretty much turns whitespace
         while (substr($expr, $index, 1) == ' ') {
             // into implicit multiplication if no operator is there)
             $index++;
         }
     }
     // pop everything off the stack and push onto output
     while (!is_null($op = $stack->pop())) {
         // if there are (s on the stack, ()s were unbalanced
         if ($op == '(') {
             return $this->error("Expecting ')'");
         }
         $output[] = $op;
     }
     return $output;
 }