parse the HTML code

@param Token[] $tokens A list of tokens to parse

@throws HtmlParsingException
170670
public function parse($tokens)
    {
        $parents = array();

        // flag : are we in a <pre> Tag ?
        $this->tagPreIn = false;

        /**
         * all the actions to do
         * @var Node[] $actions
         */
        $actions = array();

        // get the actions from the html tokens
        foreach ($tokens as $token) {
            if ($token->getType() === 'code') {
                $actions = array_merge($actions, $this->getTagAction($token, $parents));
            } elseif ($token->getType() === 'txt') {
                $actions = array_merge($actions, $this->getTextAction($token));
            }
        }

        // for each identified action, we have to clean up the begin and the end of the texte
        // based on tags that surround it

        // list of the tags to clean
        $tagsToClean = array(
            'page', 'page_header', 'page_footer', 'form',
            'table', 'thead', 'tfoot', 'tr', 'td', 'th', 'br',
            'div', 'hr', 'p', 'ul', 'ol', 'li',
            'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
            'bookmark', 'fieldset', 'legend',
            'draw', 'circle', 'ellipse', 'path', 'rect', 'line', 'g', 'polygon', 'polyline',
            'option'
        );

        // list of the tags to move space
        $tagsToSpace = array(
            'span', 'font', 'label',
            'strong', 'b',
            'address', 'cite', 'em', 'i', 'samp',
            'cite', 's',
            'ins', 'u',
            'big', 'small', 'sub', 'sup'
        );

        // foreach action
        $nb = count($actions);
        for ($k = 0; $k < $nb; $k++) {
            // if it is a Text
            if ($actions[$k]->getName() !== 'write') {
                continue;
            }

            // if the tag before the text is a tag to clean => ltrim on the text
            if ($k>0) {
                if (in_array($actions[$k - 1]->getName(), $tagsToClean)) {
                    $actions[$k]->setParam('txt', ltrim($actions[$k]->getParam('txt')));
                }
            }

            if ($k < $nb - 1) {
                // if the tag after the text is a tag to clean => rtrim on the text
                if (in_array($actions[$k + 1]->getName(), $tagsToClean)) {
                    $actions[$k]->setParam('txt', rtrim($actions[$k]->getParam('txt')));
                }

                // if the tag after the text is a tag with space to move => move the space to the next write
                if (in_array($actions[$k + 1]->getName(), $tagsToSpace)) {
                    if (substr($actions[$k]->getParam('txt'), -1) == ' ') {
                        $actions[$k]->setParam('txt', rtrim($actions[$k]->getParam('txt')));
                        for ($subK = $k+2; $subK < $nb; $subK++) {
                            if ($actions[$subK]->getName() === 'write') {
                                $actions[$subK]->setParam('txt', ' '.ltrim($actions[$subK]->getParam('txt')));
                                break;
                            }
                        }
                    }
                }
            }

            // if the text is empty => remove the action
            if (!strlen($actions[$k]->getParam('txt'))) {
                unset($actions[$k]);
            }
        }

        // if we are not on the level 0 => HTML validator ERROR
        if (count($parents)) {
            if (count($parents)>1) {
                $errorMsg = 'The following tags have not been closed:';
            } else {
                $errorMsg = 'The following tag has not been closed:';
            }

            $e = new HtmlParsingException($errorMsg.' '.implode(', ', $parents));
            $e->setInvalidTag($parents[0]);
            throw $e;
        }

        $this->verifyMustContain($actions, 'thead', 'tr');
        $this->verifyMustContain($actions, 'tfoot', 'tr');

        // save the actions to do
        $this->code = array_values($actions);
    }
881717
public function add(string ...$tokens)
    {
        if (count($tokens) === 0) {
            return;
        }
        foreach ($tokens as $t) {
            if (in_array($t, $this->tokens)) {
                continue;
            }
            $this->tokens[] = $t;
        }
        $this->setAttributeValue();
    }
