Completed
Push — master ( 3e8f03...4882f4 )
by Ilias
02:13
created

TokenMap::verifyTokenConfig()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 19
ccs 13
cts 13
cp 1
rs 9.2
cc 4
eloc 10
nc 4
nop 2
crap 4
1
<?php
2
3
4
namespace CondParse;
5
6
7
use CondParse\Exception\TokenMapException;
8
use CondParse\Operand\AndOperator;
9
use CondParse\Operand\BooleanOperand;
10
use CondParse\Operand\NotOperand;
11
use CondParse\Operand\OrOperator;
12
13
class TokenMap
14
{
15
    const TOKEN_NOT = 'NOT';
16
    const TOKEN_AND = 'AND';
17
    const TOKEN_OR = 'OR';
18
    const TOKEN_BRACKET_OPEN = 'BRACKET_OPEN';
19
    const TOKEN_BRACKET_CLOSE = 'BRACKET_CLOSE';
20
    const TOKEN_TRUE = 'TRUE';
21
    const TOKEN_FALSE = 'FALSE';
22
    const TOKEN_WHITESPACE = 'WHITESPACE';
23
24
    const DEFAULT_TOKENS = [
25
        self::TOKEN_NOT => '\!',
26
        self::TOKEN_AND => '&&',
27
        self::TOKEN_OR => '\|\|',
28
        self::TOKEN_BRACKET_OPEN => '\(',
29
        self::TOKEN_BRACKET_CLOSE => '\)',
30
        self::TOKEN_TRUE => 'true',
31
        self::TOKEN_FALSE => 'false',
32
        self::TOKEN_WHITESPACE => '\s+'
33
    ];
34
35
    const DEFAULT_OPERAND_TOKENS = [
36
        self::TOKEN_TRUE, self::TOKEN_FALSE
37
    ];
38
39
    const DEFAULT_OPERATOR_PRECEDENCE = [
40
        self::TOKEN_BRACKET_OPEN => -1,
41
        self::TOKEN_BRACKET_CLOSE => -1,
42
        self::TOKEN_OR => 100,
43
        self::TOKEN_AND => 1000,
44
        self::TOKEN_NOT => 10000
45
    ];
46
47
    const DEFAULT_TOKEN_CLASSES = [
48
        self::TOKEN_AND => AndOperator::class,
49
        self::TOKEN_OR => OrOperator::class,
50
        self::TOKEN_TRUE => BooleanOperand::class,
51
        self::TOKEN_FALSE => BooleanOperand::class,
52
        self::TOKEN_NOT => NotOperand::class
53
    ];
54
55
    private $tokenList = self::DEFAULT_TOKENS;
56
    private $operandTokens = self::DEFAULT_OPERAND_TOKENS;
57
    private $operatorPrecedence = self::DEFAULT_OPERATOR_PRECEDENCE;
58
    private $tokenClasses = self::DEFAULT_TOKEN_CLASSES;
59
60
    /**
61
     * @param string $name
62
     * @param string $regex
63
     * @param string $class
64
     */
65 16
    public function registerOperand($name, $regex, $class)
66
    {
67 16
        $this->registerToken($name, $regex, $class);
68 13
        $this->operandTokens[] = $name;
69 13
    }
70
71
    /**
72
     * @param string $name
73
     * @param string $regex
74
     * @param int $precedence
75
     * @param string $class
76
     */
77 5
    public function registerOperator($name, $regex, $precedence, $class)
78
    {
79 5
        $this->registerToken($name, $regex, $class);
80 2
        $this->operatorPrecedence[$name] = $precedence;
81 2
    }
82
83
    /**
84
     * @param string $name
85
     * @param string $regex
86
     * @param string $class
87
     * @throws TokenMapException
88
     */
89 19
    protected function registerToken($name, $regex, $class)
90
    {
91 19
        $this->verifyTokenConfig($name, $class);
92
93 13
        $this->tokenList[$name] = $regex;
94 13
        $this->tokenClasses[$name] = $class;
95 13
    }
96
97
    /**
98
     * @return string[]
99
     */
100 18
    public function getTokens()
101
    {
102 18
        return $this->tokenList;
103
    }
104
105
    /**
106
     * @param LexerToken $leftLexerToken
107
     * @param LexerToken $rightLexerToken
108
     * @return int
109
     */
110 6
    public function compareOperatorPrecedence(LexerToken $leftLexerToken, LexerToken $rightLexerToken)
111
    {
112
        return
113 6
            $this->operatorPrecedence[$leftLexerToken->getToken()]
114 6
            - $this->operatorPrecedence[$rightLexerToken->getToken()];
115
    }
116
117
    /**
118
     * @param LexerToken $lexerToken
119
     * @return bool
120
     */
121 10
    public function isOperand(LexerToken $lexerToken)
122
    {
123 10
        return in_array($lexerToken->getToken(), $this->operandTokens);
124
    }
125
126
    /**
127
     * @param LexerToken $lexerToken
128
     * @return OperandInterface
129
     */
130 10
    public function buildOperand(LexerToken $lexerToken)
131
    {
132 10
        return new $this->tokenClasses[$lexerToken->getToken()]($lexerToken);
133
    }
134
135
    /**
136
     * @param string $name
137
     * @param string $class
138
     * @throws TokenMapException
139
     */
140 19
    protected function verifyTokenConfig($name, $class)
141
    {
142 19
        if (!is_string($name)) {
143 2
            throw new TokenMapException('Can\'t register token, name must be string');
144
        }
145
146 17
        if (isset($this->tokenList[$name])) {
147 2
            throw new TokenMapException(sprintf(
148 2
                'Token <%s> already defined with regex <%s>',
149 2
                $name, $this->tokenList[$name]
150 2
            ));
151
        }
152
153 15
        if (!is_subclass_of($class, OperandInterface::class)) {
154 2
            throw new TokenMapException(sprintf(
155 2
                'Token <%s> with class <%s> must implement OperandInterface', $name, $class
156 2
            ));
157
        }
158 13
    }
159
}
160