Completed
Branch 0.8-dev (af4386)
by Kacper
03:08
created

Language::_process()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
ccs 10
cts 10
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
crap 3
1
<?php
2
/**
3
 * Highlighter
4
 *
5
 * Copyright (C) 2016, Some right reserved.
6
 *
7
 * @author Kacper "Kadet" Donat <[email protected]>
8
 *
9
 * Contact with author:
10
 * Xmpp: [email protected]
11
 * E-mail: [email protected]
12
 *
13
 * From Kadet with love.
14
 */
15
16
namespace Kadet\Highlighter\Language;
17
18
use Kadet\Highlighter\Matcher\WholeMatcher;
19
use Kadet\Highlighter\Parser\Result;
20
use Kadet\Highlighter\Parser\Rule;
21
use Kadet\Highlighter\Parser\Rules;
22
use Kadet\Highlighter\Parser\Token\LanguageToken;
23
use Kadet\Highlighter\Parser\TokenFactory;
24
use Kadet\Highlighter\Parser\TokenIterator;
25
use Kadet\Highlighter\Parser\Tokens;
26
use Kadet\Highlighter\Parser\UnprocessedTokens;
27
use Kadet\Highlighter\Parser\Validator\Validator;
28
29
/**
30
 * Class Language
31
 *
32
 * @package Kadet\Highlighter\Language
33
 */
34
abstract class Language
35
{
36
    const NOT_EMBEDDED       = false;
37
    const EMBEDDED           = true;
38
    const EMBEDDED_BY_PARENT = 2;
39
40
    /**
41
     * @var array
42
     */
43
    protected $_options = [];
44
45
    /**
46
     * Tokenizer rules
47
     *
48
     * @var Rules
49
     */
50
    public $rules;
51
    
52
    /**
53
     * Language constructor.
54
     *
55
     * @param array $options
56
     */
57 16
    public function __construct(array $options = [])
58
    {
59
60 16
        $this->_options = array_merge([
61 16
            'embedded' => []
62 16
        ], $this->_options, $options);
63
64 16
        $this->rules = new Rules($this);
65 16
        $this->setupRules();
66 16
    }
67
68
    /**
69
     * Tokenization rules setup
70
     */
71
    abstract public function setupRules();
72
73
    /**
74
     * Parses source and removes wrong tokens.
75
     *
76
     * @param TokenIterator|string $tokens
77
     *
78
     * @param array                $additional
79
     * @param bool                 $embedded
80
     *
81
     * @return Tokens
82
     */
83 12
    public function parse($tokens = null, $additional = [], $embedded = false)
84
    {
85 12
        if (is_string($tokens)) {
86 11
            $tokens = $this->tokenize($tokens, $additional, $embedded);
87 12
        } elseif (!$tokens instanceof TokenIterator) {
88
            // Todo: Own Exceptions
89 1
            throw new \InvalidArgumentException('$tokens must be string or TokenIterator');
90 12
        }
91
92 11
        return $this->_process($tokens);
93
    }
94
95 11
    private function _process(TokenIterator $tokens) {
96 11
        $context  = [];
97 11
        $result   = new Result($tokens->getSource(), [
98 11
            $tokens->current()
99 11
        ]);
100
101 11
        for ($tokens->next(); $tokens->valid(); $tokens->next()) {
102 11
            if(!$tokens->current()->process($context, $this, $result, $tokens)) {
103 11
                break;
104
            }
105 10
        }
106
107 11
        return $result;
108
    }
109
110 12
    public function tokenize($source, $additional = [], $offset = 0, $embedded = false)
111
    {
112 12
        return new TokenIterator(
113 12
            $this->_tokens($source, $offset, $additional, $embedded)->sort()->toArray(), $source
114 12
        );
115
    }
116
117
    /**
118
     * Tokenize source
119
     *
120
     * @param                    $source
121
     *
122
     * @param int                $offset
123
     * @param array|\Traversable $additional
124
     *
125
     * @param bool               $embedded
126
     *
127
     * @return UnprocessedTokens
128
     */
129 12
    private function _tokens($source, $offset = 0, $additional = [], $embedded = false)
130
    {
131 12
        $result = new UnprocessedTokens();
132
133
        /** @var Language $language */
134 12
        foreach ($this->_rules($embedded) as $rule) {
135 12
            $rule->factory->setOffset($offset);
136 12
            foreach ($rule->match($source) as $token) {
137 12
                $result->add($token);
138 12
            }
139 12
        }
140
141 12
        return $result->batch($additional);
142
    }
143
144
    /**
145
     * @param bool $embedded
146
     *
147
     * @return Rule[]
148
     */
149 13
    private function _rules($embedded = false)
150
    {
151 12
        $rules = clone $this->rules;
152 12
        if(is_bool($embedded)) {
153 12
            $rules->addMany(['language.'.$this->getIdentifier() => $this->getEnds($embedded)]);
154 12
        }
155
156 12
        foreach ($rules->all() as $rule) {
157 12
            yield $rule;
158 12
        }
159
160 12
        foreach ($this->getEmbedded() as $language) {
161 1
            foreach ($language->_rules(true) as $rule) {
162 1
                yield $rule;
163 1
            }
164 13
        }
165 12
    }
166
167
    /**
168
     * Unique language identifier, for example 'php'
169
     *
170
     * @return string
171
     */
172
    abstract public function getIdentifier();
173
174
    /**
175
     * Language range Rule(s)
176
     *
177
     * @param $embedded
178
     *
179
     * @return Rule|\Kadet\Highlighter\Parser\Rule[]
180
     */
181 16
    public function getEnds($embedded = false)
182
    {
183 16
        return new Rule(
184 16
            new WholeMatcher(), [
185 16
                'priority' => 1000,
186 16
                'factory'  => new TokenFactory(LanguageToken::class),
187 16
                'inject'   => $this,
188 16
                'language' => null,
189 16
                'context'  => Validator::everywhere(),
190
            ]
191 16
        );
192
    }
193
194
    /**
195
     * @return Language[]
196
     */
197 13
    public function getEmbedded()
198
    {
199 13
        return $this->_options['embedded'];
200
    }
201
202
    /**
203
     * @param Language $lang
204
     */
205 1
    public function embed(Language $lang)
206
    {
207 1
        $this->_options['embedded'][] = $lang;
208 1
    }
209
210 1
    public function __get($name)
211
    {
212 1
        return isset($this->_options[$name]) ? $this->_options[$name] : null;
213
    }
214
215 1
    public function __set($name, $value)
216
    {
217 1
        $this->_options[$name] = $value;
218 1
    }
219
}
220