435971
private function parsePHPToken($token)
    {
        list($type, $expr) = $token;

        if ($type == T_INLINE_HTML) {
            $expr = $this->compileStatements($expr);
            $expr = $this->compileComments($expr);
            $expr = $this->compileRawEchos($expr);
            $expr = $this->compileRegularEchos($expr);
        }

        return $expr;
    }
1306012
function parse(array $tokens, &$i) {

    if (T_OPEN_TAG !== $tokens[$i][0]) {
      return FALSE;
    }

    ++$i;

    $nodes = array();
    while (TRUE) {
      $node = $this->parser->parse($tokens, $i);
      if (FALSE === $node) {
        $id = ParserUtil::nextSubstantialIncl($tokens, $i);
        if ('#' === $id) {
          // End of file/stream.
          break;
        }
        return FALSE;
      }
      if (NULL !== $node) {
        $nodes[] = $node;
      }
    }

    return new AstFile($nodes);
  }
731137
private function parseToken(array $token): string
    {
        list($id, $content) = $token;

        if ($id == T_INLINE_HTML) {
            $content = $this->compileVerbatim($content);

            foreach ($this->compilers as $callable) {
                if (!is_callable($callable) and is_scalar($callable)) {
                    $callable = [$this, $callable];
                }

                $content = (string) call_user_func_array($callable, [$content]);
            }

            $content = $this->restoreVerbatim($content);
        }

        return $content;
    }
249346
public function parse(Token $token)
    {
        parent::parse($token);

        $split = preg_split(self::REGEX, $token->getValue(), 2, PREG_SPLIT_DELIM_CAPTURE + PREG_SPLIT_NO_EMPTY);
        $splitSize = count($split);
        if (1 === $splitSize) {
            return $this->functionTokenParser->parse($token);
        }

        if (3 !== count($split) && 4 !== count($split)) {
            throw ExpressionLanguageExceptionFactory::createForCouldNotLexValue($token->getValue());
        }

        $values = [
            $this->parser->parse($split[0].$split[1]),
        ];
        if (3 === $splitSize) {
            $values[] = $this->parser->parse('<'.$split[2]);
        }

        if (4 === $splitSize) {
            $values[] = $this->parser->parse($split[2]);
            $values[] = $this->parser->parse('<'.$split[3]);
        }

        return $this->mergeValues($values);
    }
1148201
protected function parseToken($token)
    {
        list($id, $content) = $token;
        if ($id == T_INLINE_HTML) {
            $content = $this->renderShortcodes($content);
        }

        return $content;
    }
222832
public function parse(array $tokens) : int
    {
        $tokensAvailable = array_flip($this->getGrammar()->getTokens());

        $length = count($tokens);

        if ($length === 0) {
            throw new Exception('Invalid Roman', Exception::INVALID_ROMAN);
        }

        foreach ($tokens as $position => $token) {
            if (! is_string($token)) {
                $exception = new Exception(
                    sprintf('Invalid token type "%s" at position %d', gettype($token), $position),
                    Exception::INVALID_TOKEN_TYPE
                );

                $exception->setPosition($position);

                throw $exception;
            }

            if (! isset($tokensAvailable[$token])) {
                $exception = new Exception(
                    sprintf('Unknown token "%s" at position %d', $token, $position),
                    Exception::UNKNOWN_TOKEN
                );

                $exception
                    ->setToken($token)
                    ->setPosition($position);

                throw $exception;
            }
        }

        return (new Automaton($this->getGrammar()))->read($tokens)->getValue();
    }
