Completed
Push — master ( e5166b...56b74c )
by Ilias
02:47
created

Lexer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
4
namespace CondParse;
5
6
7
use CondParse\Exception\LexerException;
8
9
class Lexer implements LexerInterface
10
{
11
    /** @var callable[] */
12
    private $postFunctions = [];
13
14
    /** @var LexerTokenFactory */
15
    private $lexerTokenFactory;
16
17 18
    public function __construct()
18
    {
19 18
        $this->lexerTokenFactory = new LexerTokenFactory;
20 18
    }
21
22
    /**
23
     * @param callable $postFunction
24
     * @return $this
25
     */
26 9
    public function registerPostFunction(callable $postFunction)
27
    {
28 9
        $this->postFunctions[] = $postFunction;
29 9
        return $this;
30
    }
31
32
    /**
33
     * @param TokenMap $tokenMap
34
     * @return string
35
     */
36 18
    protected function buildRegex(TokenMap $tokenMap)
37
    {
38 18
        return sprintf(
39 18
            '/%s/Si',
40 18
            join('|',
41
                array_map(function ($key, $value) {
42 18
                    return sprintf('(?P<%s>%s)', $key, $value);
43 18
                }, array_keys($tokenMap->getTokens()), $tokenMap->getTokens())
44 18
            )
45 18
        );
46
    }
47
48
    /**
49
     * @param string $token
50
     * @param string $match
51
     * @return string
52
     */
53 18
    protected function applyPostFunctions($token, $match)
54
    {
55 18
        foreach ($this->postFunctions as $postFunction) {
56 9
            $match = call_user_func($postFunction, $token, $match);
57 18
        }
58
59 18
        return $match;
60
    }
61
62
    /**
63
     * @param string $conditionString
64
     * @param string $regex
65
     * @return \Traversable <LexerToken>
66
     * @throws LexerException
67
     */
68 18
    protected function getTokenStreamWithRegex($conditionString, $regex)
69
    {
70 18
        $offSet = 0;
71
72 18
        $matches = [];
73 18
        while (preg_match($regex, $conditionString, $matches, 0, $offSet) !== 0) {
74 18
            list($match, $token) = $this->extractMatch($matches);
75
76 18
            $this->checkMatchOffset($conditionString, $match, $offSet);
77
78 18
            $offSet += strlen($match);
79
80 18
            yield $this->lexerTokenFactory->buildLexerToken($token, $this->applyPostFunctions($token, $match));
81 18
        }
82 17
    }
83
84
    /**
85
     * @param string $conditionString
86
     * @param TokenMap $tokenMap
87
     * @return \Traversable <string>
88
     */
89 18
    public function getTokenStream($conditionString, TokenMap $tokenMap) {
90 18
        return $this->getTokenStreamWithRegex($conditionString, $this->buildRegex($tokenMap));
91
    }
92
93
    /**
94
     * @param $matches
95
     * @return array
96
     */
97
    protected function extractMatch($matches)
98
    {
99 18
        $matches = array_filter($matches, function ($value, $key) {
100 18
            return is_string($key) && !empty($value);
101 18
        }, ARRAY_FILTER_USE_BOTH);
102
103 18
        $match = each($matches);
104 18
        $token = $match['key'];
105 18
        $match = $match['value'];
106 18
        return [$match, $token];
107
    }
108
109
    /**
110
     * @param $conditionString
111
     * @param $match
112
     * @param $offSet
113
     * @throws LexerException
114
     */
115 18
    protected function checkMatchOffset($conditionString, $match, $offSet)
116
    {
117 18
        if (($matchOffset = strpos($conditionString, $match, $offSet)) !== $offSet) {
118 1
            throw new LexerException(sprintf(
119 1
                'Found unrecognized token <%s> at offset %d',
120 1
                substr($conditionString, $offSet, ($matchOffset - $offSet)),
121
                $offSet
122 1
            ));
123
        }
124 18
    }
125
}
126