public function testFunctionDefinitionWithOptionalParams()
 {
     $context = new Context();
     $context->def('func', function ($param1, $param2 = 100) {
         return $param1 + $param2;
     });
     $actual = $context->fn('func', array(3));
     $this->assertEquals(103.0, $actual);
     $actual = $context->fn('func', array(3, 200));
     $this->assertEquals(203.0, $actual);
 }
 public function reduce(Context $ctx)
 {
     $this->stack = array();
     $len = 0;
     // While there are input tokens left
     // Read the next token from input.
     while ($t = array_shift($this->queue)) {
         switch ($t->type) {
             case Token::T_NUMBER:
             case Token::T_IDENT:
                 // wert einer konstanten ermitteln
                 if ($t->type === Token::T_IDENT) {
                     $t = new Token(Token::T_NUMBER, $ctx->cs($t->value));
                 }
                 // If the token is a value or identifier
                 // Push it onto the stack.
                 $this->stack[] = $t;
                 ++$len;
                 break;
             case Token::T_PLUS:
             case Token::T_MINUS:
             case Token::T_UNARY_PLUS:
             case Token::T_UNARY_MINUS:
             case Token::T_TIMES:
             case Token::T_DIV:
             case Token::T_MOD:
             case Token::T_POW:
             case Token::T_NOT:
                 // It is known a priori that the operator takes n arguments.
                 $na = $this->argc($t);
                 // If there are fewer than n values on the stack
                 if ($len < $na) {
                     throw new RuntimeError('laufzeit fehler: zu wenig paramter für operator "' . $t->value . '" (' . $na . ' -> ' . $len . ')');
                 }
                 $rhs = array_pop($this->stack);
                 $lhs = null;
                 if ($na > 1) {
                     $lhs = array_pop($this->stack);
                 }
                 // if ($lhs) print "{$lhs->value} {$t->value} {$rhs->value}\n";
                 // else print "{$t->value} {$rhs->value}\n";
                 $len -= $na - 1;
                 // Push the returned results, if any, back onto the stack.
                 $this->stack[] = new Token(Token::T_NUMBER, $this->op($t->type, $lhs, $rhs));
                 break;
             case Token::T_FUNCTION:
                 // function
                 $argc = $t->argc;
                 $argv = array();
                 $len -= $argc - 1;
                 for (; $argc > 0; --$argc) {
                     array_unshift($argv, array_pop($this->stack)->value);
                 }
                 // Push the returned results, if any, back onto the stack.
                 $this->stack[] = new Token(Token::T_NUMBER, $ctx->fn($t->value, $argv));
                 break;
             default:
                 throw new RuntimeError('laufzeit fehler: unerwarteter token `' . $t->value . '`');
         }
     }
     // If there is only one value in the stack
     // That value is the result of the calculation.
     if (count($this->stack) === 1) {
         return array_pop($this->stack)->value;
     }
     // If there are more values in the stack
     // (Error) The user input has too many values.
     throw new RuntimeError('laufzeit fehler: zu viele werte im stack');
 }