49591
private function shuntingYard(array $tokens)
    {
        // Clear the oepratorStack
        $this->operatorStack = new Stack();

        // Clear the operandStack.
        $this->operandStack = new Stack();

        // Remember the last token handled, this is done to recognize unary operators.
        $lastNode = null;

        // Loop over the tokens
        for ($index = 0; $index < count($tokens); $index++)
        {
            $token = $tokens[$index];

            // echo "current token $token\n";
            // echo("operands:" . $this->operandStack . "\n");
            // echo("operators: " . $this->operatorStack . "\n\n");

            if ($this->rationalFactory) {
                $node = Node::rationalFactory($token);
            } else {
                $node = Node::factory($token);
            }

            // Handle closing parentheses
            if ($token->getType() == TokenType::CloseParenthesis) {
                $this->handleSubExpression();
            }
            // Push terminal tokens on the operandStack
            elseif ($node->isTerminal()) {
                $this->operandStack->push($node);

                // Push function applications or open parentheses `(` onto the operatorStack
            } elseif ($node instanceof FunctionNode) {
                $this->operatorStack->push($node);

            } elseif ($node instanceof SubExpressionNode) {
                $this->operatorStack->push($node);

                // Handle the remaining operators.
            } elseif ($node instanceof PostfixOperatorNode) {
                $op = $this->operandStack->pop();
                if ($op == NULL) throw new SyntaxErrorException();
                $this->operandStack->push(new FunctionNode($node->getOperator(), $op));

            } elseif ($node instanceof ExpressionNode) {

                // Check for unary minus and unary plus.
                $unary = $this->isUnary($node, $lastNode);

                if ($unary) {
                    switch($token->getType()) {
                        // Unary +, just ignore it
                        case TokenType::AdditionOperator:
                        $node = null;
                        break;
                        // Unary -, replace the token.
                        case TokenType::SubtractionOperator:
                        $node->setOperator('~');
                        break;
                    }
                } else {
                    // Pop operators with higher priority

                    while ($node->lowerPrecedenceThan($this->operatorStack->peek())) {

                        $popped = $this->operatorStack->pop();
                        $popped = $this->handleExpression($popped);
                        $this->operandStack->push($popped);
                    }
                }

                if ($node) $this->operatorStack->push($node);
            }

            // Remember the current token (if it hasn't been nulled, for example being a unary +)
            if ($node) $lastNode = $node;
        }


        // Pop remaining operators

        while (!$this->operatorStack->isEmpty()) {
            $node = $this->operatorStack->pop();
            $node = $this->handleExpression($node);
            $this->operandStack->push($node);
        }

        // Stack should be empty here
        if ($this->operandStack->count() > 1) {
            throw new SyntaxErrorException();
        }

        return $this->operandStack->pop();
    }
