Passed
Push — master ( ad0973...f9c297 )
by Kacper
02:48
created

Language::setupRules()

Size

Total Lines 1

Duplication

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