/**
  * @param TraversableType $type
  *
  * @return Call|Closure
  */
 public function visitTraversableType(TraversableType $type)
 {
     $this->typeCheck->visitTraversableType(func_get_args());
     $primaryExpression = $type->primaryType()->accept($this);
     if ($type->primaryType() instanceof ArrayType && $type->keyType() instanceof MixedType && $type->valueType() instanceof MixedType) {
         return $primaryExpression;
     }
     $closure = new Closure();
     $closure->addParameter(new Parameter($this->valueIdentifier));
     $valueVariable = new Variable($this->valueIdentifier);
     $notTraversableObjectExpression = new LogicalNot(new InstanceOfType($valueVariable, QualifiedIdentifier::fromString('\\Traversable')));
     $isArrayCall = new Call(QualifiedIdentifier::fromString('\\is_array'));
     $isArrayCall->add($valueVariable);
     $notArrayExpression = new LogicalNot($isArrayCall);
     if ($type->primaryType() instanceof ArrayType) {
         $notTraversableExpression = $notArrayExpression;
     } elseif ($type->primaryType() instanceof ObjectType) {
         $notTraversableExpression = $notTraversableObjectExpression;
     } else {
         $notTraversableExpression = new LogicalAnd($notArrayExpression, $notTraversableObjectExpression);
     }
     $ifStatement = new IfStatement($notTraversableExpression);
     $ifStatement->trueBranch()->add(new ReturnStatement(new Literal(false)));
     $closure->statementBlock()->add($ifStatement);
     $keyIdentifier = new Identifier('key');
     $subValueIdentifier = new Identifier('subValue');
     $loopStatement = new StatementBlock();
     $keyVariable = new Variable($keyIdentifier);
     $oldValueIdentifier = $this->valueIdentifier;
     $this->valueIdentifier = $keyIdentifier;
     $keyExpression = $type->keyType()->accept($this);
     $this->valueIdentifier = $oldValueIdentifier;
     if ($keyExpression instanceof Closure) {
         $keyCheckVariable = new Variable(new Identifier('keyCheck'));
         $closure->statementBlock()->add(new ExpressionStatement(new Assign($keyCheckVariable, $keyExpression)));
         $keyExpression = new Call($keyCheckVariable);
         $keyExpression->add($keyVariable);
     }
     if (null !== $keyExpression) {
         $ifStatement = new IfStatement(new LogicalNot($keyExpression));
         $ifStatement->trueBranch()->add(new ReturnStatement(new Literal(false)));
         $loopStatement->add($ifStatement);
     }
     $subValueVariable = new Variable($subValueIdentifier);
     $oldValueIdentifier = $this->valueIdentifier;
     $this->valueIdentifier = $subValueIdentifier;
     $valueExpression = $type->valueType()->accept($this);
     $this->valueIdentifier = $oldValueIdentifier;
     if ($valueExpression instanceof Closure) {
         $valueCheckVariable = new Variable(new Identifier('valueCheck'));
         $closure->statementBlock()->add(new ExpressionStatement(new Assign($valueCheckVariable, $valueExpression)));
         $valueExpression = new Call($valueCheckVariable);
         $valueExpression->add($subValueVariable);
     }
     if (null !== $valueExpression) {
         $ifStatement = new IfStatement(new LogicalNot($valueExpression));
         $ifStatement->trueBranch()->add(new ReturnStatement(new Literal(false)));
         $loopStatement->add($ifStatement);
     }
     $closure->statementBlock()->add(new ForeachStatement($valueVariable, new Parameter($keyIdentifier), new Parameter($subValueIdentifier), $loopStatement));
     $closure->statementBlock()->add(new ReturnStatement(new Literal(true)));
     return $closure;
 }
 /**
  * @param ParameterList $parameterList
  *
  * @return array<integer,IStatement>
  */
 public function visitParameterList(ParameterList $parameterList)
 {
     $this->typeCheck->visitParameterList(func_get_args());
     $expressions = array();
     $parameters = $parameterList->parameters();
     $parameterCount = count($parameters);
     $argumentsVariable = new Variable(new Identifier('arguments'));
     // empty parameter list
     if ($parameterCount < 1) {
         $zeroLiteral = new Literal(0);
         $countCall = new Call(QualifiedIdentifier::fromString('\\count'));
         $countCall->add($argumentsVariable);
         $newExceptionCall = new Call(QualifiedIdentifier::fromString($this->validatorNamespace()->joinAtoms('Exception', 'UnexpectedArgumentException')->string()));
         $newExceptionCall->add($zeroLiteral);
         $newExceptionCall->add(new Subscript($argumentsVariable, $zeroLiteral));
         $ifStatement = new IfStatement(new Greater($countCall, $zeroLiteral));
         $ifStatement->trueBranch()->add(new ThrowStatement(new NewOperator($newExceptionCall)));
         $expressions[] = $ifStatement;
         return $this->wrapExpressions($expressions);
     }
     $argumentCountVariable = new Variable(new Identifier('argumentCount'));
     $argumentCountCall = new Call(QualifiedIdentifier::fromString('\\count'));
     $argumentCountCall->add($argumentsVariable);
     $expressions[] = new Assign($argumentCountVariable, $argumentCountCall);
     // missing parameter checks
     $requiredParameterCount = count($parameterList->requiredParameters());
     $lastRequiredParameterIndex = $requiredParameterCount - 1;
     $missingParametersStatement = null;
     if ($requiredParameterCount > 0) {
         $missingParametersStatement = new IfStatement(new Less($argumentCountVariable, new Literal($requiredParameterCount)));
         for ($i = 0; $i < $lastRequiredParameterIndex; $i++) {
             $newExceptionCall = new Call(QualifiedIdentifier::fromString($this->validatorNamespace()->joinAtoms('Exception', 'MissingArgumentException')->string()));
             $newExceptionCall->add(new Literal($parameters[$i]->name()));
             $newExceptionCall->add(new Literal($i));
             $newExceptionCall->add(new Literal($this->renderTypeName($parameters[$i]->type())));
             $ifStatement = new IfStatement(new Less($argumentCountVariable, new Literal($i + 1)));
             $ifStatement->trueBranch()->add(new ThrowStatement(new NewOperator($newExceptionCall)));
             $missingParametersStatement->trueBranch()->add($ifStatement);
         }
         $newExceptionCall = new Call(QualifiedIdentifier::fromString($this->validatorNamespace()->joinAtoms('Exception', 'MissingArgumentException')->string()));
         $newExceptionCall->add(new Literal($parameters[$lastRequiredParameterIndex]->name()));
         $newExceptionCall->add(new Literal($lastRequiredParameterIndex));
         $newExceptionCall->add(new Literal($this->renderTypeName($parameters[$lastRequiredParameterIndex]->type())));
         $missingParametersStatement->trueBranch()->add(new ThrowStatement(new NewOperator($newExceptionCall)));
     }
     // unexpected arguments check
     if (!$parameterList->isVariableLength()) {
         $parameterCountLiteral = new Literal($parameterCount);
         $newExceptionCall = new Call(QualifiedIdentifier::fromString($this->validatorNamespace()->joinAtoms('Exception', 'UnexpectedArgumentException')->string()));
         $newExceptionCall->add($parameterCountLiteral);
         $newExceptionCall->add(new Subscript($argumentsVariable, $parameterCountLiteral));
         $tooManyParametersStatement = new IfStatement(new Greater($argumentCountVariable, $parameterCountLiteral));
         $tooManyParametersStatement->trueBranch()->add(new ThrowStatement(new NewOperator($newExceptionCall)));
         if ($missingParametersStatement) {
             $missingParametersStatement->setFalseBranch($tooManyParametersStatement);
         } else {
             $expressions[] = $tooManyParametersStatement;
         }
     }
     if ($missingParametersStatement) {
         $expressions[] = $missingParametersStatement;
     }
     // type checks
     foreach ($parameters as $index => $parameter) {
         $isVariableLength = $parameterList->isVariableLength() && $index === $parameterCount - 1;
         $indexLiteral = new Literal($index);
         $oldArgumentExpression = $this->argumentExpression;
         $oldIndexExpression = $this->indexExpression;
         if (!$isVariableLength) {
             $this->indexExpression = $indexLiteral;
             $this->argumentExpression = new Subscript($argumentsVariable, $this->indexExpression);
         }
         $parameterExpressions = $parameter->accept($this);
         if (count($parameterExpressions) < 1) {
             $this->argumentExpression = $oldArgumentExpression;
             $this->indexExpression = $oldIndexExpression;
             continue;
         }
         array_unshift($parameterExpressions, new ExpressionStatement(new Assign(new Variable(new Identifier('value')), $this->argumentExpression)));
         $parameterExpressions = $this->wrapExpressions($parameterExpressions);
         // wrap variable length in loop
         if ($isVariableLength) {
             $closure = new Closure();
             $closure->addParameter(new ParameterASTNode(new Identifier('argument')));
             $closure->addParameter(new ParameterASTNode(new Identifier('index')));
             foreach ($parameterExpressions as $expression) {
                 $closure->statementBlock()->add($expression);
             }
             $checkVariable = new Variable(new Identifier('check'));
             $indexVariable = new Variable(new Identifier('index'));
             $checkCall = new Call($checkVariable);
             $checkCall->add(new Subscript($argumentsVariable, $indexVariable));
             $checkCall->add($indexVariable);
             $loopContents = new StatementBlock();
             $loopContents->add(new ExpressionStatement($checkCall));
             $parameterExpressions = array(new ExpressionStatement(new Assign($checkVariable, $closure)), new ForStatement(new Assign($indexVariable, $indexLiteral), new Less($indexVariable, $argumentCountVariable), new PostfixIncrement($indexVariable), $loopContents));
         }
         // wrap optional in if statement
         if ($parameter->isOptional()) {
             $if = new IfStatement(new Greater($argumentCountVariable, $indexLiteral));
             foreach ($parameterExpressions as $expression) {
                 $if->trueBranch()->add($expression);
             }
             $parameterExpressions = array($if);
         }
         foreach ($parameterExpressions as $expression) {
             $expressions[] = $expression;
         }
         $this->argumentExpression = $oldArgumentExpression;
         $this->indexExpression = $oldIndexExpression;
     }
     return $this->wrapExpressions($expressions);
 }