php-yaoi/php-yaoi

View on GitHub
src/String/Lexer/Parser.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

namespace Yaoi\String\Lexer;


use Yaoi\String\Exception;

/**
 * Class Parser
 * @package Yaoi\String\Tokenizer
 * @todo heredoc support
 * @todo check single letter quote
 */
class Parser
{
    /** @var Quote[]|array */
    protected $quoteStrings = array();

    public function addQuote($start, $end, $escape = array())
    {
        $this->quoteStrings[$start] = new Quote($start, $end, $escape);
        return $this;
    }

    /** @var LineTerminator[]|array */
    protected $lineStoppers = array();

    public function addLineStopper($start)
    {
        $this->lineStoppers[$start] = new LineTerminator($start);
        return $this;
    }


    /** @var Bracket[]|array */
    protected $brackets = array();

    public function addBracket($start, $end)
    {
        $this->brackets [$start] = new Bracket($start, $end);
        return $this;
    }

    /** @var Delimiter[]|array */
    protected $delimiters = array();
    public function addDelimiter($start)
    {
        $this->delimiters [$start] = new Delimiter($start);
        return $this;
    }

    /**
     * @param $string
     * @return Parsed
     * @throws Exception
     */
    public function tokenize($string)
    {
        $result = $mainResult = new Parsed();

        /** @var Quote $quoteStarted */
        $quoteStarted = null;
        $currentQuote = null;
        $unescapedString = false;
        $escapePosition = false;

        $prevPosition = 0;

        for ($position = 0; $position < strlen($string); ++$position) {
            do {
                if ($quoteStarted) {
                    if ($quoteStarted->escape) {
                        foreach ($quoteStarted->escape as $escaped => $unescaped) {
                            if (substr($string, $position, strlen($escaped)) === $escaped) {
                                if (null !== $escapePosition) {
                                    $unescapedString .= substr($string, $escapePosition, $position - $escapePosition);
                                }
                                $unescapedString .= $unescaped;
                                $position += strlen($escaped) - 1;
                                $escapePosition = $position + 1;
                                break 2;
                            }
                        }
                    }

                    if (substr($string, $position, $quoteStarted->endLen) === $quoteStarted->end) {
                        $originalString = substr($string, $prevPosition, $position - $prevPosition);
                        $unescapedString .= substr($string, $escapePosition, $position - $escapePosition);
                        $result->tokens [] = new Token($unescapedString, $quoteStarted->start, $quoteStarted->end, $originalString);
                        $position += $quoteStarted->endLen - 1;
                        $prevPosition = $position + 1;
                        $quoteStarted = false;
                    }

                    break;
                }

                foreach ($this->quoteStrings as $quote) {
                    if (substr($string, $position, $quote->startLen) === $quote->start) {
                        if ($prevPosition != $position) {
                            $result->tokens [] = substr($string, $prevPosition, $position - $prevPosition);
                        }

                        $position += $quote->startLen - 1;
                        $prevPosition = $position + 1;

                        $escapePosition = $position + 1;
                        $unescapedString = '';

                        $quoteStarted = $quote;
                        break 2;
                    }
                }

                foreach ($this->lineStoppers as $lineStopper) {
                    if (substr($string, $position, $lineStopper->startLen) === $lineStopper->start) {
                        $result->tokens [] = substr($string, $prevPosition, $position - $prevPosition);

                        $position += $lineStopper->startLen - 1;
                        $newLine = strpos($string, "\n", $position);
                        if (false === $newLine) {
                            $result->tokens [] = new Token(substr($string, $position + 1), $lineStopper->start);
                            $position = strlen($string);
                            $prevPosition = $position + 1;
                        } else {
                            $result->tokens [] = new Token(
                                substr($string, $position + 1, $newLine - $position - 1),
                                $lineStopper->start);
                            $prevPosition = $newLine;
                            $position = $newLine;
                        }
                        break 2;
                    }
                }

                foreach ($this->brackets as $bracket) {
                    if (substr($string, $position, $bracket->startLen) === $bracket->start) {
                        $result->tokens[] = substr($string, $prevPosition, $position - $prevPosition);
                        $position += $bracket->startLen - 1;
                        $prevPosition = $position + 1;

                        $bracketParsed = new Parsed();
                        $bracketParsed->parent = $result;
                        $bracketParsed->bracket = $bracket;
                        $result->tokens[] = $bracketParsed;

                        $result = $bracketParsed;
                        break 2;
                    } elseif (substr($string, $position, $bracket->endLen) === $bracket->end) {
                        if (!$result->bracket || $result->bracket->end != $bracket->end) {
                            throw new Exception('Closing not opened bracket', Exception::MALFORMED);
                        }

                        if ($prevPosition < $position) {
                            $result->tokens [] = substr($string, $prevPosition, $position - $prevPosition);
                        }

                        $position += $bracket->endLen - 1;
                        $prevPosition = $position + 1;
                        $bracketParsed = $result;
                        $result = $result->parent;
                        $bracketParsed->parent = null;
                        break 2;
                    }
                }

                foreach ($this->delimiters as $delimiter) {
                    if (substr($string, $position, $delimiter->startLen) === $delimiter->start) {
                        $result->tokens[] = substr($string, $prevPosition, $position - $prevPosition);
                        $result->tokens[] = $delimiter;
                        $position += $delimiter->startLen - 1;
                        $prevPosition = $position + 1;


                    }

                }

            } while (false);
        }
        if ($quoteStarted) {
            throw new Exception('Unterminated quoted expression', Exception::MALFORMED);
        }

        if ($prevPosition < $position) {
            $result->tokens [] = substr($string, $prevPosition, $position - $prevPosition);
        }

        return $mainResult;
    }

}