Passed
Push — master ( 42d865...54862f )
by Kacper
03:02
created

Language::getIdentifier()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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