546583
public function process($tokens) {
        $prev_category = "";
        $token_category = "";
        $skip_next = 0;
        $out = false;

	// $tokens may come as a numeric indexed array starting with an index greater than 0 (or as a boolean)
	$tokenCount = count($tokens);
        if ( is_array($tokens) ){
          $tokens = array_values($tokens);
        }
        for ($tokenNumber = 0; $tokenNumber < $tokenCount; ++$tokenNumber) {

            // https://github.com/greenlion/PHP-SQL-Parser/issues/279
            // https://github.com/sinri/PHP-SQL-Parser/commit/eac592a0e19f1df6f420af3777a6d5504837faa7
            // as there is no pull request for 279 by the user. His solution works and tested.
            if (!isset($tokens[$tokenNumber])) continue;// as a fix by Sinri 20180528
            $token = $tokens[$tokenNumber];
            $trim = trim($token); // this removes also \n and \t!

            // if it starts with an "(", it should follow a SELECT
            if ($trim !== "" && $trim[0] === "(" && $token_category === "") {
                $token_category = 'BRACKET';
                $prev_category = $token_category;
            }

            /*
             * If it isn't obvious, when $skip_next is set, then we ignore the next real token, that is we ignore whitespace.
             */
            if ($skip_next > 0) {
                if ($trim === "") {
                    if ($token_category !== "") { // is this correct??
                        $out[$token_category][] = $token;
                    }
                    continue;
                }
                // to skip the token we replace it with whitespace
                $trim = "";
                $token = "";
                $skip_next--;
                if ($skip_next > 0) {
                    continue;
                }
            }

            $upper = strtoupper($trim);
            switch ($upper) {

            /* Tokens that get their own sections. These keywords have subclauses. */
            case 'SELECT':
            case 'ORDER':
            case 'VALUES':
            case 'GROUP':
            case 'HAVING':
            case 'WHERE':
            case 'CALL':
            case 'PROCEDURE':
            case 'FUNCTION':
            case 'SERVER':
            case 'LOGFILE':
            case 'DEFINER':
            case 'RETURNS':
            case 'TABLESPACE':
            case 'TRIGGER':
            case 'DO':
            case 'FLUSH':
            case 'KILL':
            case 'RESET':
            case 'STOP':
            case 'PURGE':
            case 'EXECUTE':
            case 'PREPARE':
                $token_category = $upper;
                break;

            case 'DEALLOCATE':
                if ($trim === 'DEALLOCATE') {
                    $skip_next = 1;
                }
                $token_category = $upper;
                break;

            case 'DUPLICATE':
                if ($token_category !== 'VALUES') {
                    $token_category = $upper;
                }
                break;

            case 'SET':
                if ($token_category !== 'TABLE') {
                    $token_category = $upper;
                }
                break;

            case 'LIMIT':
            case 'PLUGIN':
            // no separate section
                if ($token_category === 'SHOW') {
                    continue;
                }
                $token_category = $upper;
                break;

            case 'FROM':
            // this FROM is different from FROM in other DML (not join related)
                if ($token_category === 'PREPARE') {
                    continue 2;
                }
                // no separate section
                if ($token_category === 'SHOW') {
                    continue;
                }
                $token_category = $upper;
                break;

            case 'EXPLAIN':
            case 'DESCRIBE':
            case 'SHOW':
                $token_category = $upper;
                break;

            case 'DESC':
                if ($token_category === '') {
                    // short version of DESCRIBE
                    $token_category = $upper;
                }
                // else direction of ORDER-BY
                break;

            case 'RENAME':
                $token_category = $upper;
                break;

            case 'DATABASE':
            case 'SCHEMA':
                if ($prev_category === 'DROP') {
                    continue;
                }
                if ($prev_category === 'SHOW') {
                    continue;
                }
                $token_category = $upper;
                break;

            case 'EVENT':
            // issue 71
                if ($prev_category === 'DROP' || $prev_category === 'ALTER' || $prev_category === 'CREATE') {
                    $token_category = $upper;
                }
                break;

            case 'DATA':
            // prevent wrong handling of DATA as keyword
                if ($prev_category === 'LOAD') {
                    $token_category = $upper;
                }
                break;

            case 'INTO':
            // prevent wrong handling of CACHE within LOAD INDEX INTO CACHE...
                if ($prev_category === 'LOAD') {
                    $out[$prev_category][] = $trim;
                    continue 2;
                }
                $token_category = $prev_category = $upper;
                break;

            case 'USER':
            // prevent wrong processing as keyword
                if ($prev_category === 'CREATE' || $prev_category === 'RENAME' || $prev_category === 'DROP') {
                    $token_category = $upper;
                }
                break;

            case 'VIEW':
            // prevent wrong processing as keyword
                if ($prev_category === 'CREATE' || $prev_category === 'ALTER' || $prev_category === 'DROP') {
                    $token_category = $upper;
                }
                break;

            /*
             * These tokens get their own section, but have no subclauses. These tokens identify the statement but have no specific subclauses of their own.
             */
            case 'DELETE':
            case 'ALTER':
            case 'INSERT':
            case 'TRUNCATE':
            case 'OPTIMIZE':
            case 'GRANT':
            case 'REVOKE':
            case 'HANDLER':
            case 'LOAD':
            case 'ROLLBACK':
            case 'SAVEPOINT':
            case 'UNLOCK':
            case 'INSTALL':
            case 'UNINSTALL':
            case 'ANALZYE':
            case 'BACKUP':
            case 'CHECKSUM':
            case 'REPAIR':
            case 'RESTORE':
            case 'HELP':
                $token_category = $upper;
                // set the category in case these get subclauses in a future version of MySQL
                $out[$upper][0] = $trim;
                continue 2;

            case 'REPLACE':
            	if ($prev_category === '') {
            		// set the category in case these get subclauses in a future version of MySQL
            		$token_category = $upper;
            		$out[$upper][0] = $trim;
            		continue 2;
            	}
                // part of the CREATE TABLE statement or a function
                $out[$prev_category][] = $trim;
                continue 2;

            case 'IGNORE':
                if ($prev_category === 'TABLE') {
                    // part of the CREATE TABLE statement
                    $out[$prev_category][] = $trim;
                    continue 2;
                }
                if ($token_category === 'FROM') {
                    // part of the FROM statement (index hint)
                    $out[$token_category][] = $trim;
                    continue 2;
                }
                $out['OPTIONS'][] = $upper;
                continue 2;

            case 'CHECK':
                if ($prev_category === 'TABLE') {
                    $out[$prev_category][] = $trim;
                    continue 2;
                }
                $token_category = $upper;
                $out[$upper][0] = $trim;
                continue 2;

            case 'CREATE':
                if ($prev_category === 'SHOW') {
                    continue;
                }
                $token_category = $upper;
                break;

            case 'INDEX':
	            if ( in_array( $prev_category, array( 'CREATE', 'DROP' ) ) ) {
		            $out[ $prev_category ][] = $trim;
		            $token_category          = $upper;
	            }
	            break;

            case 'TABLE':
                if ($prev_category === 'CREATE') {
                    $out[$prev_category][] = $trim;
                    $token_category = $upper;
                }
                if ($prev_category === 'TRUNCATE') {
                    $out[$prev_category][] = $trim;
                    $token_category = $upper;
                }
                break;

            case 'TEMPORARY':
                if ($prev_category === 'CREATE') {
                    $out[$prev_category][] = $trim;
                    $token_category = $prev_category;
                    continue 2;
                }
                break;

            case 'IF':
                if ($prev_category === 'TABLE') {
                    $token_category = 'CREATE';
                    $out[$token_category] = array_merge($out[$token_category], $out[$prev_category]);
                    $out[$prev_category] = array();
                    $out[$token_category][] = $trim;
                    $prev_category = $token_category;
                    continue 2;
                }
                break;

            case 'NOT':
                if ($prev_category === 'CREATE') {
                    $token_category = $prev_category;
                    $out[$prev_category][] = $trim;
                    continue 2;
                }
                break;

            case 'EXISTS':
                if ($prev_category === 'CREATE') {
                    $out[$prev_category][] = $trim;
                    $prev_category = $token_category = 'TABLE';
                    continue 2;
                }
                break;

            case 'CACHE':
                if ($prev_category === "" || $prev_category === 'RESET' || $prev_category === 'FLUSH'
                    || $prev_category === 'LOAD') {
                    $token_category = $upper;
                    continue 2;
                }
                break;

            /* This is either LOCK TABLES or SELECT ... LOCK IN SHARE MODE */
            case 'LOCK':
                if ($token_category === "") {
                    $token_category = $upper;
                    $out[$upper][0] = $trim;
                } elseif ($token_category === 'INDEX') {
                    break;
                } else {
                    $trim = 'LOCK IN SHARE MODE';
                    $skip_next = 3;
                    $out['OPTIONS'][] = $trim;
                }
                continue 2;

            case 'USING': /* USING in FROM clause is different from USING w/ prepared statement*/
                if ($token_category === 'EXECUTE') {
                    $token_category = $upper;
                    continue 2;
                }
                if ($token_category === 'FROM' && !empty($out['DELETE'])) {
                    $token_category = $upper;
                    continue 2;
                }
                break;

            /* DROP TABLE is different from ALTER TABLE DROP ... */
            case 'DROP':
                if ($token_category !== 'ALTER') {
                    $token_category = $upper;
                    continue 2;
                }
                break;

            case 'FOR':
                if ($prev_category === 'SHOW') {
                    continue;
                }
                $skip_next = 1;
                $out['OPTIONS'][] = 'FOR UPDATE'; // TODO: this could be generate problems within the position calculator
                continue 2;

            case 'UPDATE':
                if ($token_category === "") {
                    $token_category = $upper;
                    continue 2;
                }
                if ($token_category === 'DUPLICATE') {
                    continue 2;
                }
                break;

            case 'START':
                $trim = "BEGIN";
                $out[$upper][0] = $upper; // TODO: this could be generate problems within the position calculator
                $skip_next = 1;
                break;

            // This token is ignored, except within RENAME
            case 'TO':
                if ($token_category === 'RENAME') {
                    break;
                }
                continue 2;

            // This token is ignored, except within CREATE TABLE
            case 'BY':
                if ($prev_category === 'TABLE') {
                    break;
                }
                continue 2;

            // These tokens are ignored.
            case 'ALL':
            case 'SHARE':
            case 'MODE':
            case ';':
                continue 2;

            case 'KEY':
                if ($token_category === 'DUPLICATE') {
                    continue 2;
                }
                break;

            /* These tokens set particular options for the statement. */
            case 'LOW_PRIORITY':
            case 'DELAYED':
            case 'QUICK':
            case 'HIGH_PRIORITY':
                $out['OPTIONS'][] = $trim;
                continue 2;

            case 'USE':
                if ($token_category === 'FROM') {
                    // index hint within FROM clause
                    $out[$token_category][] = $trim;
                    continue 2;
                }
                // set the category in case these get subclauses in a future version of MySQL
                $token_category = $upper;
                $out[$upper][0] = $trim;
                continue 2;

            case 'FORCE':
                if ($token_category === 'FROM') {
                    // index hint within FROM clause
                    $out[$token_category][] = $trim;
                    continue 2;
                }
                $out['OPTIONS'][] = $trim;
                continue 2;

            case 'WITH':
                if ($token_category === 'GROUP') {
                    $skip_next = 1;
                    $out['OPTIONS'][] = 'WITH ROLLUP'; // TODO: this could be generate problems within the position calculator
                    continue 2;
                }
                if ($token_category === '') {
                	$token_category = $upper;
                }
                break;

            case 'AS':
                break;

            case '':
            case ',':
            case ';':
                break;

            default:
                break;
            }

            // remove obsolete category after union (empty category because of
            // empty token before select)
            if ($token_category !== "" && ($prev_category === $token_category)) {
                $out[$token_category][] = $token;
            }

            $prev_category = $token_category;
        }

        return parent::process($out);
    }
835396
public function parse(array $tokens)
    {
        $symbolNodes = $this->detectSymbols($tokens);

        $nodes = $this->createTreeByBrackets($symbolNodes);

        $nodes = $this->transformTreeByFunctions($nodes);

        $this->checkGrammar($nodes);

        // Wrap the nodes in an array node.
        $rootNode = new ContainerNode($nodes);

        return $rootNode;
    }
113232
private function parse($headers)
    {
        foreach (preg_split('/\r\n|\r|\n/', $headers) as $i => $line) {
            if ($i === 0) {
                $this->headers['http_code'] = $line;
            } else {
                list($key, $value) = explode(': ', $line);

                if ($key === 'Set-Cookie') {
                    list($textCookie) = explode(';', $value);
                    list($cookieKey, $cookieValue) = explode('=', $textCookie);

                    $this->cookies[$cookieKey] = $cookieValue;
                } else {
                    $this->headers[$key] = $value;
                }
            }
        }
    }
394574
protected function parseMethodModifiers(array $tokens)
	{
		$modifiers = MetaInfo::IS_PUBLIC;
	
		foreach($tokens as $tok)
		{
			if(is_array($tok))
			{
				switch($tok[0])
				{
					case T_PUBLIC:
						$modifiers = ($modifiers | MetaInfo::MASK_VISIBILITY) ^ MetaInfo::IS_NOT_PUBLIC;
						break;
					case T_PRIVATE:
						$modifiers = ($modifiers | MetaInfo::MASK_VISIBILITY) ^ MetaInfo::IS_NOT_PRIVATE;
						break;
					case T_PROTECTED:
						$modifiers = ($modifiers | MetaInfo::MASK_VISIBILITY) ^ MetaInfo::IS_NOT_PROTECTED;
						break;
					case T_STATIC:
						$modifiers |= MetaInfo::IS_STATIC;
						break;
					case T_ABSTRACT:
						$modifiers |= MetaInfo::IS_ABSTRACT;
						break;
					case T_FINAL:
						$modifiers |= MetaInfo::IS_FINAL;
						break;
				}
			}
		}
	
		return $modifiers;
	}
429203
public static function parseTokens(&$tokens) {
        do {
		    $typeArray = array_shift($tokens);
            if ($typeArray['token'] == "T_WHITESPACE"){
                continue;
            }
            else {
                break;
            }
        }while(true);

        if ($typeArray['token'] != 'T_TYPE') {
			throw new MoufTypeParserException("Invalid type! Expecting a type name. Got '".$typeArray['match']."'");
		}
		
		$type = new TypeDescriptor();
		$type->type = $typeArray['match'];
		
		if (empty($typeArray)) {
			return $type;
		}
		
		do {
			$continue = false;
			$nextToken = array_shift($tokens);
		
			switch ($nextToken['token']) {
				case 'T_ARRAY';
					$continue = true;
					$subType = $type;
					$type = new TypeDescriptor();
					$type->type = 'array';
					$type->subType = $subType;
					break;
				case 'T_END_ARRAY';
                    array_unshift($tokens, $nextToken);
					break;
				case 'T_START_ARRAY';
					$tok1 = self::getNthTokenWithoutWhitespace($tokens, 1);
					if ($tok1['token'] == 'T_COMA') {
						$tok0 = self::getNthTokenWithoutWhitespace($tokens, 0);
						if ($tok0['token'] != 'T_TYPE') {
							throw new MoufTypeParserException("Invalid type! Expecting a type name. Got ".$tokens['match']);
						}
						$type->keyType = $tok0['match'];
						// Let's shift twice, without counting whitespaces:
						while ($tokens[0] != $tok1) {
							array_shift($tokens);
						}
						array_shift($tokens);
						// TODO: with a state machine, we could handle whitespaces in a best way
						// TODO: develop a statemachine!
						while ($tokens[0]['token'] == 'T_WHITESPACE') {
							array_shift($tokens);
						}
						
					}
					$type->subType = TypeDescriptor::parseTokens($tokens);

                    while ($tokens[0]['token'] == 'T_WHITESPACE') {
                        array_shift($tokens);
                    }

                    if ($tokens[0]['token'] != 'T_END_ARRAY'){
                        throw new MoufTypeParserException("Invalid type! Expecting >. Got '".$tokens[0]['match']."'");
                    }

                    array_shift($tokens);

					break;
				case 'T_OR':
					break;
			}
		} while ($continue);
		
		return $type;
	}
418554
public function parseToken(string $tokenString)
    {
        // try to parse a token string.
        try {
            return $this->parser()->parse($tokenString);
        } catch (\Exception $e) {
            // if it was not possible to, return null.
            return null;
        }
    }
490547
protected function setCode(array $tokens, int $line = 0): void
    {
        $this->tokens = $tokens;
        $this->line = $line;
    }
503731
public function parse(array $tokens)
    {
        $parser = new Parser($this->symbolContainer);

        if ($this->customGrammarChecker !== null) {
            $parser->setCustomGrammarChecker($this->customGrammarChecker);
        }

        $rootNode = $parser->parse($tokens);

        return $rootNode;
    }