Passed
Push — master ( b558c2...d01ccb )
by Chris
04:06
created
vendor/leafo/scssphp/scss.inc.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -1,32 +1,32 @@
 block discarded – undo
1 1
 <?php
2 2
 if (version_compare(PHP_VERSION, '5.4') < 0) {
3
-    throw new \Exception('scssphp requires PHP 5.4 or above');
3
+	throw new \Exception('scssphp requires PHP 5.4 or above');
4 4
 }
5 5
 
6 6
 if (! class_exists('Leafo\ScssPhp\Version', false)) {
7
-    include_once __DIR__ . '/src/Base/Range.php';
8
-    include_once __DIR__ . '/src/Block.php';
9
-    include_once __DIR__ . '/src/Colors.php';
10
-    include_once __DIR__ . '/src/Compiler.php';
11
-    include_once __DIR__ . '/src/Compiler/Environment.php';
12
-    include_once __DIR__ . '/src/Exception/CompilerException.php';
13
-    include_once __DIR__ . '/src/Exception/ParserException.php';
14
-    include_once __DIR__ . '/src/Exception/RangeException.php';
15
-    include_once __DIR__ . '/src/Exception/ServerException.php';
16
-    include_once __DIR__ . '/src/Formatter.php';
17
-    include_once __DIR__ . '/src/Formatter/Compact.php';
18
-    include_once __DIR__ . '/src/Formatter/Compressed.php';
19
-    include_once __DIR__ . '/src/Formatter/Crunched.php';
20
-    include_once __DIR__ . '/src/Formatter/Debug.php';
21
-    include_once __DIR__ . '/src/Formatter/Expanded.php';
22
-    include_once __DIR__ . '/src/Formatter/Nested.php';
23
-    include_once __DIR__ . '/src/Formatter/OutputBlock.php';
24
-    include_once __DIR__ . '/src/Node.php';
25
-    include_once __DIR__ . '/src/Node/Number.php';
26
-    include_once __DIR__ . '/src/Parser.php';
27
-    include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php';
28
-    include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php';
29
-    include_once __DIR__ . '/src/Type.php';
30
-    include_once __DIR__ . '/src/Util.php';
31
-    include_once __DIR__ . '/src/Version.php';
7
+	include_once __DIR__ . '/src/Base/Range.php';
8
+	include_once __DIR__ . '/src/Block.php';
9
+	include_once __DIR__ . '/src/Colors.php';
10
+	include_once __DIR__ . '/src/Compiler.php';
11
+	include_once __DIR__ . '/src/Compiler/Environment.php';
12
+	include_once __DIR__ . '/src/Exception/CompilerException.php';
13
+	include_once __DIR__ . '/src/Exception/ParserException.php';
14
+	include_once __DIR__ . '/src/Exception/RangeException.php';
15
+	include_once __DIR__ . '/src/Exception/ServerException.php';
16
+	include_once __DIR__ . '/src/Formatter.php';
17
+	include_once __DIR__ . '/src/Formatter/Compact.php';
18
+	include_once __DIR__ . '/src/Formatter/Compressed.php';
19
+	include_once __DIR__ . '/src/Formatter/Crunched.php';
20
+	include_once __DIR__ . '/src/Formatter/Debug.php';
21
+	include_once __DIR__ . '/src/Formatter/Expanded.php';
22
+	include_once __DIR__ . '/src/Formatter/Nested.php';
23
+	include_once __DIR__ . '/src/Formatter/OutputBlock.php';
24
+	include_once __DIR__ . '/src/Node.php';
25
+	include_once __DIR__ . '/src/Node/Number.php';
26
+	include_once __DIR__ . '/src/Parser.php';
27
+	include_once __DIR__ . '/src/SourceMap/Base64VLQEncoder.php';
28
+	include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php';
29
+	include_once __DIR__ . '/src/Type.php';
30
+	include_once __DIR__ . '/src/Util.php';
31
+	include_once __DIR__ . '/src/Version.php';
32 32
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/Formatter.php 1 patch
Indentation   +250 added lines, -250 removed lines patch added patch discarded remove patch
@@ -21,254 +21,254 @@
 block discarded – undo
21 21
  */
22 22
 abstract class Formatter
23 23
 {
24
-    /**
25
-     * @var integer
26
-     */
27
-    public $indentLevel;
28
-
29
-    /**
30
-     * @var string
31
-     */
32
-    public $indentChar;
33
-
34
-    /**
35
-     * @var string
36
-     */
37
-    public $break;
38
-
39
-    /**
40
-     * @var string
41
-     */
42
-    public $open;
43
-
44
-    /**
45
-     * @var string
46
-     */
47
-    public $close;
48
-
49
-    /**
50
-     * @var string
51
-     */
52
-    public $tagSeparator;
53
-
54
-    /**
55
-     * @var string
56
-     */
57
-    public $assignSeparator;
58
-
59
-    /**
60
-     * @var boolean
61
-     */
62
-    public $keepSemicolons;
63
-
64
-    /**
65
-     * @var \Leafo\ScssPhp\Formatter\OutputBlock
66
-     */
67
-    protected $currentBlock;
68
-
69
-    /**
70
-     * @var integer
71
-     */
72
-    protected $currentLine;
73
-
74
-    /**
75
-     * @var integer
76
-     */
77
-    protected $currentColumn;
78
-
79
-    /**
80
-     * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
81
-     */
82
-    protected $sourceMapGenerator;
83
-
84
-    /**
85
-     * Initialize formatter
86
-     *
87
-     * @api
88
-     */
89
-    abstract public function __construct();
90
-
91
-    /**
92
-     * Return indentation (whitespace)
93
-     *
94
-     * @return string
95
-     */
96
-    protected function indentStr()
97
-    {
98
-        return '';
99
-    }
100
-
101
-    /**
102
-     * Return property assignment
103
-     *
104
-     * @api
105
-     *
106
-     * @param string $name
107
-     * @param mixed  $value
108
-     *
109
-     * @return string
110
-     */
111
-    public function property($name, $value)
112
-    {
113
-        return rtrim($name) . $this->assignSeparator . $value . ';';
114
-    }
115
-
116
-    /**
117
-     * Strip semi-colon appended by property(); it's a separator, not a terminator
118
-     *
119
-     * @api
120
-     *
121
-     * @param array $lines
122
-     */
123
-    public function stripSemicolon(&$lines)
124
-    {
125
-        if ($this->keepSemicolons) {
126
-            return;
127
-        }
128
-
129
-        if (($count = count($lines))
130
-            && substr($lines[$count - 1], -1) === ';'
131
-        ) {
132
-            $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
133
-        }
134
-    }
135
-
136
-    /**
137
-     * Output lines inside a block
138
-     *
139
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
140
-     */
141
-    protected function blockLines(OutputBlock $block)
142
-    {
143
-        $inner = $this->indentStr();
144
-
145
-        $glue = $this->break . $inner;
146
-
147
-        $this->write($inner . implode($glue, $block->lines));
148
-
149
-        if (! empty($block->children)) {
150
-            $this->write($this->break);
151
-        }
152
-    }
153
-
154
-    /**
155
-     * Output block selectors
156
-     *
157
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
158
-     */
159
-    protected function blockSelectors(OutputBlock $block)
160
-    {
161
-        $inner = $this->indentStr();
162
-
163
-        $this->write($inner
164
-            . implode($this->tagSeparator, $block->selectors)
165
-            . $this->open . $this->break);
166
-    }
167
-
168
-    /**
169
-     * Output block children
170
-     *
171
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
172
-     */
173
-    protected function blockChildren(OutputBlock $block)
174
-    {
175
-        foreach ($block->children as $child) {
176
-            $this->block($child);
177
-        }
178
-    }
179
-
180
-    /**
181
-     * Output non-empty block
182
-     *
183
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
184
-     */
185
-    protected function block(OutputBlock $block)
186
-    {
187
-        if (empty($block->lines) && empty($block->children)) {
188
-            return;
189
-        }
190
-
191
-        $this->currentBlock = $block;
192
-
193
-        $pre = $this->indentStr();
194
-
195
-        if (! empty($block->selectors)) {
196
-            $this->blockSelectors($block);
197
-
198
-            $this->indentLevel++;
199
-        }
200
-
201
-        if (! empty($block->lines)) {
202
-            $this->blockLines($block);
203
-        }
204
-
205
-        if (! empty($block->children)) {
206
-            $this->blockChildren($block);
207
-        }
208
-
209
-        if (! empty($block->selectors)) {
210
-            $this->indentLevel--;
211
-
212
-            if (empty($block->children)) {
213
-                $this->write($this->break);
214
-            }
215
-
216
-            $this->write($pre . $this->close . $this->break);
217
-        }
218
-    }
219
-
220
-    /**
221
-     * Entry point to formatting a block
222
-     *
223
-     * @api
224
-     *
225
-     * @param \Leafo\ScssPhp\Formatter\OutputBlock             $block              An abstract syntax tree
226
-     * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
227
-     *
228
-     * @return string
229
-     */
230
-    public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
231
-    {
232
-        $this->sourceMapGenerator = null;
233
-
234
-        if ($sourceMapGenerator) {
235
-            $this->currentLine = 1;
236
-            $this->currentColumn = 0;
237
-            $this->sourceMapGenerator = $sourceMapGenerator;
238
-        }
239
-
240
-        ob_start();
241
-
242
-        $this->block($block);
243
-
244
-        $out = ob_get_clean();
245
-
246
-        return $out;
247
-    }
248
-
249
-    /**
250
-     * @param string $str
251
-     */
252
-    protected function write($str)
253
-    {
254
-        if ($this->sourceMapGenerator) {
255
-            $this->sourceMapGenerator->addMapping(
256
-                $this->currentLine,
257
-                $this->currentColumn,
258
-                $this->currentBlock->sourceLine,
259
-                $this->currentBlock->sourceColumn - 1, //columns from parser are off by one
260
-                $this->currentBlock->sourceName
261
-            );
262
-
263
-            $lines = explode("\n", $str);
264
-            $lineCount = count($lines);
265
-            $this->currentLine += $lineCount-1;
266
-
267
-            $lastLine = array_pop($lines);
268
-
269
-            $this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine);
270
-        }
271
-
272
-        echo $str;
273
-    }
24
+	/**
25
+	 * @var integer
26
+	 */
27
+	public $indentLevel;
28
+
29
+	/**
30
+	 * @var string
31
+	 */
32
+	public $indentChar;
33
+
34
+	/**
35
+	 * @var string
36
+	 */
37
+	public $break;
38
+
39
+	/**
40
+	 * @var string
41
+	 */
42
+	public $open;
43
+
44
+	/**
45
+	 * @var string
46
+	 */
47
+	public $close;
48
+
49
+	/**
50
+	 * @var string
51
+	 */
52
+	public $tagSeparator;
53
+
54
+	/**
55
+	 * @var string
56
+	 */
57
+	public $assignSeparator;
58
+
59
+	/**
60
+	 * @var boolean
61
+	 */
62
+	public $keepSemicolons;
63
+
64
+	/**
65
+	 * @var \Leafo\ScssPhp\Formatter\OutputBlock
66
+	 */
67
+	protected $currentBlock;
68
+
69
+	/**
70
+	 * @var integer
71
+	 */
72
+	protected $currentLine;
73
+
74
+	/**
75
+	 * @var integer
76
+	 */
77
+	protected $currentColumn;
78
+
79
+	/**
80
+	 * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
81
+	 */
82
+	protected $sourceMapGenerator;
83
+
84
+	/**
85
+	 * Initialize formatter
86
+	 *
87
+	 * @api
88
+	 */
89
+	abstract public function __construct();
90
+
91
+	/**
92
+	 * Return indentation (whitespace)
93
+	 *
94
+	 * @return string
95
+	 */
96
+	protected function indentStr()
97
+	{
98
+		return '';
99
+	}
100
+
101
+	/**
102
+	 * Return property assignment
103
+	 *
104
+	 * @api
105
+	 *
106
+	 * @param string $name
107
+	 * @param mixed  $value
108
+	 *
109
+	 * @return string
110
+	 */
111
+	public function property($name, $value)
112
+	{
113
+		return rtrim($name) . $this->assignSeparator . $value . ';';
114
+	}
115
+
116
+	/**
117
+	 * Strip semi-colon appended by property(); it's a separator, not a terminator
118
+	 *
119
+	 * @api
120
+	 *
121
+	 * @param array $lines
122
+	 */
123
+	public function stripSemicolon(&$lines)
124
+	{
125
+		if ($this->keepSemicolons) {
126
+			return;
127
+		}
128
+
129
+		if (($count = count($lines))
130
+			&& substr($lines[$count - 1], -1) === ';'
131
+		) {
132
+			$lines[$count - 1] = substr($lines[$count - 1], 0, -1);
133
+		}
134
+	}
135
+
136
+	/**
137
+	 * Output lines inside a block
138
+	 *
139
+	 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
140
+	 */
141
+	protected function blockLines(OutputBlock $block)
142
+	{
143
+		$inner = $this->indentStr();
144
+
145
+		$glue = $this->break . $inner;
146
+
147
+		$this->write($inner . implode($glue, $block->lines));
148
+
149
+		if (! empty($block->children)) {
150
+			$this->write($this->break);
151
+		}
152
+	}
153
+
154
+	/**
155
+	 * Output block selectors
156
+	 *
157
+	 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
158
+	 */
159
+	protected function blockSelectors(OutputBlock $block)
160
+	{
161
+		$inner = $this->indentStr();
162
+
163
+		$this->write($inner
164
+			. implode($this->tagSeparator, $block->selectors)
165
+			. $this->open . $this->break);
166
+	}
167
+
168
+	/**
169
+	 * Output block children
170
+	 *
171
+	 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
172
+	 */
173
+	protected function blockChildren(OutputBlock $block)
174
+	{
175
+		foreach ($block->children as $child) {
176
+			$this->block($child);
177
+		}
178
+	}
179
+
180
+	/**
181
+	 * Output non-empty block
182
+	 *
183
+	 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
184
+	 */
185
+	protected function block(OutputBlock $block)
186
+	{
187
+		if (empty($block->lines) && empty($block->children)) {
188
+			return;
189
+		}
190
+
191
+		$this->currentBlock = $block;
192
+
193
+		$pre = $this->indentStr();
194
+
195
+		if (! empty($block->selectors)) {
196
+			$this->blockSelectors($block);
197
+
198
+			$this->indentLevel++;
199
+		}
200
+
201
+		if (! empty($block->lines)) {
202
+			$this->blockLines($block);
203
+		}
204
+
205
+		if (! empty($block->children)) {
206
+			$this->blockChildren($block);
207
+		}
208
+
209
+		if (! empty($block->selectors)) {
210
+			$this->indentLevel--;
211
+
212
+			if (empty($block->children)) {
213
+				$this->write($this->break);
214
+			}
215
+
216
+			$this->write($pre . $this->close . $this->break);
217
+		}
218
+	}
219
+
220
+	/**
221
+	 * Entry point to formatting a block
222
+	 *
223
+	 * @api
224
+	 *
225
+	 * @param \Leafo\ScssPhp\Formatter\OutputBlock             $block              An abstract syntax tree
226
+	 * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
227
+	 *
228
+	 * @return string
229
+	 */
230
+	public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
231
+	{
232
+		$this->sourceMapGenerator = null;
233
+
234
+		if ($sourceMapGenerator) {
235
+			$this->currentLine = 1;
236
+			$this->currentColumn = 0;
237
+			$this->sourceMapGenerator = $sourceMapGenerator;
238
+		}
239
+
240
+		ob_start();
241
+
242
+		$this->block($block);
243
+
244
+		$out = ob_get_clean();
245
+
246
+		return $out;
247
+	}
248
+
249
+	/**
250
+	 * @param string $str
251
+	 */
252
+	protected function write($str)
253
+	{
254
+		if ($this->sourceMapGenerator) {
255
+			$this->sourceMapGenerator->addMapping(
256
+				$this->currentLine,
257
+				$this->currentColumn,
258
+				$this->currentBlock->sourceLine,
259
+				$this->currentBlock->sourceColumn - 1, //columns from parser are off by one
260
+				$this->currentBlock->sourceName
261
+			);
262
+
263
+			$lines = explode("\n", $str);
264
+			$lineCount = count($lines);
265
+			$this->currentLine += $lineCount-1;
266
+
267
+			$lastLine = array_pop($lines);
268
+
269
+			$this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine);
270
+		}
271
+
272
+		echo $str;
273
+	}
274 274
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/Base/Range.php 1 patch
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -18,30 +18,30 @@
 block discarded – undo
18 18
  */
19 19
 class Range
20 20
 {
21
-    public $first;
22
-    public $last;
21
+	public $first;
22
+	public $last;
23 23
 
24
-    /**
25
-     * Initialize range
26
-     *
27
-     * @param integer|float $first
28
-     * @param integer|float $last
29
-     */
30
-    public function __construct($first, $last)
31
-    {
32
-        $this->first = $first;
33
-        $this->last = $last;
34
-    }
24
+	/**
25
+	 * Initialize range
26
+	 *
27
+	 * @param integer|float $first
28
+	 * @param integer|float $last
29
+	 */
30
+	public function __construct($first, $last)
31
+	{
32
+		$this->first = $first;
33
+		$this->last = $last;
34
+	}
35 35
 
36
-    /**
37
-     * Test for inclusion in range
38
-     *
39
-     * @param integer|float $value
40
-     *
41
-     * @return boolean
42
-     */
43
-    public function includes($value)
44
-    {
45
-        return $value >= $this->first && $value <= $this->last;
46
-    }
36
+	/**
37
+	 * Test for inclusion in range
38
+	 *
39
+	 * @param integer|float $value
40
+	 *
41
+	 * @return boolean
42
+	 */
43
+	public function includes($value)
44
+	{
45
+		return $value >= $this->first && $value <= $this->last;
46
+	}
47 47
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/SourceMap/Base64VLQEncoder.php 1 patch
Indentation   +193 added lines, -193 removed lines patch added patch discarded remove patch
@@ -21,197 +21,197 @@
 block discarded – undo
21 21
  */
22 22
 class Base64VLQEncoder
23 23
 {
24
-    /**
25
-     * Shift
26
-     *
27
-     * @var integer
28
-     */
29
-    private $shift = 5;
30
-
31
-    /**
32
-     * Mask
33
-     *
34
-     * @var integer
35
-     */
36
-    private $mask = 0x1F; // == (1 << shift) == 0b00011111
37
-
38
-    /**
39
-     * Continuation bit
40
-     *
41
-     * @var integer
42
-     */
43
-    private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
44
-
45
-    /**
46
-     * Char to integer map
47
-     *
48
-     * @var array
49
-     */
50
-    private $charToIntMap = array(
51
-        'A' => 0,  'B' => 1,  'C' => 2,  'D' => 3,  'E' => 4,  'F' => 5,  'G' => 6,  'H' => 7,
52
-        'I' => 8,  'J' => 9,  'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15,
53
-        'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23,
54
-        'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31,
55
-        'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39,
56
-        'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47,
57
-        'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51,   0 => 52,   1 => 53,   2 => 54,   3 => 55,
58
-          4 => 56,   5 => 57,   6 => 58,   7 => 59,   8 => 60,   9 => 61, '+' => 62, '/' => 63,
59
-    );
60
-
61
-    /**
62
-     * Integer to char map
63
-     *
64
-     * @var array
65
-     */
66
-    private $intToCharMap = array(
67
-         0 => 'A',  1 => 'B',  2 => 'C',  3 => 'D',  4 => 'E',  5 => 'F',  6 => 'G',  7 => 'H',
68
-         8 => 'I',  9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P',
69
-        16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X',
70
-        24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f',
71
-        32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n',
72
-        40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v',
73
-        48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
74
-        56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/',
75
-    );
76
-
77
-    /**
78
-     * Constructor
79
-     */
80
-    public function __construct()
81
-    {
82
-        // I leave it here for future reference
83
-        // foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
84
-        // {
85
-        //     $this->charToIntMap[$char] = $i;
86
-        //     $this->intToCharMap[$i] = $char;
87
-        // }
88
-    }
89
-
90
-    /**
91
-     * Convert from a two-complement value to a value where the sign bit is
92
-     * is placed in the least significant bit. For example, as decimals:
93
-     *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
94
-     *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
95
-     * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
96
-     * even on a 64 bit machine.
97
-     *
98
-     * @param string $aValue
99
-     */
100
-    public function toVLQSigned($aValue)
101
-    {
102
-        return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
103
-    }
104
-
105
-    /**
106
-     * Convert to a two-complement value from a value where the sign bit is
107
-     * is placed in the least significant bit. For example, as decimals:
108
-     *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
109
-     *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
110
-     * We assume that the value was generated with a 32 bit machine in mind.
111
-     * Hence
112
-     *   1 becomes -2147483648
113
-     * even on a 64 bit machine.
114
-     *
115
-     * @param integer $aValue
116
-     */
117
-    public function fromVLQSigned($aValue)
118
-    {
119
-        return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
120
-    }
121
-
122
-    /**
123
-     * Return the base 64 VLQ encoded value.
124
-     *
125
-     * @param string $aValue The value to encode
126
-     *
127
-     * @return string The encoded value
128
-     */
129
-    public function encode($aValue)
130
-    {
131
-        $encoded = '';
132
-        $vlq = $this->toVLQSigned($aValue);
133
-
134
-        do {
135
-            $digit = $vlq & $this->mask;
136
-            $vlq = $this->zeroFill($vlq, $this->shift);
137
-
138
-            if ($vlq > 0) {
139
-                $digit |= $this->continuationBit;
140
-            }
141
-
142
-            $encoded .= $this->base64Encode($digit);
143
-        } while ($vlq > 0);
144
-
145
-        return $encoded;
146
-    }
147
-
148
-    /**
149
-     * Return the value decoded from base 64 VLQ.
150
-     *
151
-     * @param string $encoded The encoded value to decode
152
-     *
153
-     * @return integer The decoded value
154
-     */
155
-    public function decode($encoded)
156
-    {
157
-        $vlq = 0;
158
-        $i = 0;
159
-
160
-        do {
161
-            $digit = $this->base64Decode($encoded[$i]);
162
-            $vlq |= ($digit & $this->mask) << ($i * $this->shift);
163
-            $i++;
164
-        } while ($digit & $this->continuationBit);
165
-
166
-        return $this->fromVLQSigned($vlq);
167
-    }
168
-
169
-    /**
170
-     * Right shift with zero fill.
171
-     *
172
-     * @param integer $a number to shift
173
-     * @param integer $b number of bits to shift
174
-     *
175
-     * @return integer
176
-     */
177
-    public function zeroFill($a, $b)
178
-    {
179
-        return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
180
-    }
181
-
182
-    /**
183
-     * Encode single 6-bit digit as base64.
184
-     *
185
-     * @param integer $number
186
-     *
187
-     * @return string
188
-     *
189
-     * @throws \Exception If the number is invalid
190
-     */
191
-    public function base64Encode($number)
192
-    {
193
-        if ($number < 0 || $number > 63) {
194
-            throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
195
-        }
196
-
197
-        return $this->intToCharMap[$number];
198
-    }
199
-
200
-    /**
201
-     * Decode single 6-bit digit from base64
202
-     *
203
-     * @param string $char
204
-     *
205
-     * @return integer
206
-     *
207
-     * @throws \Exception If the number is invalid
208
-     */
209
-    public function base64Decode($char)
210
-    {
211
-        if (! array_key_exists($char, $this->charToIntMap)) {
212
-            throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
213
-        }
214
-
215
-        return $this->charToIntMap[$char];
216
-    }
24
+	/**
25
+	 * Shift
26
+	 *
27
+	 * @var integer
28
+	 */
29
+	private $shift = 5;
30
+
31
+	/**
32
+	 * Mask
33
+	 *
34
+	 * @var integer
35
+	 */
36
+	private $mask = 0x1F; // == (1 << shift) == 0b00011111
37
+
38
+	/**
39
+	 * Continuation bit
40
+	 *
41
+	 * @var integer
42
+	 */
43
+	private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
44
+
45
+	/**
46
+	 * Char to integer map
47
+	 *
48
+	 * @var array
49
+	 */
50
+	private $charToIntMap = array(
51
+		'A' => 0,  'B' => 1,  'C' => 2,  'D' => 3,  'E' => 4,  'F' => 5,  'G' => 6,  'H' => 7,
52
+		'I' => 8,  'J' => 9,  'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15,
53
+		'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23,
54
+		'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31,
55
+		'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39,
56
+		'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47,
57
+		'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51,   0 => 52,   1 => 53,   2 => 54,   3 => 55,
58
+		  4 => 56,   5 => 57,   6 => 58,   7 => 59,   8 => 60,   9 => 61, '+' => 62, '/' => 63,
59
+	);
60
+
61
+	/**
62
+	 * Integer to char map
63
+	 *
64
+	 * @var array
65
+	 */
66
+	private $intToCharMap = array(
67
+		 0 => 'A',  1 => 'B',  2 => 'C',  3 => 'D',  4 => 'E',  5 => 'F',  6 => 'G',  7 => 'H',
68
+		 8 => 'I',  9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P',
69
+		16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X',
70
+		24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f',
71
+		32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n',
72
+		40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v',
73
+		48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
74
+		56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/',
75
+	);
76
+
77
+	/**
78
+	 * Constructor
79
+	 */
80
+	public function __construct()
81
+	{
82
+		// I leave it here for future reference
83
+		// foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
84
+		// {
85
+		//     $this->charToIntMap[$char] = $i;
86
+		//     $this->intToCharMap[$i] = $char;
87
+		// }
88
+	}
89
+
90
+	/**
91
+	 * Convert from a two-complement value to a value where the sign bit is
92
+	 * is placed in the least significant bit. For example, as decimals:
93
+	 *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
94
+	 *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
95
+	 * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
96
+	 * even on a 64 bit machine.
97
+	 *
98
+	 * @param string $aValue
99
+	 */
100
+	public function toVLQSigned($aValue)
101
+	{
102
+		return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
103
+	}
104
+
105
+	/**
106
+	 * Convert to a two-complement value from a value where the sign bit is
107
+	 * is placed in the least significant bit. For example, as decimals:
108
+	 *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
109
+	 *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
110
+	 * We assume that the value was generated with a 32 bit machine in mind.
111
+	 * Hence
112
+	 *   1 becomes -2147483648
113
+	 * even on a 64 bit machine.
114
+	 *
115
+	 * @param integer $aValue
116
+	 */
117
+	public function fromVLQSigned($aValue)
118
+	{
119
+		return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
120
+	}
121
+
122
+	/**
123
+	 * Return the base 64 VLQ encoded value.
124
+	 *
125
+	 * @param string $aValue The value to encode
126
+	 *
127
+	 * @return string The encoded value
128
+	 */
129
+	public function encode($aValue)
130
+	{
131
+		$encoded = '';
132
+		$vlq = $this->toVLQSigned($aValue);
133
+
134
+		do {
135
+			$digit = $vlq & $this->mask;
136
+			$vlq = $this->zeroFill($vlq, $this->shift);
137
+
138
+			if ($vlq > 0) {
139
+				$digit |= $this->continuationBit;
140
+			}
141
+
142
+			$encoded .= $this->base64Encode($digit);
143
+		} while ($vlq > 0);
144
+
145
+		return $encoded;
146
+	}
147
+
148
+	/**
149
+	 * Return the value decoded from base 64 VLQ.
150
+	 *
151
+	 * @param string $encoded The encoded value to decode
152
+	 *
153
+	 * @return integer The decoded value
154
+	 */
155
+	public function decode($encoded)
156
+	{
157
+		$vlq = 0;
158
+		$i = 0;
159
+
160
+		do {
161
+			$digit = $this->base64Decode($encoded[$i]);
162
+			$vlq |= ($digit & $this->mask) << ($i * $this->shift);
163
+			$i++;
164
+		} while ($digit & $this->continuationBit);
165
+
166
+		return $this->fromVLQSigned($vlq);
167
+	}
168
+
169
+	/**
170
+	 * Right shift with zero fill.
171
+	 *
172
+	 * @param integer $a number to shift
173
+	 * @param integer $b number of bits to shift
174
+	 *
175
+	 * @return integer
176
+	 */
177
+	public function zeroFill($a, $b)
178
+	{
179
+		return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
180
+	}
181
+
182
+	/**
183
+	 * Encode single 6-bit digit as base64.
184
+	 *
185
+	 * @param integer $number
186
+	 *
187
+	 * @return string
188
+	 *
189
+	 * @throws \Exception If the number is invalid
190
+	 */
191
+	public function base64Encode($number)
192
+	{
193
+		if ($number < 0 || $number > 63) {
194
+			throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
195
+		}
196
+
197
+		return $this->intToCharMap[$number];
198
+	}
199
+
200
+	/**
201
+	 * Decode single 6-bit digit from base64
202
+	 *
203
+	 * @param string $char
204
+	 *
205
+	 * @return integer
206
+	 *
207
+	 * @throws \Exception If the number is invalid
208
+	 */
209
+	public function base64Decode($char)
210
+	{
211
+		if (! array_key_exists($char, $this->charToIntMap)) {
212
+			throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
213
+		}
214
+
215
+		return $this->charToIntMap[$char];
216
+	}
217 217
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/SourceMap/SourceMapGenerator.php 1 patch
Indentation   +311 added lines, -311 removed lines patch added patch discarded remove patch
@@ -23,315 +23,315 @@
 block discarded – undo
23 23
  */
24 24
 class SourceMapGenerator
25 25
 {
26
-    /**
27
-     * What version of source map does the generator generate?
28
-     */
29
-    const VERSION = 3;
30
-
31
-    /**
32
-     * Array of default options
33
-     *
34
-     * @var array
35
-     */
36
-    protected $defaultOptions = array(
37
-        // an optional source root, useful for relocating source files
38
-        // on a server or removing repeated values in the 'sources' entry.
39
-        // This value is prepended to the individual entries in the 'source' field.
40
-        'sourceRoot' => '',
41
-
42
-        // an optional name of the generated code that this source map is associated with.
43
-        'sourceMapFilename' => null,
44
-
45
-        // url of the map
46
-        'sourceMapURL' => null,
47
-
48
-        // absolute path to a file to write the map to
49
-        'sourceMapWriteTo' => null,
50
-
51
-        // output source contents?
52
-        'outputSourceFiles' => false,
53
-
54
-        // base path for filename normalization
55
-        'sourceMapRootpath' => '',
56
-
57
-        // base path for filename normalization
58
-        'sourceMapBasepath' => ''
59
-    );
60
-
61
-    /**
62
-     * The base64 VLQ encoder
63
-     *
64
-     * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder
65
-     */
66
-    protected $encoder;
67
-
68
-    /**
69
-     * Array of mappings
70
-     *
71
-     * @var array
72
-     */
73
-    protected $mappings = array();
74
-
75
-    /**
76
-     * Array of contents map
77
-     *
78
-     * @var array
79
-     */
80
-    protected $contentsMap = array();
81
-
82
-    /**
83
-     * File to content map
84
-     *
85
-     * @var array
86
-     */
87
-    protected $sources = array();
88
-    protected $source_keys = array();
89
-
90
-    /**
91
-     * @var array
92
-     */
93
-    private $options;
94
-
95
-    public function __construct(array $options = [])
96
-    {
97
-        $this->options = array_merge($this->defaultOptions, $options);
98
-        $this->encoder = new Base64VLQEncoder();
99
-    }
100
-
101
-    /**
102
-     * Adds a mapping
103
-     *
104
-     * @param integer $generatedLine   The line number in generated file
105
-     * @param integer $generatedColumn The column number in generated file
106
-     * @param integer $originalLine    The line number in original file
107
-     * @param integer $originalColumn  The column number in original file
108
-     * @param string  $sourceFile      The original source file
109
-     */
110
-    public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
111
-    {
112
-        $this->mappings[] = array(
113
-            'generated_line' => $generatedLine,
114
-            'generated_column' => $generatedColumn,
115
-            'original_line' => $originalLine,
116
-            'original_column' => $originalColumn,
117
-            'source_file' => $sourceFile
118
-        );
119
-
120
-        $this->sources[$sourceFile] = $sourceFile;
121
-    }
122
-
123
-    /**
124
-     * Saves the source map to a file
125
-     *
126
-     * @param string $file    The absolute path to a file
127
-     * @param string $content The content to write
128
-     *
129
-     * @throws \Leafo\ScssPhp\Exception\CompilerException If the file could not be saved
130
-     */
131
-    public function saveMap($content)
132
-    {
133
-        $file = $this->options['sourceMapWriteTo'];
134
-        $dir  = dirname($file);
135
-
136
-        // directory does not exist
137
-        if (! is_dir($dir)) {
138
-            // FIXME: create the dir automatically?
139
-            throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
140
-        }
141
-
142
-        // FIXME: proper saving, with dir write check!
143
-        if (file_put_contents($file, $content) === false) {
144
-            throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file));
145
-        }
146
-
147
-        return $this->options['sourceMapURL'];
148
-    }
149
-
150
-    /**
151
-     * Generates the JSON source map
152
-     *
153
-     * @return string
154
-     *
155
-     * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
156
-     */
157
-    public function generateJson()
158
-    {
159
-        $sourceMap = array();
160
-        $mappings  = $this->generateMappings();
161
-
162
-        // File version (always the first entry in the object) and must be a positive integer.
163
-        $sourceMap['version'] = self::VERSION;
164
-
165
-        // An optional name of the generated code that this source map is associated with.
166
-        $file = $this->options['sourceMapFilename'];
167
-
168
-        if ($file) {
169
-            $sourceMap['file'] = $file;
170
-        }
171
-
172
-        // An optional source root, useful for relocating source files on a server or removing repeated values in the
173
-        // 'sources' entry. This value is prepended to the individual entries in the 'source' field.
174
-        $root = $this->options['sourceRoot'];
175
-
176
-        if ($root) {
177
-            $sourceMap['sourceRoot'] = $root;
178
-        }
179
-
180
-        // A list of original sources used by the 'mappings' entry.
181
-        $sourceMap['sources'] = array();
182
-
183
-        foreach ($this->sources as $source_uri => $source_filename) {
184
-            $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
185
-        }
186
-
187
-        // A list of symbol names used by the 'mappings' entry.
188
-        $sourceMap['names'] = array();
189
-
190
-        // A string with the encoded mapping data.
191
-        $sourceMap['mappings'] = $mappings;
192
-
193
-        if ($this->options['outputSourceFiles']) {
194
-            // An optional list of source content, useful when the 'source' can't be hosted.
195
-            // The contents are listed in the same order as the sources above.
196
-            // 'null' may be used if some original sources should be retrieved by name.
197
-            $sourceMap['sourcesContent'] = $this->getSourcesContent();
198
-        }
199
-
200
-        // less.js compat fixes
201
-        if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) {
202
-            unset($sourceMap['sourceRoot']);
203
-        }
204
-
205
-        return json_encode($sourceMap);
206
-    }
207
-
208
-    /**
209
-     * Returns the sources contents
210
-     *
211
-     * @return array|null
212
-     */
213
-    protected function getSourcesContent()
214
-    {
215
-        if (empty($this->sources)) {
216
-            return null;
217
-        }
218
-
219
-        $content = array();
220
-
221
-        foreach ($this->sources as $sourceFile) {
222
-            $content[] = file_get_contents($sourceFile);
223
-        }
224
-
225
-        return $content;
226
-    }
227
-
228
-    /**
229
-     * Generates the mappings string
230
-     *
231
-     * @return string
232
-     */
233
-    public function generateMappings()
234
-    {
235
-        if (! count($this->mappings)) {
236
-            return '';
237
-        }
238
-
239
-        $this->source_keys = array_flip(array_keys($this->sources));
240
-
241
-        // group mappings by generated line number.
242
-        $groupedMap = $groupedMapEncoded = array();
243
-
244
-        foreach ($this->mappings as $m) {
245
-            $groupedMap[$m['generated_line']][] = $m;
246
-        }
247
-
248
-        ksort($groupedMap);
249
-        $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
250
-
251
-        foreach ($groupedMap as $lineNumber => $line_map) {
252
-            while (++$lastGeneratedLine < $lineNumber) {
253
-                $groupedMapEncoded[] = ';';
254
-            }
255
-
256
-            $lineMapEncoded = array();
257
-            $lastGeneratedColumn = 0;
258
-
259
-            foreach ($line_map as $m) {
260
-                $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
261
-                $lastGeneratedColumn = $m['generated_column'];
262
-
263
-                // find the index
264
-                if ($m['source_file']) {
265
-                    $index = $this->findFileIndex($m['source_file']);
266
-
267
-                    if ($index !== false) {
268
-                        $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
269
-                        $lastOriginalIndex = $index;
270
-                        // lines are stored 0-based in SourceMap spec version 3
271
-                        $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
272
-                        $lastOriginalLine = $m['original_line'] - 1;
273
-                        $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
274
-                        $lastOriginalColumn = $m['original_column'];
275
-                    }
276
-                }
277
-
278
-                $lineMapEncoded[] = $mapEncoded;
279
-            }
280
-
281
-            $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
282
-        }
283
-
284
-        return rtrim(implode($groupedMapEncoded), ';');
285
-    }
286
-
287
-    /**
288
-     * Finds the index for the filename
289
-     *
290
-     * @param string $filename
291
-     *
292
-     * @return integer|false
293
-     */
294
-    protected function findFileIndex($filename)
295
-    {
296
-        return $this->source_keys[$filename];
297
-    }
298
-
299
-    protected function normalizeFilename($filename)
300
-    {
301
-        $filename = $this->fixWindowsPath($filename);
302
-        $rootpath = $this->options['sourceMapRootpath'];
303
-        $basePath = $this->options['sourceMapBasepath'];
304
-
305
-        // "Trim" the 'sourceMapBasepath' from the output filename.
306
-        if (strpos($filename, $basePath) === 0) {
307
-            $filename = substr($filename, strlen($basePath));
308
-        }
309
-
310
-        // Remove extra leading path separators.
311
-        if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) {
312
-            $filename = substr($filename, 1);
313
-        }
314
-
315
-        return $rootpath . $filename;
316
-    }
317
-
318
-    /**
319
-     * Fix windows paths
320
-     *
321
-     * @param string  $path
322
-     * @param boolean $addEndSlash
323
-     *
324
-     * @return string
325
-     */
326
-    public function fixWindowsPath($path, $addEndSlash = false)
327
-    {
328
-        $slash = ($addEndSlash) ? '/' : '';
329
-
330
-        if (! empty($path)) {
331
-            $path = str_replace('\\', '/', $path);
332
-            $path = rtrim($path, '/') . $slash;
333
-        }
334
-
335
-        return $path;
336
-    }
26
+	/**
27
+	 * What version of source map does the generator generate?
28
+	 */
29
+	const VERSION = 3;
30
+
31
+	/**
32
+	 * Array of default options
33
+	 *
34
+	 * @var array
35
+	 */
36
+	protected $defaultOptions = array(
37
+		// an optional source root, useful for relocating source files
38
+		// on a server or removing repeated values in the 'sources' entry.
39
+		// This value is prepended to the individual entries in the 'source' field.
40
+		'sourceRoot' => '',
41
+
42
+		// an optional name of the generated code that this source map is associated with.
43
+		'sourceMapFilename' => null,
44
+
45
+		// url of the map
46
+		'sourceMapURL' => null,
47
+
48
+		// absolute path to a file to write the map to
49
+		'sourceMapWriteTo' => null,
50
+
51
+		// output source contents?
52
+		'outputSourceFiles' => false,
53
+
54
+		// base path for filename normalization
55
+		'sourceMapRootpath' => '',
56
+
57
+		// base path for filename normalization
58
+		'sourceMapBasepath' => ''
59
+	);
60
+
61
+	/**
62
+	 * The base64 VLQ encoder
63
+	 *
64
+	 * @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder
65
+	 */
66
+	protected $encoder;
67
+
68
+	/**
69
+	 * Array of mappings
70
+	 *
71
+	 * @var array
72
+	 */
73
+	protected $mappings = array();
74
+
75
+	/**
76
+	 * Array of contents map
77
+	 *
78
+	 * @var array
79
+	 */
80
+	protected $contentsMap = array();
81
+
82
+	/**
83
+	 * File to content map
84
+	 *
85
+	 * @var array
86
+	 */
87
+	protected $sources = array();
88
+	protected $source_keys = array();
89
+
90
+	/**
91
+	 * @var array
92
+	 */
93
+	private $options;
94
+
95
+	public function __construct(array $options = [])
96
+	{
97
+		$this->options = array_merge($this->defaultOptions, $options);
98
+		$this->encoder = new Base64VLQEncoder();
99
+	}
100
+
101
+	/**
102
+	 * Adds a mapping
103
+	 *
104
+	 * @param integer $generatedLine   The line number in generated file
105
+	 * @param integer $generatedColumn The column number in generated file
106
+	 * @param integer $originalLine    The line number in original file
107
+	 * @param integer $originalColumn  The column number in original file
108
+	 * @param string  $sourceFile      The original source file
109
+	 */
110
+	public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
111
+	{
112
+		$this->mappings[] = array(
113
+			'generated_line' => $generatedLine,
114
+			'generated_column' => $generatedColumn,
115
+			'original_line' => $originalLine,
116
+			'original_column' => $originalColumn,
117
+			'source_file' => $sourceFile
118
+		);
119
+
120
+		$this->sources[$sourceFile] = $sourceFile;
121
+	}
122
+
123
+	/**
124
+	 * Saves the source map to a file
125
+	 *
126
+	 * @param string $file    The absolute path to a file
127
+	 * @param string $content The content to write
128
+	 *
129
+	 * @throws \Leafo\ScssPhp\Exception\CompilerException If the file could not be saved
130
+	 */
131
+	public function saveMap($content)
132
+	{
133
+		$file = $this->options['sourceMapWriteTo'];
134
+		$dir  = dirname($file);
135
+
136
+		// directory does not exist
137
+		if (! is_dir($dir)) {
138
+			// FIXME: create the dir automatically?
139
+			throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
140
+		}
141
+
142
+		// FIXME: proper saving, with dir write check!
143
+		if (file_put_contents($file, $content) === false) {
144
+			throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file));
145
+		}
146
+
147
+		return $this->options['sourceMapURL'];
148
+	}
149
+
150
+	/**
151
+	 * Generates the JSON source map
152
+	 *
153
+	 * @return string
154
+	 *
155
+	 * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
156
+	 */
157
+	public function generateJson()
158
+	{
159
+		$sourceMap = array();
160
+		$mappings  = $this->generateMappings();
161
+
162
+		// File version (always the first entry in the object) and must be a positive integer.
163
+		$sourceMap['version'] = self::VERSION;
164
+
165
+		// An optional name of the generated code that this source map is associated with.
166
+		$file = $this->options['sourceMapFilename'];
167
+
168
+		if ($file) {
169
+			$sourceMap['file'] = $file;
170
+		}
171
+
172
+		// An optional source root, useful for relocating source files on a server or removing repeated values in the
173
+		// 'sources' entry. This value is prepended to the individual entries in the 'source' field.
174
+		$root = $this->options['sourceRoot'];
175
+
176
+		if ($root) {
177
+			$sourceMap['sourceRoot'] = $root;
178
+		}
179
+
180
+		// A list of original sources used by the 'mappings' entry.
181
+		$sourceMap['sources'] = array();
182
+
183
+		foreach ($this->sources as $source_uri => $source_filename) {
184
+			$sourceMap['sources'][] = $this->normalizeFilename($source_filename);
185
+		}
186
+
187
+		// A list of symbol names used by the 'mappings' entry.
188
+		$sourceMap['names'] = array();
189
+
190
+		// A string with the encoded mapping data.
191
+		$sourceMap['mappings'] = $mappings;
192
+
193
+		if ($this->options['outputSourceFiles']) {
194
+			// An optional list of source content, useful when the 'source' can't be hosted.
195
+			// The contents are listed in the same order as the sources above.
196
+			// 'null' may be used if some original sources should be retrieved by name.
197
+			$sourceMap['sourcesContent'] = $this->getSourcesContent();
198
+		}
199
+
200
+		// less.js compat fixes
201
+		if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) {
202
+			unset($sourceMap['sourceRoot']);
203
+		}
204
+
205
+		return json_encode($sourceMap);
206
+	}
207
+
208
+	/**
209
+	 * Returns the sources contents
210
+	 *
211
+	 * @return array|null
212
+	 */
213
+	protected function getSourcesContent()
214
+	{
215
+		if (empty($this->sources)) {
216
+			return null;
217
+		}
218
+
219
+		$content = array();
220
+
221
+		foreach ($this->sources as $sourceFile) {
222
+			$content[] = file_get_contents($sourceFile);
223
+		}
224
+
225
+		return $content;
226
+	}
227
+
228
+	/**
229
+	 * Generates the mappings string
230
+	 *
231
+	 * @return string
232
+	 */
233
+	public function generateMappings()
234
+	{
235
+		if (! count($this->mappings)) {
236
+			return '';
237
+		}
238
+
239
+		$this->source_keys = array_flip(array_keys($this->sources));
240
+
241
+		// group mappings by generated line number.
242
+		$groupedMap = $groupedMapEncoded = array();
243
+
244
+		foreach ($this->mappings as $m) {
245
+			$groupedMap[$m['generated_line']][] = $m;
246
+		}
247
+
248
+		ksort($groupedMap);
249
+		$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
250
+
251
+		foreach ($groupedMap as $lineNumber => $line_map) {
252
+			while (++$lastGeneratedLine < $lineNumber) {
253
+				$groupedMapEncoded[] = ';';
254
+			}
255
+
256
+			$lineMapEncoded = array();
257
+			$lastGeneratedColumn = 0;
258
+
259
+			foreach ($line_map as $m) {
260
+				$mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
261
+				$lastGeneratedColumn = $m['generated_column'];
262
+
263
+				// find the index
264
+				if ($m['source_file']) {
265
+					$index = $this->findFileIndex($m['source_file']);
266
+
267
+					if ($index !== false) {
268
+						$mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
269
+						$lastOriginalIndex = $index;
270
+						// lines are stored 0-based in SourceMap spec version 3
271
+						$mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
272
+						$lastOriginalLine = $m['original_line'] - 1;
273
+						$mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
274
+						$lastOriginalColumn = $m['original_column'];
275
+					}
276
+				}
277
+
278
+				$lineMapEncoded[] = $mapEncoded;
279
+			}
280
+
281
+			$groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
282
+		}
283
+
284
+		return rtrim(implode($groupedMapEncoded), ';');
285
+	}
286
+
287
+	/**
288
+	 * Finds the index for the filename
289
+	 *
290
+	 * @param string $filename
291
+	 *
292
+	 * @return integer|false
293
+	 */
294
+	protected function findFileIndex($filename)
295
+	{
296
+		return $this->source_keys[$filename];
297
+	}
298
+
299
+	protected function normalizeFilename($filename)
300
+	{
301
+		$filename = $this->fixWindowsPath($filename);
302
+		$rootpath = $this->options['sourceMapRootpath'];
303
+		$basePath = $this->options['sourceMapBasepath'];
304
+
305
+		// "Trim" the 'sourceMapBasepath' from the output filename.
306
+		if (strpos($filename, $basePath) === 0) {
307
+			$filename = substr($filename, strlen($basePath));
308
+		}
309
+
310
+		// Remove extra leading path separators.
311
+		if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) {
312
+			$filename = substr($filename, 1);
313
+		}
314
+
315
+		return $rootpath . $filename;
316
+	}
317
+
318
+	/**
319
+	 * Fix windows paths
320
+	 *
321
+	 * @param string  $path
322
+	 * @param boolean $addEndSlash
323
+	 *
324
+	 * @return string
325
+	 */
326
+	public function fixWindowsPath($path, $addEndSlash = false)
327
+	{
328
+		$slash = ($addEndSlash) ? '/' : '';
329
+
330
+		if (! empty($path)) {
331
+			$path = str_replace('\\', '/', $path);
332
+			$path = rtrim($path, '/') . $slash;
333
+		}
334
+
335
+		return $path;
336
+	}
337 337
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/Type.php 1 patch
Indentation   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -18,52 +18,52 @@
 block discarded – undo
18 18
  */
19 19
 class Type
20 20
 {
21
-    const T_ASSIGN = 'assign';
22
-    const T_AT_ROOT = 'at-root';
23
-    const T_BLOCK = 'block';
24
-    const T_BREAK = 'break';
25
-    const T_CHARSET = 'charset';
26
-    const T_COLOR = 'color';
27
-    const T_COMMENT = 'comment';
28
-    const T_CONTINUE = 'continue';
29
-    const T_CONTROL = 'control';
30
-    const T_DEBUG = 'debug';
31
-    const T_DIRECTIVE = 'directive';
32
-    const T_EACH = 'each';
33
-    const T_ELSE = 'else';
34
-    const T_ELSEIF = 'elseif';
35
-    const T_ERROR = 'error';
36
-    const T_EXPRESSION = 'exp';
37
-    const T_EXTEND = 'extend';
38
-    const T_FOR = 'for';
39
-    const T_FUNCTION = 'function';
40
-    const T_FUNCTION_CALL = 'fncall';
41
-    const T_HSL = 'hsl';
42
-    const T_IF = 'if';
43
-    const T_IMPORT = 'import';
44
-    const T_INCLUDE = 'include';
45
-    const T_INTERPOLATE = 'interpolate';
46
-    const T_INTERPOLATED = 'interpolated';
47
-    const T_KEYWORD = 'keyword';
48
-    const T_LIST = 'list';
49
-    const T_MAP = 'map';
50
-    const T_MEDIA = 'media';
51
-    const T_MEDIA_EXPRESSION = 'mediaExp';
52
-    const T_MEDIA_TYPE = 'mediaType';
53
-    const T_MEDIA_VALUE = 'mediaValue';
54
-    const T_MIXIN = 'mixin';
55
-    const T_MIXIN_CONTENT = 'mixin_content';
56
-    const T_NESTED_PROPERTY = 'nestedprop';
57
-    const T_NOT = 'not';
58
-    const T_NULL = 'null';
59
-    const T_NUMBER = 'number';
60
-    const T_RETURN = 'return';
61
-    const T_ROOT = 'root';
62
-    const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
63
-    const T_SELF = 'self';
64
-    const T_STRING = 'string';
65
-    const T_UNARY = 'unary';
66
-    const T_VARIABLE = 'var';
67
-    const T_WARN = 'warn';
68
-    const T_WHILE = 'while';
21
+	const T_ASSIGN = 'assign';
22
+	const T_AT_ROOT = 'at-root';
23
+	const T_BLOCK = 'block';
24
+	const T_BREAK = 'break';
25
+	const T_CHARSET = 'charset';
26
+	const T_COLOR = 'color';
27
+	const T_COMMENT = 'comment';
28
+	const T_CONTINUE = 'continue';
29
+	const T_CONTROL = 'control';
30
+	const T_DEBUG = 'debug';
31
+	const T_DIRECTIVE = 'directive';
32
+	const T_EACH = 'each';
33
+	const T_ELSE = 'else';
34
+	const T_ELSEIF = 'elseif';
35
+	const T_ERROR = 'error';
36
+	const T_EXPRESSION = 'exp';
37
+	const T_EXTEND = 'extend';
38
+	const T_FOR = 'for';
39
+	const T_FUNCTION = 'function';
40
+	const T_FUNCTION_CALL = 'fncall';
41
+	const T_HSL = 'hsl';
42
+	const T_IF = 'if';
43
+	const T_IMPORT = 'import';
44
+	const T_INCLUDE = 'include';
45
+	const T_INTERPOLATE = 'interpolate';
46
+	const T_INTERPOLATED = 'interpolated';
47
+	const T_KEYWORD = 'keyword';
48
+	const T_LIST = 'list';
49
+	const T_MAP = 'map';
50
+	const T_MEDIA = 'media';
51
+	const T_MEDIA_EXPRESSION = 'mediaExp';
52
+	const T_MEDIA_TYPE = 'mediaType';
53
+	const T_MEDIA_VALUE = 'mediaValue';
54
+	const T_MIXIN = 'mixin';
55
+	const T_MIXIN_CONTENT = 'mixin_content';
56
+	const T_NESTED_PROPERTY = 'nestedprop';
57
+	const T_NOT = 'not';
58
+	const T_NULL = 'null';
59
+	const T_NUMBER = 'number';
60
+	const T_RETURN = 'return';
61
+	const T_ROOT = 'root';
62
+	const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
63
+	const T_SELF = 'self';
64
+	const T_STRING = 'string';
65
+	const T_UNARY = 'unary';
66
+	const T_VARIABLE = 'var';
67
+	const T_WARN = 'warn';
68
+	const T_WHILE = 'while';
69 69
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/Version.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -18,5 +18,5 @@
 block discarded – undo
18 18
  */
19 19
 class Version
20 20
 {
21
-    const VERSION = 'v0.7.5';
21
+	const VERSION = 'v0.7.5';
22 22
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/Colors.php 1 patch
Indentation   +158 added lines, -158 removed lines patch added patch discarded remove patch
@@ -18,162 +18,162 @@
 block discarded – undo
18 18
  */
19 19
 class Colors
20 20
 {
21
-    /**
22
-     * CSS Colors
23
-     *
24
-     * @see http://www.w3.org/TR/css3-color
25
-     *
26
-     * @var array
27
-     */
28
-    public static $cssColors = [
29
-        'aliceblue' => '240,248,255',
30
-        'antiquewhite' => '250,235,215',
31
-        'aqua' => '0,255,255',
32
-        'aquamarine' => '127,255,212',
33
-        'azure' => '240,255,255',
34
-        'beige' => '245,245,220',
35
-        'bisque' => '255,228,196',
36
-        'black' => '0,0,0',
37
-        'blanchedalmond' => '255,235,205',
38
-        'blue' => '0,0,255',
39
-        'blueviolet' => '138,43,226',
40
-        'brown' => '165,42,42',
41
-        'burlywood' => '222,184,135',
42
-        'cadetblue' => '95,158,160',
43
-        'chartreuse' => '127,255,0',
44
-        'chocolate' => '210,105,30',
45
-        'coral' => '255,127,80',
46
-        'cornflowerblue' => '100,149,237',
47
-        'cornsilk' => '255,248,220',
48
-        'crimson' => '220,20,60',
49
-        'cyan' => '0,255,255',
50
-        'darkblue' => '0,0,139',
51
-        'darkcyan' => '0,139,139',
52
-        'darkgoldenrod' => '184,134,11',
53
-        'darkgray' => '169,169,169',
54
-        'darkgreen' => '0,100,0',
55
-        'darkgrey' => '169,169,169',
56
-        'darkkhaki' => '189,183,107',
57
-        'darkmagenta' => '139,0,139',
58
-        'darkolivegreen' => '85,107,47',
59
-        'darkorange' => '255,140,0',
60
-        'darkorchid' => '153,50,204',
61
-        'darkred' => '139,0,0',
62
-        'darksalmon' => '233,150,122',
63
-        'darkseagreen' => '143,188,143',
64
-        'darkslateblue' => '72,61,139',
65
-        'darkslategray' => '47,79,79',
66
-        'darkslategrey' => '47,79,79',
67
-        'darkturquoise' => '0,206,209',
68
-        'darkviolet' => '148,0,211',
69
-        'deeppink' => '255,20,147',
70
-        'deepskyblue' => '0,191,255',
71
-        'dimgray' => '105,105,105',
72
-        'dimgrey' => '105,105,105',
73
-        'dodgerblue' => '30,144,255',
74
-        'firebrick' => '178,34,34',
75
-        'floralwhite' => '255,250,240',
76
-        'forestgreen' => '34,139,34',
77
-        'fuchsia' => '255,0,255',
78
-        'gainsboro' => '220,220,220',
79
-        'ghostwhite' => '248,248,255',
80
-        'gold' => '255,215,0',
81
-        'goldenrod' => '218,165,32',
82
-        'gray' => '128,128,128',
83
-        'green' => '0,128,0',
84
-        'greenyellow' => '173,255,47',
85
-        'grey' => '128,128,128',
86
-        'honeydew' => '240,255,240',
87
-        'hotpink' => '255,105,180',
88
-        'indianred' => '205,92,92',
89
-        'indigo' => '75,0,130',
90
-        'ivory' => '255,255,240',
91
-        'khaki' => '240,230,140',
92
-        'lavender' => '230,230,250',
93
-        'lavenderblush' => '255,240,245',
94
-        'lawngreen' => '124,252,0',
95
-        'lemonchiffon' => '255,250,205',
96
-        'lightblue' => '173,216,230',
97
-        'lightcoral' => '240,128,128',
98
-        'lightcyan' => '224,255,255',
99
-        'lightgoldenrodyellow' => '250,250,210',
100
-        'lightgray' => '211,211,211',
101
-        'lightgreen' => '144,238,144',
102
-        'lightgrey' => '211,211,211',
103
-        'lightpink' => '255,182,193',
104
-        'lightsalmon' => '255,160,122',
105
-        'lightseagreen' => '32,178,170',
106
-        'lightskyblue' => '135,206,250',
107
-        'lightslategray' => '119,136,153',
108
-        'lightslategrey' => '119,136,153',
109
-        'lightsteelblue' => '176,196,222',
110
-        'lightyellow' => '255,255,224',
111
-        'lime' => '0,255,0',
112
-        'limegreen' => '50,205,50',
113
-        'linen' => '250,240,230',
114
-        'magenta' => '255,0,255',
115
-        'maroon' => '128,0,0',
116
-        'mediumaquamarine' => '102,205,170',
117
-        'mediumblue' => '0,0,205',
118
-        'mediumorchid' => '186,85,211',
119
-        'mediumpurple' => '147,112,219',
120
-        'mediumseagreen' => '60,179,113',
121
-        'mediumslateblue' => '123,104,238',
122
-        'mediumspringgreen' => '0,250,154',
123
-        'mediumturquoise' => '72,209,204',
124
-        'mediumvioletred' => '199,21,133',
125
-        'midnightblue' => '25,25,112',
126
-        'mintcream' => '245,255,250',
127
-        'mistyrose' => '255,228,225',
128
-        'moccasin' => '255,228,181',
129
-        'navajowhite' => '255,222,173',
130
-        'navy' => '0,0,128',
131
-        'oldlace' => '253,245,230',
132
-        'olive' => '128,128,0',
133
-        'olivedrab' => '107,142,35',
134
-        'orange' => '255,165,0',
135
-        'orangered' => '255,69,0',
136
-        'orchid' => '218,112,214',
137
-        'palegoldenrod' => '238,232,170',
138
-        'palegreen' => '152,251,152',
139
-        'paleturquoise' => '175,238,238',
140
-        'palevioletred' => '219,112,147',
141
-        'papayawhip' => '255,239,213',
142
-        'peachpuff' => '255,218,185',
143
-        'peru' => '205,133,63',
144
-        'pink' => '255,192,203',
145
-        'plum' => '221,160,221',
146
-        'powderblue' => '176,224,230',
147
-        'purple' => '128,0,128',
148
-        'rebeccapurple' => '102,51,153',
149
-        'red' => '255,0,0',
150
-        'rosybrown' => '188,143,143',
151
-        'royalblue' => '65,105,225',
152
-        'saddlebrown' => '139,69,19',
153
-        'salmon' => '250,128,114',
154
-        'sandybrown' => '244,164,96',
155
-        'seagreen' => '46,139,87',
156
-        'seashell' => '255,245,238',
157
-        'sienna' => '160,82,45',
158
-        'silver' => '192,192,192',
159
-        'skyblue' => '135,206,235',
160
-        'slateblue' => '106,90,205',
161
-        'slategray' => '112,128,144',
162
-        'slategrey' => '112,128,144',
163
-        'snow' => '255,250,250',
164
-        'springgreen' => '0,255,127',
165
-        'steelblue' => '70,130,180',
166
-        'tan' => '210,180,140',
167
-        'teal' => '0,128,128',
168
-        'thistle' => '216,191,216',
169
-        'tomato' => '255,99,71',
170
-        'transparent' => '0,0,0,0',
171
-        'turquoise' => '64,224,208',
172
-        'violet' => '238,130,238',
173
-        'wheat' => '245,222,179',
174
-        'white' => '255,255,255',
175
-        'whitesmoke' => '245,245,245',
176
-        'yellow' => '255,255,0',
177
-        'yellowgreen' => '154,205,50',
178
-    ];
21
+	/**
22
+	 * CSS Colors
23
+	 *
24
+	 * @see http://www.w3.org/TR/css3-color
25
+	 *
26
+	 * @var array
27
+	 */
28
+	public static $cssColors = [
29
+		'aliceblue' => '240,248,255',
30
+		'antiquewhite' => '250,235,215',
31
+		'aqua' => '0,255,255',
32
+		'aquamarine' => '127,255,212',
33
+		'azure' => '240,255,255',
34
+		'beige' => '245,245,220',
35
+		'bisque' => '255,228,196',
36
+		'black' => '0,0,0',
37
+		'blanchedalmond' => '255,235,205',
38
+		'blue' => '0,0,255',
39
+		'blueviolet' => '138,43,226',
40
+		'brown' => '165,42,42',
41
+		'burlywood' => '222,184,135',
42
+		'cadetblue' => '95,158,160',
43
+		'chartreuse' => '127,255,0',
44
+		'chocolate' => '210,105,30',
45
+		'coral' => '255,127,80',
46
+		'cornflowerblue' => '100,149,237',
47
+		'cornsilk' => '255,248,220',
48
+		'crimson' => '220,20,60',
49
+		'cyan' => '0,255,255',
50
+		'darkblue' => '0,0,139',
51
+		'darkcyan' => '0,139,139',
52
+		'darkgoldenrod' => '184,134,11',
53
+		'darkgray' => '169,169,169',
54
+		'darkgreen' => '0,100,0',
55
+		'darkgrey' => '169,169,169',
56
+		'darkkhaki' => '189,183,107',
57
+		'darkmagenta' => '139,0,139',
58
+		'darkolivegreen' => '85,107,47',
59
+		'darkorange' => '255,140,0',
60
+		'darkorchid' => '153,50,204',
61
+		'darkred' => '139,0,0',
62
+		'darksalmon' => '233,150,122',
63
+		'darkseagreen' => '143,188,143',
64
+		'darkslateblue' => '72,61,139',
65
+		'darkslategray' => '47,79,79',
66
+		'darkslategrey' => '47,79,79',
67
+		'darkturquoise' => '0,206,209',
68
+		'darkviolet' => '148,0,211',
69
+		'deeppink' => '255,20,147',
70
+		'deepskyblue' => '0,191,255',
71
+		'dimgray' => '105,105,105',
72
+		'dimgrey' => '105,105,105',
73
+		'dodgerblue' => '30,144,255',
74
+		'firebrick' => '178,34,34',
75
+		'floralwhite' => '255,250,240',
76
+		'forestgreen' => '34,139,34',
77
+		'fuchsia' => '255,0,255',
78
+		'gainsboro' => '220,220,220',
79
+		'ghostwhite' => '248,248,255',
80
+		'gold' => '255,215,0',
81
+		'goldenrod' => '218,165,32',
82
+		'gray' => '128,128,128',
83
+		'green' => '0,128,0',
84
+		'greenyellow' => '173,255,47',
85
+		'grey' => '128,128,128',
86
+		'honeydew' => '240,255,240',
87
+		'hotpink' => '255,105,180',
88
+		'indianred' => '205,92,92',
89
+		'indigo' => '75,0,130',
90
+		'ivory' => '255,255,240',
91
+		'khaki' => '240,230,140',
92
+		'lavender' => '230,230,250',
93
+		'lavenderblush' => '255,240,245',
94
+		'lawngreen' => '124,252,0',
95
+		'lemonchiffon' => '255,250,205',
96
+		'lightblue' => '173,216,230',
97
+		'lightcoral' => '240,128,128',
98
+		'lightcyan' => '224,255,255',
99
+		'lightgoldenrodyellow' => '250,250,210',
100
+		'lightgray' => '211,211,211',
101
+		'lightgreen' => '144,238,144',
102
+		'lightgrey' => '211,211,211',
103
+		'lightpink' => '255,182,193',
104
+		'lightsalmon' => '255,160,122',
105
+		'lightseagreen' => '32,178,170',
106
+		'lightskyblue' => '135,206,250',
107
+		'lightslategray' => '119,136,153',
108
+		'lightslategrey' => '119,136,153',
109
+		'lightsteelblue' => '176,196,222',
110
+		'lightyellow' => '255,255,224',
111
+		'lime' => '0,255,0',
112
+		'limegreen' => '50,205,50',
113
+		'linen' => '250,240,230',
114
+		'magenta' => '255,0,255',
115
+		'maroon' => '128,0,0',
116
+		'mediumaquamarine' => '102,205,170',
117
+		'mediumblue' => '0,0,205',
118
+		'mediumorchid' => '186,85,211',
119
+		'mediumpurple' => '147,112,219',
120
+		'mediumseagreen' => '60,179,113',
121
+		'mediumslateblue' => '123,104,238',
122
+		'mediumspringgreen' => '0,250,154',
123
+		'mediumturquoise' => '72,209,204',
124
+		'mediumvioletred' => '199,21,133',
125
+		'midnightblue' => '25,25,112',
126
+		'mintcream' => '245,255,250',
127
+		'mistyrose' => '255,228,225',
128
+		'moccasin' => '255,228,181',
129
+		'navajowhite' => '255,222,173',
130
+		'navy' => '0,0,128',
131
+		'oldlace' => '253,245,230',
132
+		'olive' => '128,128,0',
133
+		'olivedrab' => '107,142,35',
134
+		'orange' => '255,165,0',
135
+		'orangered' => '255,69,0',
136
+		'orchid' => '218,112,214',
137
+		'palegoldenrod' => '238,232,170',
138
+		'palegreen' => '152,251,152',
139
+		'paleturquoise' => '175,238,238',
140
+		'palevioletred' => '219,112,147',
141
+		'papayawhip' => '255,239,213',
142
+		'peachpuff' => '255,218,185',
143
+		'peru' => '205,133,63',
144
+		'pink' => '255,192,203',
145
+		'plum' => '221,160,221',
146
+		'powderblue' => '176,224,230',
147
+		'purple' => '128,0,128',
148
+		'rebeccapurple' => '102,51,153',
149
+		'red' => '255,0,0',
150
+		'rosybrown' => '188,143,143',
151
+		'royalblue' => '65,105,225',
152
+		'saddlebrown' => '139,69,19',
153
+		'salmon' => '250,128,114',
154
+		'sandybrown' => '244,164,96',
155
+		'seagreen' => '46,139,87',
156
+		'seashell' => '255,245,238',
157
+		'sienna' => '160,82,45',
158
+		'silver' => '192,192,192',
159
+		'skyblue' => '135,206,235',
160
+		'slateblue' => '106,90,205',
161
+		'slategray' => '112,128,144',
162
+		'slategrey' => '112,128,144',
163
+		'snow' => '255,250,250',
164
+		'springgreen' => '0,255,127',
165
+		'steelblue' => '70,130,180',
166
+		'tan' => '210,180,140',
167
+		'teal' => '0,128,128',
168
+		'thistle' => '216,191,216',
169
+		'tomato' => '255,99,71',
170
+		'transparent' => '0,0,0,0',
171
+		'turquoise' => '64,224,208',
172
+		'violet' => '238,130,238',
173
+		'wheat' => '245,222,179',
174
+		'white' => '255,255,255',
175
+		'whitesmoke' => '245,245,245',
176
+		'yellow' => '255,255,0',
177
+		'yellowgreen' => '154,205,50',
178
+	];
179 179
 }
Please login to merge, or discard this patch.
vendor/leafo/scssphp/src/Parser.php 1 patch
Indentation   +2420 added lines, -2420 removed lines patch added patch discarded remove patch
@@ -24,2471 +24,2471 @@
 block discarded – undo
24 24
  */
25 25
 class Parser
26 26
 {
27
-    const SOURCE_INDEX  = -1;
28
-    const SOURCE_LINE   = -2;
29
-    const SOURCE_COLUMN = -3;
30
-
31
-    /**
32
-     * @var array
33
-     */
34
-    protected static $precedence = [
35
-        '='   => 0,
36
-        'or'  => 1,
37
-        'and' => 2,
38
-        '=='  => 3,
39
-        '!='  => 3,
40
-        '<=>' => 3,
41
-        '<='  => 4,
42
-        '>='  => 4,
43
-        '<'   => 4,
44
-        '>'   => 4,
45
-        '+'   => 5,
46
-        '-'   => 5,
47
-        '*'   => 6,
48
-        '/'   => 6,
49
-        '%'   => 6,
50
-    ];
51
-
52
-    protected static $commentPattern;
53
-    protected static $operatorPattern;
54
-    protected static $whitePattern;
55
-
56
-    private $sourceName;
57
-    private $sourceIndex;
58
-    private $sourcePositions;
59
-    private $charset;
60
-    private $count;
61
-    private $env;
62
-    private $inParens;
63
-    private $eatWhiteDefault;
64
-    private $buffer;
65
-    private $utf8;
66
-    private $encoding;
67
-    private $patternModifiers;
68
-
69
-    /**
70
-     * Constructor
71
-     *
72
-     * @api
73
-     *
74
-     * @param string  $sourceName
75
-     * @param integer $sourceIndex
76
-     * @param string  $encoding
77
-     */
78
-    public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8')
79
-    {
80
-        $this->sourceName       = $sourceName ?: '(stdin)';
81
-        $this->sourceIndex      = $sourceIndex;
82
-        $this->charset          = null;
83
-        $this->utf8             = ! $encoding || strtolower($encoding) === 'utf-8';
84
-        $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
85
-
86
-        if (empty(static::$operatorPattern)) {
87
-            static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)';
88
-
89
-            $commentSingle      = '\/\/';
90
-            $commentMultiLeft   = '\/\*';
91
-            $commentMultiRight  = '\*\/';
92
-
93
-            static::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight;
94
-            static::$whitePattern = $this->utf8
95
-                ? '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisuS'
96
-                : '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS';
97
-        }
98
-    }
99
-
100
-    /**
101
-     * Get source file name
102
-     *
103
-     * @api
104
-     *
105
-     * @return string
106
-     */
107
-    public function getSourceName()
108
-    {
109
-        return $this->sourceName;
110
-    }
111
-
112
-    /**
113
-     * Throw parser error
114
-     *
115
-     * @api
116
-     *
117
-     * @param string $msg
118
-     *
119
-     * @throws \Leafo\ScssPhp\Exception\ParserException
120
-     */
121
-    public function throwParseError($msg = 'parse error')
122
-    {
123
-        list($line, /* $column */) = $this->getSourcePosition($this->count);
124
-
125
-        $loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line";
126
-
127
-        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
128
-            throw new ParserException("$msg: failed at `$m[1]` $loc");
129
-        }
130
-
131
-        throw new ParserException("$msg: $loc");
132
-    }
133
-
134
-    /**
135
-     * Parser buffer
136
-     *
137
-     * @api
138
-     *
139
-     * @param string $buffer
140
-     *
141
-     * @return \Leafo\ScssPhp\Block
142
-     */
143
-    public function parse($buffer)
144
-    {
145
-        // strip BOM (byte order marker)
146
-        if (substr($buffer, 0, 3) === "\xef\xbb\xbf") {
147
-            $buffer = substr($buffer, 3);
148
-        }
149
-
150
-        $this->buffer          = rtrim($buffer, "\x00..\x1f");
151
-        $this->count           = 0;
152
-        $this->env             = null;
153
-        $this->inParens        = false;
154
-        $this->eatWhiteDefault = true;
155
-
156
-        $this->saveEncoding();
157
-        $this->extractLineNumbers($buffer);
158
-
159
-        $this->pushBlock(null); // root block
160
-        $this->whitespace();
161
-        $this->pushBlock(null);
162
-        $this->popBlock();
163
-
164
-        while ($this->parseChunk()) {
165
-            ;
166
-        }
167
-
168
-        if ($this->count !== strlen($this->buffer)) {
169
-            $this->throwParseError();
170
-        }
171
-
172
-        if (! empty($this->env->parent)) {
173
-            $this->throwParseError('unclosed block');
174
-        }
175
-
176
-        if ($this->charset) {
177
-            array_unshift($this->env->children, $this->charset);
178
-        }
179
-
180
-        $this->env->isRoot    = true;
181
-
182
-        $this->restoreEncoding();
183
-
184
-        return $this->env;
185
-    }
186
-
187
-    /**
188
-     * Parse a value or value list
189
-     *
190
-     * @api
191
-     *
192
-     * @param string $buffer
193
-     * @param string $out
194
-     *
195
-     * @return boolean
196
-     */
197
-    public function parseValue($buffer, &$out)
198
-    {
199
-        $this->count           = 0;
200
-        $this->env             = null;
201
-        $this->inParens        = false;
202
-        $this->eatWhiteDefault = true;
203
-        $this->buffer          = (string) $buffer;
204
-
205
-        $this->saveEncoding();
206
-
207
-        $list = $this->valueList($out);
208
-
209
-        $this->restoreEncoding();
210
-
211
-        return $list;
212
-    }
213
-
214
-    /**
215
-     * Parse a selector or selector list
216
-     *
217
-     * @api
218
-     *
219
-     * @param string $buffer
220
-     * @param string $out
221
-     *
222
-     * @return boolean
223
-     */
224
-    public function parseSelector($buffer, &$out)
225
-    {
226
-        $this->count           = 0;
227
-        $this->env             = null;
228
-        $this->inParens        = false;
229
-        $this->eatWhiteDefault = true;
230
-        $this->buffer          = (string) $buffer;
231
-
232
-        $this->saveEncoding();
233
-
234
-        $selector = $this->selectors($out);
235
-
236
-        $this->restoreEncoding();
237
-
238
-        return $selector;
239
-    }
240
-
241
-    /**
242
-     * Parse a single chunk off the head of the buffer and append it to the
243
-     * current parse environment.
244
-     *
245
-     * Returns false when the buffer is empty, or when there is an error.
246
-     *
247
-     * This function is called repeatedly until the entire document is
248
-     * parsed.
249
-     *
250
-     * This parser is most similar to a recursive descent parser. Single
251
-     * functions represent discrete grammatical rules for the language, and
252
-     * they are able to capture the text that represents those rules.
253
-     *
254
-     * Consider the function Compiler::keyword(). (All parse functions are
255
-     * structured the same.)
256
-     *
257
-     * The function takes a single reference argument. When calling the
258
-     * function it will attempt to match a keyword on the head of the buffer.
259
-     * If it is successful, it will place the keyword in the referenced
260
-     * argument, advance the position in the buffer, and return true. If it
261
-     * fails then it won't advance the buffer and it will return false.
262
-     *
263
-     * All of these parse functions are powered by Compiler::match(), which behaves
264
-     * the same way, but takes a literal regular expression. Sometimes it is
265
-     * more convenient to use match instead of creating a new function.
266
-     *
267
-     * Because of the format of the functions, to parse an entire string of
268
-     * grammatical rules, you can chain them together using &&.
269
-     *
270
-     * But, if some of the rules in the chain succeed before one fails, then
271
-     * the buffer position will be left at an invalid state. In order to
272
-     * avoid this, Compiler::seek() is used to remember and set buffer positions.
273
-     *
274
-     * Before parsing a chain, use $s = $this->seek() to remember the current
275
-     * position into $s. Then if a chain fails, use $this->seek($s) to
276
-     * go back where we started.
277
-     *
278
-     * @return boolean
279
-     */
280
-    protected function parseChunk()
281
-    {
282
-        $s = $this->seek();
283
-
284
-        // the directives
285
-        if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') {
286
-            if ($this->literal('@at-root') &&
287
-                ($this->selectors($selector) || true) &&
288
-                ($this->map($with) || true) &&
289
-                $this->literal('{')
290
-            ) {
291
-                $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
292
-                $atRoot->selector = $selector;
293
-                $atRoot->with = $with;
294
-
295
-                return true;
296
-            }
297
-
298
-            $this->seek($s);
299
-
300
-            if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) {
301
-                $media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
302
-                $media->queryList = $mediaQueryList[2];
303
-
304
-                return true;
305
-            }
306
-
307
-            $this->seek($s);
308
-
309
-            if ($this->literal('@mixin') &&
310
-                $this->keyword($mixinName) &&
311
-                ($this->argumentDef($args) || true) &&
312
-                $this->literal('{')
313
-            ) {
314
-                $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
315
-                $mixin->name = $mixinName;
316
-                $mixin->args = $args;
317
-
318
-                return true;
319
-            }
320
-
321
-            $this->seek($s);
322
-
323
-            if ($this->literal('@include') &&
324
-                $this->keyword($mixinName) &&
325
-                ($this->literal('(') &&
326
-                    ($this->argValues($argValues) || true) &&
327
-                    $this->literal(')') || true) &&
328
-                ($this->end() ||
329
-                    $this->literal('{') && $hasBlock = true)
330
-            ) {
331
-                $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null];
332
-
333
-                if (! empty($hasBlock)) {
334
-                    $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s);
335
-                    $include->child = $child;
336
-                } else {
337
-                    $this->append($child, $s);
338
-                }
339
-
340
-                return true;
341
-            }
342
-
343
-            $this->seek($s);
344
-
345
-            if ($this->literal('@scssphp-import-once') &&
346
-                $this->valueList($importPath) &&
347
-                $this->end()
348
-            ) {
349
-                $this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s);
350
-
351
-                return true;
352
-            }
353
-
354
-            $this->seek($s);
355
-
356
-            if ($this->literal('@import') &&
357
-                $this->valueList($importPath) &&
358
-                $this->end()
359
-            ) {
360
-                $this->append([Type::T_IMPORT, $importPath], $s);
361
-
362
-                return true;
363
-            }
364
-
365
-            $this->seek($s);
366
-
367
-            if ($this->literal('@import') &&
368
-                $this->url($importPath) &&
369
-                $this->end()
370
-            ) {
371
-                $this->append([Type::T_IMPORT, $importPath], $s);
372
-
373
-                return true;
374
-            }
375
-
376
-            $this->seek($s);
377
-
378
-            if ($this->literal('@extend') &&
379
-                $this->selectors($selectors) &&
380
-                $this->end()
381
-            ) {
382
-                // check for '!flag'
383
-                $optional = $this->stripOptionalFlag($selectors);
384
-                $this->append([Type::T_EXTEND, $selectors, $optional], $s);
27
+	const SOURCE_INDEX  = -1;
28
+	const SOURCE_LINE   = -2;
29
+	const SOURCE_COLUMN = -3;
30
+
31
+	/**
32
+	 * @var array
33
+	 */
34
+	protected static $precedence = [
35
+		'='   => 0,
36
+		'or'  => 1,
37
+		'and' => 2,
38
+		'=='  => 3,
39
+		'!='  => 3,
40
+		'<=>' => 3,
41
+		'<='  => 4,
42
+		'>='  => 4,
43
+		'<'   => 4,
44
+		'>'   => 4,
45
+		'+'   => 5,
46
+		'-'   => 5,
47
+		'*'   => 6,
48
+		'/'   => 6,
49
+		'%'   => 6,
50
+	];
51
+
52
+	protected static $commentPattern;
53
+	protected static $operatorPattern;
54
+	protected static $whitePattern;
55
+
56
+	private $sourceName;
57
+	private $sourceIndex;
58
+	private $sourcePositions;
59
+	private $charset;
60
+	private $count;
61
+	private $env;
62
+	private $inParens;
63
+	private $eatWhiteDefault;
64
+	private $buffer;
65
+	private $utf8;
66
+	private $encoding;
67
+	private $patternModifiers;
68
+
69
+	/**
70
+	 * Constructor
71
+	 *
72
+	 * @api
73
+	 *
74
+	 * @param string  $sourceName
75
+	 * @param integer $sourceIndex
76
+	 * @param string  $encoding
77
+	 */
78
+	public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8')
79
+	{
80
+		$this->sourceName       = $sourceName ?: '(stdin)';
81
+		$this->sourceIndex      = $sourceIndex;
82
+		$this->charset          = null;
83
+		$this->utf8             = ! $encoding || strtolower($encoding) === 'utf-8';
84
+		$this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais';
85
+
86
+		if (empty(static::$operatorPattern)) {
87
+			static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)';
88
+
89
+			$commentSingle      = '\/\/';
90
+			$commentMultiLeft   = '\/\*';
91
+			$commentMultiRight  = '\*\/';
92
+
93
+			static::$commentPattern = $commentMultiLeft . '.*?' . $commentMultiRight;
94
+			static::$whitePattern = $this->utf8
95
+				? '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisuS'
96
+				: '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS';
97
+		}
98
+	}
99
+
100
+	/**
101
+	 * Get source file name
102
+	 *
103
+	 * @api
104
+	 *
105
+	 * @return string
106
+	 */
107
+	public function getSourceName()
108
+	{
109
+		return $this->sourceName;
110
+	}
111
+
112
+	/**
113
+	 * Throw parser error
114
+	 *
115
+	 * @api
116
+	 *
117
+	 * @param string $msg
118
+	 *
119
+	 * @throws \Leafo\ScssPhp\Exception\ParserException
120
+	 */
121
+	public function throwParseError($msg = 'parse error')
122
+	{
123
+		list($line, /* $column */) = $this->getSourcePosition($this->count);
124
+
125
+		$loc = empty($this->sourceName) ? "line: $line" : "$this->sourceName on line $line";
126
+
127
+		if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
128
+			throw new ParserException("$msg: failed at `$m[1]` $loc");
129
+		}
130
+
131
+		throw new ParserException("$msg: $loc");
132
+	}
133
+
134
+	/**
135
+	 * Parser buffer
136
+	 *
137
+	 * @api
138
+	 *
139
+	 * @param string $buffer
140
+	 *
141
+	 * @return \Leafo\ScssPhp\Block
142
+	 */
143
+	public function parse($buffer)
144
+	{
145
+		// strip BOM (byte order marker)
146
+		if (substr($buffer, 0, 3) === "\xef\xbb\xbf") {
147
+			$buffer = substr($buffer, 3);
148
+		}
149
+
150
+		$this->buffer          = rtrim($buffer, "\x00..\x1f");
151
+		$this->count           = 0;
152
+		$this->env             = null;
153
+		$this->inParens        = false;
154
+		$this->eatWhiteDefault = true;
155
+
156
+		$this->saveEncoding();
157
+		$this->extractLineNumbers($buffer);
158
+
159
+		$this->pushBlock(null); // root block
160
+		$this->whitespace();
161
+		$this->pushBlock(null);
162
+		$this->popBlock();
163
+
164
+		while ($this->parseChunk()) {
165
+			;
166
+		}
167
+
168
+		if ($this->count !== strlen($this->buffer)) {
169
+			$this->throwParseError();
170
+		}
171
+
172
+		if (! empty($this->env->parent)) {
173
+			$this->throwParseError('unclosed block');
174
+		}
175
+
176
+		if ($this->charset) {
177
+			array_unshift($this->env->children, $this->charset);
178
+		}
179
+
180
+		$this->env->isRoot    = true;
181
+
182
+		$this->restoreEncoding();
183
+
184
+		return $this->env;
185
+	}
186
+
187
+	/**
188
+	 * Parse a value or value list
189
+	 *
190
+	 * @api
191
+	 *
192
+	 * @param string $buffer
193
+	 * @param string $out
194
+	 *
195
+	 * @return boolean
196
+	 */
197
+	public function parseValue($buffer, &$out)
198
+	{
199
+		$this->count           = 0;
200
+		$this->env             = null;
201
+		$this->inParens        = false;
202
+		$this->eatWhiteDefault = true;
203
+		$this->buffer          = (string) $buffer;
204
+
205
+		$this->saveEncoding();
206
+
207
+		$list = $this->valueList($out);
208
+
209
+		$this->restoreEncoding();
210
+
211
+		return $list;
212
+	}
213
+
214
+	/**
215
+	 * Parse a selector or selector list
216
+	 *
217
+	 * @api
218
+	 *
219
+	 * @param string $buffer
220
+	 * @param string $out
221
+	 *
222
+	 * @return boolean
223
+	 */
224
+	public function parseSelector($buffer, &$out)
225
+	{
226
+		$this->count           = 0;
227
+		$this->env             = null;
228
+		$this->inParens        = false;
229
+		$this->eatWhiteDefault = true;
230
+		$this->buffer          = (string) $buffer;
231
+
232
+		$this->saveEncoding();
233
+
234
+		$selector = $this->selectors($out);
235
+
236
+		$this->restoreEncoding();
237
+
238
+		return $selector;
239
+	}
240
+
241
+	/**
242
+	 * Parse a single chunk off the head of the buffer and append it to the
243
+	 * current parse environment.
244
+	 *
245
+	 * Returns false when the buffer is empty, or when there is an error.
246
+	 *
247
+	 * This function is called repeatedly until the entire document is
248
+	 * parsed.
249
+	 *
250
+	 * This parser is most similar to a recursive descent parser. Single
251
+	 * functions represent discrete grammatical rules for the language, and
252
+	 * they are able to capture the text that represents those rules.
253
+	 *
254
+	 * Consider the function Compiler::keyword(). (All parse functions are
255
+	 * structured the same.)
256
+	 *
257
+	 * The function takes a single reference argument. When calling the
258
+	 * function it will attempt to match a keyword on the head of the buffer.
259
+	 * If it is successful, it will place the keyword in the referenced
260
+	 * argument, advance the position in the buffer, and return true. If it
261
+	 * fails then it won't advance the buffer and it will return false.
262
+	 *
263
+	 * All of these parse functions are powered by Compiler::match(), which behaves
264
+	 * the same way, but takes a literal regular expression. Sometimes it is
265
+	 * more convenient to use match instead of creating a new function.
266
+	 *
267
+	 * Because of the format of the functions, to parse an entire string of
268
+	 * grammatical rules, you can chain them together using &&.
269
+	 *
270
+	 * But, if some of the rules in the chain succeed before one fails, then
271
+	 * the buffer position will be left at an invalid state. In order to
272
+	 * avoid this, Compiler::seek() is used to remember and set buffer positions.
273
+	 *
274
+	 * Before parsing a chain, use $s = $this->seek() to remember the current
275
+	 * position into $s. Then if a chain fails, use $this->seek($s) to
276
+	 * go back where we started.
277
+	 *
278
+	 * @return boolean
279
+	 */
280
+	protected function parseChunk()
281
+	{
282
+		$s = $this->seek();
283
+
284
+		// the directives
285
+		if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') {
286
+			if ($this->literal('@at-root') &&
287
+				($this->selectors($selector) || true) &&
288
+				($this->map($with) || true) &&
289
+				$this->literal('{')
290
+			) {
291
+				$atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
292
+				$atRoot->selector = $selector;
293
+				$atRoot->with = $with;
294
+
295
+				return true;
296
+			}
297
+
298
+			$this->seek($s);
299
+
300
+			if ($this->literal('@media') && $this->mediaQueryList($mediaQueryList) && $this->literal('{')) {
301
+				$media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
302
+				$media->queryList = $mediaQueryList[2];
303
+
304
+				return true;
305
+			}
306
+
307
+			$this->seek($s);
308
+
309
+			if ($this->literal('@mixin') &&
310
+				$this->keyword($mixinName) &&
311
+				($this->argumentDef($args) || true) &&
312
+				$this->literal('{')
313
+			) {
314
+				$mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
315
+				$mixin->name = $mixinName;
316
+				$mixin->args = $args;
317
+
318
+				return true;
319
+			}
320
+
321
+			$this->seek($s);
322
+
323
+			if ($this->literal('@include') &&
324
+				$this->keyword($mixinName) &&
325
+				($this->literal('(') &&
326
+					($this->argValues($argValues) || true) &&
327
+					$this->literal(')') || true) &&
328
+				($this->end() ||
329
+					$this->literal('{') && $hasBlock = true)
330
+			) {
331
+				$child = [Type::T_INCLUDE, $mixinName, isset($argValues) ? $argValues : null, null];
332
+
333
+				if (! empty($hasBlock)) {
334
+					$include = $this->pushSpecialBlock(Type::T_INCLUDE, $s);
335
+					$include->child = $child;
336
+				} else {
337
+					$this->append($child, $s);
338
+				}
339
+
340
+				return true;
341
+			}
342
+
343
+			$this->seek($s);
344
+
345
+			if ($this->literal('@scssphp-import-once') &&
346
+				$this->valueList($importPath) &&
347
+				$this->end()
348
+			) {
349
+				$this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s);
350
+
351
+				return true;
352
+			}
353
+
354
+			$this->seek($s);
355
+
356
+			if ($this->literal('@import') &&
357
+				$this->valueList($importPath) &&
358
+				$this->end()
359
+			) {
360
+				$this->append([Type::T_IMPORT, $importPath], $s);
361
+
362
+				return true;
363
+			}
364
+
365
+			$this->seek($s);
366
+
367
+			if ($this->literal('@import') &&
368
+				$this->url($importPath) &&
369
+				$this->end()
370
+			) {
371
+				$this->append([Type::T_IMPORT, $importPath], $s);
372
+
373
+				return true;
374
+			}
375
+
376
+			$this->seek($s);
377
+
378
+			if ($this->literal('@extend') &&
379
+				$this->selectors($selectors) &&
380
+				$this->end()
381
+			) {
382
+				// check for '!flag'
383
+				$optional = $this->stripOptionalFlag($selectors);
384
+				$this->append([Type::T_EXTEND, $selectors, $optional], $s);
385 385
 
386
-                return true;
387
-            }
386
+				return true;
387
+			}
388 388
 
389
-            $this->seek($s);
389
+			$this->seek($s);
390 390
 
391
-            if ($this->literal('@function') &&
392
-                $this->keyword($fnName) &&
393
-                $this->argumentDef($args) &&
394
-                $this->literal('{')
395
-            ) {
396
-                $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
397
-                $func->name = $fnName;
398
-                $func->args = $args;
391
+			if ($this->literal('@function') &&
392
+				$this->keyword($fnName) &&
393
+				$this->argumentDef($args) &&
394
+				$this->literal('{')
395
+			) {
396
+				$func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
397
+				$func->name = $fnName;
398
+				$func->args = $args;
399 399
 
400
-                return true;
401
-            }
400
+				return true;
401
+			}
402 402
 
403
-            $this->seek($s);
403
+			$this->seek($s);
404 404
 
405
-            if ($this->literal('@break') && $this->end()) {
406
-                $this->append([Type::T_BREAK], $s);
405
+			if ($this->literal('@break') && $this->end()) {
406
+				$this->append([Type::T_BREAK], $s);
407 407
 
408
-                return true;
409
-            }
408
+				return true;
409
+			}
410 410
 
411
-            $this->seek($s);
411
+			$this->seek($s);
412 412
 
413
-            if ($this->literal('@continue') && $this->end()) {
414
-                $this->append([Type::T_CONTINUE], $s);
413
+			if ($this->literal('@continue') && $this->end()) {
414
+				$this->append([Type::T_CONTINUE], $s);
415 415
 
416
-                return true;
417
-            }
416
+				return true;
417
+			}
418 418
 
419
-            $this->seek($s);
419
+			$this->seek($s);
420 420
 
421 421
 
422
-            if ($this->literal('@return') && ($this->valueList($retVal) || true) && $this->end()) {
423
-                $this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s);
422
+			if ($this->literal('@return') && ($this->valueList($retVal) || true) && $this->end()) {
423
+				$this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s);
424 424
 
425
-                return true;
426
-            }
425
+				return true;
426
+			}
427 427
 
428
-            $this->seek($s);
428
+			$this->seek($s);
429 429
 
430
-            if ($this->literal('@each') &&
431
-                $this->genericList($varNames, 'variable', ',', false) &&
432
-                $this->literal('in') &&
433
-                $this->valueList($list) &&
434
-                $this->literal('{')
435
-            ) {
436
-                $each = $this->pushSpecialBlock(Type::T_EACH, $s);
430
+			if ($this->literal('@each') &&
431
+				$this->genericList($varNames, 'variable', ',', false) &&
432
+				$this->literal('in') &&
433
+				$this->valueList($list) &&
434
+				$this->literal('{')
435
+			) {
436
+				$each = $this->pushSpecialBlock(Type::T_EACH, $s);
437 437
 
438
-                foreach ($varNames[2] as $varName) {
439
-                    $each->vars[] = $varName[1];
440
-                }
438
+				foreach ($varNames[2] as $varName) {
439
+					$each->vars[] = $varName[1];
440
+				}
441 441
 
442
-                $each->list = $list;
442
+				$each->list = $list;
443 443
 
444
-                return true;
445
-            }
444
+				return true;
445
+			}
446 446
 
447
-            $this->seek($s);
447
+			$this->seek($s);
448 448
 
449
-            if ($this->literal('@while') &&
450
-                $this->expression($cond) &&
451
-                $this->literal('{')
452
-            ) {
453
-                $while = $this->pushSpecialBlock(Type::T_WHILE, $s);
454
-                $while->cond = $cond;
449
+			if ($this->literal('@while') &&
450
+				$this->expression($cond) &&
451
+				$this->literal('{')
452
+			) {
453
+				$while = $this->pushSpecialBlock(Type::T_WHILE, $s);
454
+				$while->cond = $cond;
455 455
 
456
-                return true;
457
-            }
456
+				return true;
457
+			}
458 458
 
459
-            $this->seek($s);
459
+			$this->seek($s);
460 460
 
461
-            if ($this->literal('@for') &&
462
-                $this->variable($varName) &&
463
-                $this->literal('from') &&
464
-                $this->expression($start) &&
465
-                ($this->literal('through') ||
466
-                    ($forUntil = true && $this->literal('to'))) &&
467
-                $this->expression($end) &&
468
-                $this->literal('{')
469
-            ) {
470
-                $for = $this->pushSpecialBlock(Type::T_FOR, $s);
471
-                $for->var = $varName[1];
472
-                $for->start = $start;
473
-                $for->end = $end;
474
-                $for->until = isset($forUntil);
461
+			if ($this->literal('@for') &&
462
+				$this->variable($varName) &&
463
+				$this->literal('from') &&
464
+				$this->expression($start) &&
465
+				($this->literal('through') ||
466
+					($forUntil = true && $this->literal('to'))) &&
467
+				$this->expression($end) &&
468
+				$this->literal('{')
469
+			) {
470
+				$for = $this->pushSpecialBlock(Type::T_FOR, $s);
471
+				$for->var = $varName[1];
472
+				$for->start = $start;
473
+				$for->end = $end;
474
+				$for->until = isset($forUntil);
475 475
 
476
-                return true;
477
-            }
476
+				return true;
477
+			}
478 478
 
479
-            $this->seek($s);
479
+			$this->seek($s);
480 480
 
481
-            if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) {
482
-                $if = $this->pushSpecialBlock(Type::T_IF, $s);
483
-                $if->cond = $cond;
484
-                $if->cases = [];
481
+			if ($this->literal('@if') && $this->valueList($cond) && $this->literal('{')) {
482
+				$if = $this->pushSpecialBlock(Type::T_IF, $s);
483
+				$if->cond = $cond;
484
+				$if->cases = [];
485 485
 
486
-                return true;
487
-            }
486
+				return true;
487
+			}
488 488
 
489
-            $this->seek($s);
489
+			$this->seek($s);
490 490
 
491
-            if ($this->literal('@debug') &&
492
-                $this->valueList($value) &&
493
-                $this->end()
494
-            ) {
495
-                $this->append([Type::T_DEBUG, $value], $s);
491
+			if ($this->literal('@debug') &&
492
+				$this->valueList($value) &&
493
+				$this->end()
494
+			) {
495
+				$this->append([Type::T_DEBUG, $value], $s);
496 496
 
497
-                return true;
498
-            }
497
+				return true;
498
+			}
499 499
 
500
-            $this->seek($s);
500
+			$this->seek($s);
501 501
 
502
-            if ($this->literal('@warn') &&
503
-                $this->valueList($value) &&
504
-                $this->end()
505
-            ) {
506
-                $this->append([Type::T_WARN, $value], $s);
502
+			if ($this->literal('@warn') &&
503
+				$this->valueList($value) &&
504
+				$this->end()
505
+			) {
506
+				$this->append([Type::T_WARN, $value], $s);
507 507
 
508
-                return true;
509
-            }
508
+				return true;
509
+			}
510 510
 
511
-            $this->seek($s);
511
+			$this->seek($s);
512 512
 
513
-            if ($this->literal('@error') &&
514
-                $this->valueList($value) &&
515
-                $this->end()
516
-            ) {
517
-                $this->append([Type::T_ERROR, $value], $s);
513
+			if ($this->literal('@error') &&
514
+				$this->valueList($value) &&
515
+				$this->end()
516
+			) {
517
+				$this->append([Type::T_ERROR, $value], $s);
518 518
 
519
-                return true;
520
-            }
519
+				return true;
520
+			}
521 521
 
522
-            $this->seek($s);
523
-
524
-            if ($this->literal('@content') && $this->end()) {
525
-                $this->append([Type::T_MIXIN_CONTENT], $s);
526
-
527
-                return true;
528
-            }
529
-
530
-            $this->seek($s);
531
-
532
-            $last = $this->last();
533
-
534
-            if (isset($last) && $last[0] === Type::T_IF) {
535
-                list(, $if) = $last;
536
-
537
-                if ($this->literal('@else')) {
538
-                    if ($this->literal('{')) {
539
-                        $else = $this->pushSpecialBlock(Type::T_ELSE, $s);
540
-                    } elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) {
541
-                        $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
542
-                        $else->cond = $cond;
543
-                    }
544
-
545
-                    if (isset($else)) {
546
-                        $else->dontAppend = true;
547
-                        $if->cases[] = $else;
548
-
549
-                        return true;
550
-                    }
551
-                }
552
-
553
-                $this->seek($s);
554
-            }
522
+			$this->seek($s);
523
+
524
+			if ($this->literal('@content') && $this->end()) {
525
+				$this->append([Type::T_MIXIN_CONTENT], $s);
526
+
527
+				return true;
528
+			}
529
+
530
+			$this->seek($s);
531
+
532
+			$last = $this->last();
533
+
534
+			if (isset($last) && $last[0] === Type::T_IF) {
535
+				list(, $if) = $last;
536
+
537
+				if ($this->literal('@else')) {
538
+					if ($this->literal('{')) {
539
+						$else = $this->pushSpecialBlock(Type::T_ELSE, $s);
540
+					} elseif ($this->literal('if') && $this->valueList($cond) && $this->literal('{')) {
541
+						$else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
542
+						$else->cond = $cond;
543
+					}
544
+
545
+					if (isset($else)) {
546
+						$else->dontAppend = true;
547
+						$if->cases[] = $else;
548
+
549
+						return true;
550
+					}
551
+				}
552
+
553
+				$this->seek($s);
554
+			}
555 555
 
556
-            // only retain the first @charset directive encountered
557
-            if ($this->literal('@charset') &&
558
-                $this->valueList($charset) &&
559
-                $this->end()
560
-            ) {
561
-                if (! isset($this->charset)) {
562
-                    $statement = [Type::T_CHARSET, $charset];
563
-
564
-                    list($line, $column) = $this->getSourcePosition($s);
565
-
566
-                    $statement[static::SOURCE_LINE]   = $line;
567
-                    $statement[static::SOURCE_COLUMN] = $column;
568
-                    $statement[static::SOURCE_INDEX]  = $this->sourceIndex;
569
-
570
-                    $this->charset = $statement;
571
-                }
556
+			// only retain the first @charset directive encountered
557
+			if ($this->literal('@charset') &&
558
+				$this->valueList($charset) &&
559
+				$this->end()
560
+			) {
561
+				if (! isset($this->charset)) {
562
+					$statement = [Type::T_CHARSET, $charset];
563
+
564
+					list($line, $column) = $this->getSourcePosition($s);
565
+
566
+					$statement[static::SOURCE_LINE]   = $line;
567
+					$statement[static::SOURCE_COLUMN] = $column;
568
+					$statement[static::SOURCE_INDEX]  = $this->sourceIndex;
569
+
570
+					$this->charset = $statement;
571
+				}
572 572
 
573
-                return true;
574
-            }
573
+				return true;
574
+			}
575 575
 
576
-            $this->seek($s);
576
+			$this->seek($s);
577 577
 
578
-            // doesn't match built in directive, do generic one
579
-            if ($this->literal('@', false) &&
580
-                $this->keyword($dirName) &&
581
-                ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) &&
582
-                $this->literal('{')
583
-            ) {
584
-                if ($dirName === 'media') {
585
-                    $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s);
586
-                } else {
587
-                    $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
588
-                    $directive->name = $dirName;
589
-                }
590
-
591
-                if (isset($dirValue)) {
592
-                    $directive->value = $dirValue;
593
-                }
594
-
595
-                return true;
596
-            }
597
-
598
-            $this->seek($s);
599
-
600
-            return false;
601
-        }
602
-
603
-        // property shortcut
604
-        // captures most properties before having to parse a selector
605
-        if ($this->keyword($name, false) &&
606
-            $this->literal(': ') &&
607
-            $this->valueList($value) &&
608
-            $this->end()
609
-        ) {
610
-            $name = [Type::T_STRING, '', [$name]];
611
-            $this->append([Type::T_ASSIGN, $name, $value], $s);
612
-
613
-            return true;
614
-        }
615
-
616
-        $this->seek($s);
617
-
618
-        // variable assigns
619
-        if ($this->variable($name) &&
620
-            $this->literal(':') &&
621
-            $this->valueList($value) &&
622
-            $this->end()
623
-        ) {
624
-            // check for '!flag'
625
-            $assignmentFlags = $this->stripAssignmentFlags($value);
626
-            $this->append([Type::T_ASSIGN, $name, $value, $assignmentFlags], $s);
627
-
628
-            return true;
629
-        }
630
-
631
-        $this->seek($s);
632
-
633
-        // misc
634
-        if ($this->literal('-->')) {
635
-            return true;
636
-        }
637
-
638
-        // opening css block
639
-        if ($this->selectors($selectors) && $this->literal('{')) {
640
-            $this->pushBlock($selectors, $s);
641
-
642
-            return true;
643
-        }
644
-
645
-        $this->seek($s);
646
-
647
-        // property assign, or nested assign
648
-        if ($this->propertyName($name) && $this->literal(':')) {
649
-            $foundSomething = false;
650
-
651
-            if ($this->valueList($value)) {
652
-                $this->append([Type::T_ASSIGN, $name, $value], $s);
653
-                $foundSomething = true;
654
-            }
655
-
656
-            if ($this->literal('{')) {
657
-                $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
658
-                $propBlock->prefix = $name;
659
-                $foundSomething = true;
660
-            } elseif ($foundSomething) {
661
-                $foundSomething = $this->end();
662
-            }
663
-
664
-            if ($foundSomething) {
665
-                return true;
666
-            }
667
-        }
668
-
669
-        $this->seek($s);
670
-
671
-        // closing a block
672
-        if ($this->literal('}')) {
673
-            $block = $this->popBlock();
674
-
675
-            if (isset($block->type) && $block->type === Type::T_INCLUDE) {
676
-                $include = $block->child;
677
-                unset($block->child);
678
-                $include[3] = $block;
679
-                $this->append($include, $s);
680
-            } elseif (empty($block->dontAppend)) {
681
-                $type = isset($block->type) ? $block->type : Type::T_BLOCK;
682
-                $this->append([$type, $block], $s);
683
-            }
684
-
685
-            return true;
686
-        }
687
-
688
-        // extra stuff
689
-        if ($this->literal(';') ||
690
-            $this->literal('<!--')
691
-        ) {
692
-            return true;
693
-        }
694
-
695
-        return false;
696
-    }
697
-
698
-    /**
699
-     * Push block onto parse tree
700
-     *
701
-     * @param array   $selectors
702
-     * @param integer $pos
703
-     *
704
-     * @return \Leafo\ScssPhp\Block
705
-     */
706
-    protected function pushBlock($selectors, $pos = 0)
707
-    {
708
-        list($line, $column) = $this->getSourcePosition($pos);
709
-
710
-        $b = new Block;
711
-        $b->sourceName   = $this->sourceName;
712
-        $b->sourceLine   = $line;
713
-        $b->sourceColumn = $column;
714
-        $b->sourceIndex  = $this->sourceIndex;
715
-        $b->selectors    = $selectors;
716
-        $b->comments     = [];
717
-        $b->parent       = $this->env;
718
-
719
-        if (! $this->env) {
720
-            $b->children = [];
721
-        } elseif (empty($this->env->children)) {
722
-            $this->env->children = $this->env->comments;
723
-            $b->children = [];
724
-            $this->env->comments = [];
725
-        } else {
726
-            $b->children = $this->env->comments;
727
-            $this->env->comments = [];
728
-        }
729
-
730
-        $this->env = $b;
731
-
732
-        return $b;
733
-    }
734
-
735
-    /**
736
-     * Push special (named) block onto parse tree
737
-     *
738
-     * @param string  $type
739
-     * @param integer $pos
740
-     *
741
-     * @return \Leafo\ScssPhp\Block
742
-     */
743
-    protected function pushSpecialBlock($type, $pos)
744
-    {
745
-        $block = $this->pushBlock(null, $pos);
746
-        $block->type = $type;
747
-
748
-        return $block;
749
-    }
750
-
751
-    /**
752
-     * Pop scope and return last block
753
-     *
754
-     * @return \Leafo\ScssPhp\Block
755
-     *
756
-     * @throws \Exception
757
-     */
758
-    protected function popBlock()
759
-    {
760
-        $block = $this->env;
761
-
762
-        if (empty($block->parent)) {
763
-            $this->throwParseError('unexpected }');
764
-        }
765
-
766
-        $this->env = $block->parent;
767
-        unset($block->parent);
768
-
769
-        $comments = $block->comments;
770
-        if (count($comments)) {
771
-            $this->env->comments = $comments;
772
-            unset($block->comments);
773
-        }
774
-
775
-        return $block;
776
-    }
777
-
778
-    /**
779
-     * Peek input stream
780
-     *
781
-     * @param string  $regex
782
-     * @param array   $out
783
-     * @param integer $from
784
-     *
785
-     * @return integer
786
-     */
787
-    protected function peek($regex, &$out, $from = null)
788
-    {
789
-        if (! isset($from)) {
790
-            $from = $this->count;
791
-        }
792
-
793
-        $r = '/' . $regex . '/' . $this->patternModifiers;
794
-        $result = preg_match($r, $this->buffer, $out, null, $from);
795
-
796
-        return $result;
797
-    }
798
-
799
-    /**
800
-     * Seek to position in input stream (or return current position in input stream)
801
-     *
802
-     * @param integer $where
803
-     *
804
-     * @return integer
805
-     */
806
-    protected function seek($where = null)
807
-    {
808
-        if ($where === null) {
809
-            return $this->count;
810
-        }
811
-
812
-        $this->count = $where;
813
-
814
-        return true;
815
-    }
816
-
817
-    /**
818
-     * Match string looking for either ending delim, escape, or string interpolation
819
-     *
820
-     * {@internal This is a workaround for preg_match's 250K string match limit. }}
821
-     *
822
-     * @param array  $m     Matches (passed by reference)
823
-     * @param string $delim Delimeter
824
-     *
825
-     * @return boolean True if match; false otherwise
826
-     */
827
-    protected function matchString(&$m, $delim)
828
-    {
829
-        $token = null;
830
-
831
-        $end = strlen($this->buffer);
832
-
833
-        // look for either ending delim, escape, or string interpolation
834
-        foreach (['#{', '\\', $delim] as $lookahead) {
835
-            $pos = strpos($this->buffer, $lookahead, $this->count);
836
-
837
-            if ($pos !== false && $pos < $end) {
838
-                $end = $pos;
839
-                $token = $lookahead;
840
-            }
841
-        }
842
-
843
-        if (! isset($token)) {
844
-            return false;
845
-        }
846
-
847
-        $match = substr($this->buffer, $this->count, $end - $this->count);
848
-        $m = [
849
-            $match . $token,
850
-            $match,
851
-            $token
852
-        ];
853
-        $this->count = $end + strlen($token);
854
-
855
-        return true;
856
-    }
857
-
858
-    /**
859
-     * Try to match something on head of buffer
860
-     *
861
-     * @param string  $regex
862
-     * @param array   $out
863
-     * @param boolean $eatWhitespace
864
-     *
865
-     * @return boolean
866
-     */
867
-    protected function match($regex, &$out, $eatWhitespace = null)
868
-    {
869
-        if (! isset($eatWhitespace)) {
870
-            $eatWhitespace = $this->eatWhiteDefault;
871
-        }
872
-
873
-        $r = '/' . $regex . '/' . $this->patternModifiers;
874
-
875
-        if (preg_match($r, $this->buffer, $out, null, $this->count)) {
876
-            $this->count += strlen($out[0]);
877
-
878
-            if ($eatWhitespace) {
879
-                $this->whitespace();
880
-            }
881
-
882
-            return true;
883
-        }
884
-
885
-        return false;
886
-    }
887
-
888
-    /**
889
-     * Match literal string
890
-     *
891
-     * @param string  $what
892
-     * @param boolean $eatWhitespace
893
-     *
894
-     * @return boolean
895
-     */
896
-    protected function literal($what, $eatWhitespace = null)
897
-    {
898
-        if (! isset($eatWhitespace)) {
899
-            $eatWhitespace = $this->eatWhiteDefault;
900
-        }
901
-
902
-        $len = strlen($what);
903
-
904
-        if (strcasecmp(substr($this->buffer, $this->count, $len), $what) === 0) {
905
-            $this->count += $len;
906
-
907
-            if ($eatWhitespace) {
908
-                $this->whitespace();
909
-            }
910
-
911
-            return true;
912
-        }
913
-
914
-        return false;
915
-    }
916
-
917
-    /**
918
-     * Match some whitespace
919
-     *
920
-     * @return boolean
921
-     */
922
-    protected function whitespace()
923
-    {
924
-        $gotWhite = false;
925
-
926
-        while (preg_match(static::$whitePattern, $this->buffer, $m, null, $this->count)) {
927
-            if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
928
-                $this->appendComment([Type::T_COMMENT, $m[1]]);
929
-
930
-                $this->commentsSeen[$this->count] = true;
931
-            }
932
-
933
-            $this->count += strlen($m[0]);
934
-            $gotWhite = true;
935
-        }
936
-
937
-        return $gotWhite;
938
-    }
939
-
940
-    /**
941
-     * Append comment to current block
942
-     *
943
-     * @param array $comment
944
-     */
945
-    protected function appendComment($comment)
946
-    {
947
-        $comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1);
948
-
949
-        $this->env->comments[] = $comment;
950
-    }
951
-
952
-    /**
953
-     * Append statement to current block
954
-     *
955
-     * @param array   $statement
956
-     * @param integer $pos
957
-     */
958
-    protected function append($statement, $pos = null)
959
-    {
960
-        if ($pos !== null) {
961
-            list($line, $column) = $this->getSourcePosition($pos);
962
-
963
-            $statement[static::SOURCE_LINE]   = $line;
964
-            $statement[static::SOURCE_COLUMN] = $column;
965
-            $statement[static::SOURCE_INDEX]  = $this->sourceIndex;
966
-        }
967
-
968
-        $this->env->children[] = $statement;
969
-
970
-        $comments = $this->env->comments;
971
-
972
-        if (count($comments)) {
973
-            $this->env->children = array_merge($this->env->children, $comments);
974
-            $this->env->comments = [];
975
-        }
976
-    }
977
-
978
-    /**
979
-     * Returns last child was appended
980
-     *
981
-     * @return array|null
982
-     */
983
-    protected function last()
984
-    {
985
-        $i = count($this->env->children) - 1;
986
-
987
-        if (isset($this->env->children[$i])) {
988
-            return $this->env->children[$i];
989
-        }
990
-    }
991
-
992
-    /**
993
-     * Parse media query list
994
-     *
995
-     * @param array $out
996
-     *
997
-     * @return boolean
998
-     */
999
-    protected function mediaQueryList(&$out)
1000
-    {
1001
-        return $this->genericList($out, 'mediaQuery', ',', false);
1002
-    }
1003
-
1004
-    /**
1005
-     * Parse media query
1006
-     *
1007
-     * @param array $out
1008
-     *
1009
-     * @return boolean
1010
-     */
1011
-    protected function mediaQuery(&$out)
1012
-    {
1013
-        $expressions = null;
1014
-        $parts = [];
1015
-
1016
-        if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) &&
1017
-            $this->mixedKeyword($mediaType)
1018
-        ) {
1019
-            $prop = [Type::T_MEDIA_TYPE];
1020
-
1021
-            if (isset($only)) {
1022
-                $prop[] = [Type::T_KEYWORD, 'only'];
1023
-            }
1024
-
1025
-            if (isset($not)) {
1026
-                $prop[] = [Type::T_KEYWORD, 'not'];
1027
-            }
1028
-
1029
-            $media = [Type::T_LIST, '', []];
1030
-
1031
-            foreach ((array) $mediaType as $type) {
1032
-                if (is_array($type)) {
1033
-                    $media[2][] = $type;
1034
-                } else {
1035
-                    $media[2][] = [Type::T_KEYWORD, $type];
1036
-                }
1037
-            }
1038
-
1039
-            $prop[]  = $media;
1040
-            $parts[] = $prop;
1041
-        }
1042
-
1043
-        if (empty($parts) || $this->literal('and')) {
1044
-            $this->genericList($expressions, 'mediaExpression', 'and', false);
1045
-
1046
-            if (is_array($expressions)) {
1047
-                $parts = array_merge($parts, $expressions[2]);
1048
-            }
1049
-        }
1050
-
1051
-        $out = $parts;
1052
-
1053
-        return true;
1054
-    }
1055
-
1056
-    /**
1057
-     * Parse media expression
1058
-     *
1059
-     * @param array $out
1060
-     *
1061
-     * @return boolean
1062
-     */
1063
-    protected function mediaExpression(&$out)
1064
-    {
1065
-        $s = $this->seek();
1066
-        $value = null;
1067
-
1068
-        if ($this->literal('(') &&
1069
-            $this->expression($feature) &&
1070
-            ($this->literal(':') && $this->expression($value) || true) &&
1071
-            $this->literal(')')
1072
-        ) {
1073
-            $out = [Type::T_MEDIA_EXPRESSION, $feature];
1074
-
1075
-            if ($value) {
1076
-                $out[] = $value;
1077
-            }
1078
-
1079
-            return true;
1080
-        }
1081
-
1082
-        $this->seek($s);
1083
-
1084
-        return false;
1085
-    }
1086
-
1087
-    /**
1088
-     * Parse argument values
1089
-     *
1090
-     * @param array $out
1091
-     *
1092
-     * @return boolean
1093
-     */
1094
-    protected function argValues(&$out)
1095
-    {
1096
-        if ($this->genericList($list, 'argValue', ',', false)) {
1097
-            $out = $list[2];
1098
-
1099
-            return true;
1100
-        }
1101
-
1102
-        return false;
1103
-    }
1104
-
1105
-    /**
1106
-     * Parse argument value
1107
-     *
1108
-     * @param array $out
1109
-     *
1110
-     * @return boolean
1111
-     */
1112
-    protected function argValue(&$out)
1113
-    {
1114
-        $s = $this->seek();
1115
-
1116
-        $keyword = null;
1117
-
1118
-        if (! $this->variable($keyword) || ! $this->literal(':')) {
1119
-            $this->seek($s);
1120
-            $keyword = null;
1121
-        }
1122
-
1123
-        if ($this->genericList($value, 'expression')) {
1124
-            $out = [$keyword, $value, false];
1125
-            $s = $this->seek();
1126
-
1127
-            if ($this->literal('...')) {
1128
-                $out[2] = true;
1129
-            } else {
1130
-                $this->seek($s);
1131
-            }
1132
-
1133
-            return true;
1134
-        }
1135
-
1136
-        return false;
1137
-    }
1138
-
1139
-    /**
1140
-     * Parse comma separated value list
1141
-     *
1142
-     * @param string $out
1143
-     *
1144
-     * @return boolean
1145
-     */
1146
-    protected function valueList(&$out)
1147
-    {
1148
-        return $this->genericList($out, 'spaceList', ',');
1149
-    }
1150
-
1151
-    /**
1152
-     * Parse space separated value list
1153
-     *
1154
-     * @param array $out
1155
-     *
1156
-     * @return boolean
1157
-     */
1158
-    protected function spaceList(&$out)
1159
-    {
1160
-        return $this->genericList($out, 'expression');
1161
-    }
1162
-
1163
-    /**
1164
-     * Parse generic list
1165
-     *
1166
-     * @param array    $out
1167
-     * @param callable $parseItem
1168
-     * @param string   $delim
1169
-     * @param boolean  $flatten
1170
-     *
1171
-     * @return boolean
1172
-     */
1173
-    protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
1174
-    {
1175
-        $s = $this->seek();
1176
-        $items = [];
1177
-
1178
-        while ($this->$parseItem($value)) {
1179
-            $items[] = $value;
1180
-
1181
-            if ($delim) {
1182
-                if (! $this->literal($delim)) {
1183
-                    break;
1184
-                }
1185
-            }
1186
-        }
1187
-
1188
-        if (count($items) === 0) {
1189
-            $this->seek($s);
1190
-
1191
-            return false;
1192
-        }
1193
-
1194
-        if ($flatten && count($items) === 1) {
1195
-            $out = $items[0];
1196
-        } else {
1197
-            $out = [Type::T_LIST, $delim, $items];
1198
-        }
1199
-
1200
-        return true;
1201
-    }
1202
-
1203
-    /**
1204
-     * Parse expression
1205
-     *
1206
-     * @param array $out
1207
-     *
1208
-     * @return boolean
1209
-     */
1210
-    protected function expression(&$out)
1211
-    {
1212
-        $s = $this->seek();
1213
-
1214
-        if ($this->literal('(')) {
1215
-            if ($this->literal(')')) {
1216
-                $out = [Type::T_LIST, '', []];
1217
-
1218
-                return true;
1219
-            }
1220
-
1221
-            if ($this->valueList($out) && $this->literal(')') && $out[0] === Type::T_LIST) {
1222
-                return true;
1223
-            }
1224
-
1225
-            $this->seek($s);
1226
-
1227
-            if ($this->map($out)) {
1228
-                return true;
1229
-            }
1230
-
1231
-            $this->seek($s);
1232
-        }
1233
-
1234
-        if ($this->value($lhs)) {
1235
-            $out = $this->expHelper($lhs, 0);
1236
-
1237
-            return true;
1238
-        }
1239
-
1240
-        return false;
1241
-    }
1242
-
1243
-    /**
1244
-     * Parse left-hand side of subexpression
1245
-     *
1246
-     * @param array   $lhs
1247
-     * @param integer $minP
1248
-     *
1249
-     * @return array
1250
-     */
1251
-    protected function expHelper($lhs, $minP)
1252
-    {
1253
-        $operators = static::$operatorPattern;
1254
-
1255
-        $ss = $this->seek();
1256
-        $whiteBefore = isset($this->buffer[$this->count - 1]) &&
1257
-            ctype_space($this->buffer[$this->count - 1]);
1258
-
1259
-        while ($this->match($operators, $m, false) && static::$precedence[$m[1]] >= $minP) {
1260
-            $whiteAfter = isset($this->buffer[$this->count]) &&
1261
-                ctype_space($this->buffer[$this->count]);
1262
-            $varAfter = isset($this->buffer[$this->count]) &&
1263
-                $this->buffer[$this->count] === '$';
1264
-
1265
-            $this->whitespace();
1266
-
1267
-            $op = $m[1];
1268
-
1269
-            // don't turn negative numbers into expressions
1270
-            if ($op === '-' && $whiteBefore && ! $whiteAfter && ! $varAfter) {
1271
-                break;
1272
-            }
1273
-
1274
-            if (! $this->value($rhs)) {
1275
-                break;
1276
-            }
1277
-
1278
-            // peek and see if rhs belongs to next operator
1279
-            if ($this->peek($operators, $next) && static::$precedence[$next[1]] > static::$precedence[$op]) {
1280
-                $rhs = $this->expHelper($rhs, static::$precedence[$next[1]]);
1281
-            }
1282
-
1283
-            $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
1284
-            $ss = $this->seek();
1285
-            $whiteBefore = isset($this->buffer[$this->count - 1]) &&
1286
-                ctype_space($this->buffer[$this->count - 1]);
1287
-        }
1288
-
1289
-        $this->seek($ss);
1290
-
1291
-        return $lhs;
1292
-    }
1293
-
1294
-    /**
1295
-     * Parse value
1296
-     *
1297
-     * @param array $out
1298
-     *
1299
-     * @return boolean
1300
-     */
1301
-    protected function value(&$out)
1302
-    {
1303
-        $s = $this->seek();
1304
-
1305
-        if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) {
1306
-            $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
1307
-
1308
-            return true;
1309
-        }
1310
-
1311
-        $this->seek($s);
1312
-
1313
-        if ($this->literal('not', false) && $this->parenValue($inner)) {
1314
-            $out = [Type::T_UNARY, 'not', $inner, $this->inParens];
1315
-
1316
-            return true;
1317
-        }
1318
-
1319
-        $this->seek($s);
1320
-
1321
-        if ($this->literal('+') && $this->value($inner)) {
1322
-            $out = [Type::T_UNARY, '+', $inner, $this->inParens];
1323
-
1324
-            return true;
1325
-        }
1326
-
1327
-        $this->seek($s);
1328
-
1329
-        // negation
1330
-        if ($this->literal('-', false) &&
1331
-            ($this->variable($inner) ||
1332
-            $this->unit($inner) ||
1333
-            $this->parenValue($inner))
1334
-        ) {
1335
-            $out = [Type::T_UNARY, '-', $inner, $this->inParens];
1336
-
1337
-            return true;
1338
-        }
1339
-
1340
-        $this->seek($s);
1341
-
1342
-        if ($this->parenValue($out) ||
1343
-            $this->interpolation($out) ||
1344
-            $this->variable($out) ||
1345
-            $this->color($out) ||
1346
-            $this->unit($out) ||
1347
-            $this->string($out) ||
1348
-            $this->func($out) ||
1349
-            $this->progid($out)
1350
-        ) {
1351
-            return true;
1352
-        }
1353
-
1354
-        if ($this->keyword($keyword)) {
1355
-            if ($keyword === 'null') {
1356
-                $out = [Type::T_NULL];
1357
-            } else {
1358
-                $out = [Type::T_KEYWORD, $keyword];
1359
-            }
1360
-
1361
-            return true;
1362
-        }
1363
-
1364
-        return false;
1365
-    }
1366
-
1367
-    /**
1368
-     * Parse parenthesized value
1369
-     *
1370
-     * @param array $out
1371
-     *
1372
-     * @return boolean
1373
-     */
1374
-    protected function parenValue(&$out)
1375
-    {
1376
-        $s = $this->seek();
1377
-
1378
-        $inParens = $this->inParens;
1379
-
1380
-        if ($this->literal('(')) {
1381
-            if ($this->literal(')')) {
1382
-                $out = [Type::T_LIST, '', []];
1383
-
1384
-                return true;
1385
-            }
1386
-
1387
-            $this->inParens = true;
1388
-
1389
-            if ($this->expression($exp) && $this->literal(')')) {
1390
-                $out = $exp;
1391
-                $this->inParens = $inParens;
1392
-
1393
-                return true;
1394
-            }
1395
-        }
1396
-
1397
-        $this->inParens = $inParens;
1398
-        $this->seek($s);
1399
-
1400
-        return false;
1401
-    }
1402
-
1403
-    /**
1404
-     * Parse "progid:"
1405
-     *
1406
-     * @param array $out
1407
-     *
1408
-     * @return boolean
1409
-     */
1410
-    protected function progid(&$out)
1411
-    {
1412
-        $s = $this->seek();
1413
-
1414
-        if ($this->literal('progid:', false) &&
1415
-            $this->openString('(', $fn) &&
1416
-            $this->literal('(')
1417
-        ) {
1418
-            $this->openString(')', $args, '(');
1419
-
1420
-            if ($this->literal(')')) {
1421
-                $out = [Type::T_STRING, '', [
1422
-                    'progid:', $fn, '(', $args, ')'
1423
-                ]];
1424
-
1425
-                return true;
1426
-            }
1427
-        }
1428
-
1429
-        $this->seek($s);
1430
-
1431
-        return false;
1432
-    }
1433
-
1434
-    /**
1435
-     * Parse function call
1436
-     *
1437
-     * @param array $out
1438
-     *
1439
-     * @return boolean
1440
-     */
1441
-    protected function func(&$func)
1442
-    {
1443
-        $s = $this->seek();
1444
-
1445
-        if ($this->keyword($name, false) &&
1446
-            $this->literal('(')
1447
-        ) {
1448
-            if ($name === 'alpha' && $this->argumentList($args)) {
1449
-                $func = [Type::T_FUNCTION, $name, [Type::T_STRING, '', $args]];
1450
-
1451
-                return true;
1452
-            }
1453
-
1454
-            if ($name !== 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) {
1455
-                $ss = $this->seek();
1456
-
1457
-                if ($this->argValues($args) && $this->literal(')')) {
1458
-                    $func = [Type::T_FUNCTION_CALL, $name, $args];
1459
-
1460
-                    return true;
1461
-                }
1462
-
1463
-                $this->seek($ss);
1464
-            }
1465
-
1466
-            if (($this->openString(')', $str, '(') || true) &&
1467
-                $this->literal(')')
1468
-            ) {
1469
-                $args = [];
1470
-
1471
-                if (! empty($str)) {
1472
-                    $args[] = [null, [Type::T_STRING, '', [$str]]];
1473
-                }
1474
-
1475
-                $func = [Type::T_FUNCTION_CALL, $name, $args];
1476
-
1477
-                return true;
1478
-            }
1479
-        }
1480
-
1481
-        $this->seek($s);
578
+			// doesn't match built in directive, do generic one
579
+			if ($this->literal('@', false) &&
580
+				$this->keyword($dirName) &&
581
+				($this->variable($dirValue) || $this->openString('{', $dirValue) || true) &&
582
+				$this->literal('{')
583
+			) {
584
+				if ($dirName === 'media') {
585
+					$directive = $this->pushSpecialBlock(Type::T_MEDIA, $s);
586
+				} else {
587
+					$directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
588
+					$directive->name = $dirName;
589
+				}
590
+
591
+				if (isset($dirValue)) {
592
+					$directive->value = $dirValue;
593
+				}
594
+
595
+				return true;
596
+			}
597
+
598
+			$this->seek($s);
599
+
600
+			return false;
601
+		}
602
+
603
+		// property shortcut
604
+		// captures most properties before having to parse a selector
605
+		if ($this->keyword($name, false) &&
606
+			$this->literal(': ') &&
607
+			$this->valueList($value) &&
608
+			$this->end()
609
+		) {
610
+			$name = [Type::T_STRING, '', [$name]];
611
+			$this->append([Type::T_ASSIGN, $name, $value], $s);
612
+
613
+			return true;
614
+		}
615
+
616
+		$this->seek($s);
617
+
618
+		// variable assigns
619
+		if ($this->variable($name) &&
620
+			$this->literal(':') &&
621
+			$this->valueList($value) &&
622
+			$this->end()
623
+		) {
624
+			// check for '!flag'
625
+			$assignmentFlags = $this->stripAssignmentFlags($value);
626
+			$this->append([Type::T_ASSIGN, $name, $value, $assignmentFlags], $s);
627
+
628
+			return true;
629
+		}
630
+
631
+		$this->seek($s);
632
+
633
+		// misc
634
+		if ($this->literal('-->')) {
635
+			return true;
636
+		}
637
+
638
+		// opening css block
639
+		if ($this->selectors($selectors) && $this->literal('{')) {
640
+			$this->pushBlock($selectors, $s);
641
+
642
+			return true;
643
+		}
644
+
645
+		$this->seek($s);
646
+
647
+		// property assign, or nested assign
648
+		if ($this->propertyName($name) && $this->literal(':')) {
649
+			$foundSomething = false;
650
+
651
+			if ($this->valueList($value)) {
652
+				$this->append([Type::T_ASSIGN, $name, $value], $s);
653
+				$foundSomething = true;
654
+			}
655
+
656
+			if ($this->literal('{')) {
657
+				$propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
658
+				$propBlock->prefix = $name;
659
+				$foundSomething = true;
660
+			} elseif ($foundSomething) {
661
+				$foundSomething = $this->end();
662
+			}
663
+
664
+			if ($foundSomething) {
665
+				return true;
666
+			}
667
+		}
668
+
669
+		$this->seek($s);
670
+
671
+		// closing a block
672
+		if ($this->literal('}')) {
673
+			$block = $this->popBlock();
674
+
675
+			if (isset($block->type) && $block->type === Type::T_INCLUDE) {
676
+				$include = $block->child;
677
+				unset($block->child);
678
+				$include[3] = $block;
679
+				$this->append($include, $s);
680
+			} elseif (empty($block->dontAppend)) {
681
+				$type = isset($block->type) ? $block->type : Type::T_BLOCK;
682
+				$this->append([$type, $block], $s);
683
+			}
684
+
685
+			return true;
686
+		}
687
+
688
+		// extra stuff
689
+		if ($this->literal(';') ||
690
+			$this->literal('<!--')
691
+		) {
692
+			return true;
693
+		}
694
+
695
+		return false;
696
+	}
697
+
698
+	/**
699
+	 * Push block onto parse tree
700
+	 *
701
+	 * @param array   $selectors
702
+	 * @param integer $pos
703
+	 *
704
+	 * @return \Leafo\ScssPhp\Block
705
+	 */
706
+	protected function pushBlock($selectors, $pos = 0)
707
+	{
708
+		list($line, $column) = $this->getSourcePosition($pos);
709
+
710
+		$b = new Block;
711
+		$b->sourceName   = $this->sourceName;
712
+		$b->sourceLine   = $line;
713
+		$b->sourceColumn = $column;
714
+		$b->sourceIndex  = $this->sourceIndex;
715
+		$b->selectors    = $selectors;
716
+		$b->comments     = [];
717
+		$b->parent       = $this->env;
718
+
719
+		if (! $this->env) {
720
+			$b->children = [];
721
+		} elseif (empty($this->env->children)) {
722
+			$this->env->children = $this->env->comments;
723
+			$b->children = [];
724
+			$this->env->comments = [];
725
+		} else {
726
+			$b->children = $this->env->comments;
727
+			$this->env->comments = [];
728
+		}
729
+
730
+		$this->env = $b;
731
+
732
+		return $b;
733
+	}
734
+
735
+	/**
736
+	 * Push special (named) block onto parse tree
737
+	 *
738
+	 * @param string  $type
739
+	 * @param integer $pos
740
+	 *
741
+	 * @return \Leafo\ScssPhp\Block
742
+	 */
743
+	protected function pushSpecialBlock($type, $pos)
744
+	{
745
+		$block = $this->pushBlock(null, $pos);
746
+		$block->type = $type;
747
+
748
+		return $block;
749
+	}
750
+
751
+	/**
752
+	 * Pop scope and return last block
753
+	 *
754
+	 * @return \Leafo\ScssPhp\Block
755
+	 *
756
+	 * @throws \Exception
757
+	 */
758
+	protected function popBlock()
759
+	{
760
+		$block = $this->env;
761
+
762
+		if (empty($block->parent)) {
763
+			$this->throwParseError('unexpected }');
764
+		}
765
+
766
+		$this->env = $block->parent;
767
+		unset($block->parent);
768
+
769
+		$comments = $block->comments;
770
+		if (count($comments)) {
771
+			$this->env->comments = $comments;
772
+			unset($block->comments);
773
+		}
774
+
775
+		return $block;
776
+	}
777
+
778
+	/**
779
+	 * Peek input stream
780
+	 *
781
+	 * @param string  $regex
782
+	 * @param array   $out
783
+	 * @param integer $from
784
+	 *
785
+	 * @return integer
786
+	 */
787
+	protected function peek($regex, &$out, $from = null)
788
+	{
789
+		if (! isset($from)) {
790
+			$from = $this->count;
791
+		}
792
+
793
+		$r = '/' . $regex . '/' . $this->patternModifiers;
794
+		$result = preg_match($r, $this->buffer, $out, null, $from);
795
+
796
+		return $result;
797
+	}
798
+
799
+	/**
800
+	 * Seek to position in input stream (or return current position in input stream)
801
+	 *
802
+	 * @param integer $where
803
+	 *
804
+	 * @return integer
805
+	 */
806
+	protected function seek($where = null)
807
+	{
808
+		if ($where === null) {
809
+			return $this->count;
810
+		}
811
+
812
+		$this->count = $where;
813
+
814
+		return true;
815
+	}
816
+
817
+	/**
818
+	 * Match string looking for either ending delim, escape, or string interpolation
819
+	 *
820
+	 * {@internal This is a workaround for preg_match's 250K string match limit. }}
821
+	 *
822
+	 * @param array  $m     Matches (passed by reference)
823
+	 * @param string $delim Delimeter
824
+	 *
825
+	 * @return boolean True if match; false otherwise
826
+	 */
827
+	protected function matchString(&$m, $delim)
828
+	{
829
+		$token = null;
830
+
831
+		$end = strlen($this->buffer);
832
+
833
+		// look for either ending delim, escape, or string interpolation
834
+		foreach (['#{', '\\', $delim] as $lookahead) {
835
+			$pos = strpos($this->buffer, $lookahead, $this->count);
836
+
837
+			if ($pos !== false && $pos < $end) {
838
+				$end = $pos;
839
+				$token = $lookahead;
840
+			}
841
+		}
842
+
843
+		if (! isset($token)) {
844
+			return false;
845
+		}
846
+
847
+		$match = substr($this->buffer, $this->count, $end - $this->count);
848
+		$m = [
849
+			$match . $token,
850
+			$match,
851
+			$token
852
+		];
853
+		$this->count = $end + strlen($token);
854
+
855
+		return true;
856
+	}
857
+
858
+	/**
859
+	 * Try to match something on head of buffer
860
+	 *
861
+	 * @param string  $regex
862
+	 * @param array   $out
863
+	 * @param boolean $eatWhitespace
864
+	 *
865
+	 * @return boolean
866
+	 */
867
+	protected function match($regex, &$out, $eatWhitespace = null)
868
+	{
869
+		if (! isset($eatWhitespace)) {
870
+			$eatWhitespace = $this->eatWhiteDefault;
871
+		}
872
+
873
+		$r = '/' . $regex . '/' . $this->patternModifiers;
874
+
875
+		if (preg_match($r, $this->buffer, $out, null, $this->count)) {
876
+			$this->count += strlen($out[0]);
877
+
878
+			if ($eatWhitespace) {
879
+				$this->whitespace();
880
+			}
881
+
882
+			return true;
883
+		}
884
+
885
+		return false;
886
+	}
887
+
888
+	/**
889
+	 * Match literal string
890
+	 *
891
+	 * @param string  $what
892
+	 * @param boolean $eatWhitespace
893
+	 *
894
+	 * @return boolean
895
+	 */
896
+	protected function literal($what, $eatWhitespace = null)
897
+	{
898
+		if (! isset($eatWhitespace)) {
899
+			$eatWhitespace = $this->eatWhiteDefault;
900
+		}
901
+
902
+		$len = strlen($what);
903
+
904
+		if (strcasecmp(substr($this->buffer, $this->count, $len), $what) === 0) {
905
+			$this->count += $len;
906
+
907
+			if ($eatWhitespace) {
908
+				$this->whitespace();
909
+			}
910
+
911
+			return true;
912
+		}
913
+
914
+		return false;
915
+	}
916
+
917
+	/**
918
+	 * Match some whitespace
919
+	 *
920
+	 * @return boolean
921
+	 */
922
+	protected function whitespace()
923
+	{
924
+		$gotWhite = false;
925
+
926
+		while (preg_match(static::$whitePattern, $this->buffer, $m, null, $this->count)) {
927
+			if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
928
+				$this->appendComment([Type::T_COMMENT, $m[1]]);
929
+
930
+				$this->commentsSeen[$this->count] = true;
931
+			}
932
+
933
+			$this->count += strlen($m[0]);
934
+			$gotWhite = true;
935
+		}
936
+
937
+		return $gotWhite;
938
+	}
939
+
940
+	/**
941
+	 * Append comment to current block
942
+	 *
943
+	 * @param array $comment
944
+	 */
945
+	protected function appendComment($comment)
946
+	{
947
+		$comment[1] = substr(preg_replace(['/^\s+/m', '/^(.)/m'], ['', ' \1'], $comment[1]), 1);
948
+
949
+		$this->env->comments[] = $comment;
950
+	}
951
+
952
+	/**
953
+	 * Append statement to current block
954
+	 *
955
+	 * @param array   $statement
956
+	 * @param integer $pos
957
+	 */
958
+	protected function append($statement, $pos = null)
959
+	{
960
+		if ($pos !== null) {
961
+			list($line, $column) = $this->getSourcePosition($pos);
962
+
963
+			$statement[static::SOURCE_LINE]   = $line;
964
+			$statement[static::SOURCE_COLUMN] = $column;
965
+			$statement[static::SOURCE_INDEX]  = $this->sourceIndex;
966
+		}
967
+
968
+		$this->env->children[] = $statement;
969
+
970
+		$comments = $this->env->comments;
971
+
972
+		if (count($comments)) {
973
+			$this->env->children = array_merge($this->env->children, $comments);
974
+			$this->env->comments = [];
975
+		}
976
+	}
977
+
978
+	/**
979
+	 * Returns last child was appended
980
+	 *
981
+	 * @return array|null
982
+	 */
983
+	protected function last()
984
+	{
985
+		$i = count($this->env->children) - 1;
986
+
987
+		if (isset($this->env->children[$i])) {
988
+			return $this->env->children[$i];
989
+		}
990
+	}
991
+
992
+	/**
993
+	 * Parse media query list
994
+	 *
995
+	 * @param array $out
996
+	 *
997
+	 * @return boolean
998
+	 */
999
+	protected function mediaQueryList(&$out)
1000
+	{
1001
+		return $this->genericList($out, 'mediaQuery', ',', false);
1002
+	}
1003
+
1004
+	/**
1005
+	 * Parse media query
1006
+	 *
1007
+	 * @param array $out
1008
+	 *
1009
+	 * @return boolean
1010
+	 */
1011
+	protected function mediaQuery(&$out)
1012
+	{
1013
+		$expressions = null;
1014
+		$parts = [];
1015
+
1016
+		if (($this->literal('only') && ($only = true) || $this->literal('not') && ($not = true) || true) &&
1017
+			$this->mixedKeyword($mediaType)
1018
+		) {
1019
+			$prop = [Type::T_MEDIA_TYPE];
1020
+
1021
+			if (isset($only)) {
1022
+				$prop[] = [Type::T_KEYWORD, 'only'];
1023
+			}
1024
+
1025
+			if (isset($not)) {
1026
+				$prop[] = [Type::T_KEYWORD, 'not'];
1027
+			}
1028
+
1029
+			$media = [Type::T_LIST, '', []];
1030
+
1031
+			foreach ((array) $mediaType as $type) {
1032
+				if (is_array($type)) {
1033
+					$media[2][] = $type;
1034
+				} else {
1035
+					$media[2][] = [Type::T_KEYWORD, $type];
1036
+				}
1037
+			}
1038
+
1039
+			$prop[]  = $media;
1040
+			$parts[] = $prop;
1041
+		}
1042
+
1043
+		if (empty($parts) || $this->literal('and')) {
1044
+			$this->genericList($expressions, 'mediaExpression', 'and', false);
1045
+
1046
+			if (is_array($expressions)) {
1047
+				$parts = array_merge($parts, $expressions[2]);
1048
+			}
1049
+		}
1050
+
1051
+		$out = $parts;
1052
+
1053
+		return true;
1054
+	}
1055
+
1056
+	/**
1057
+	 * Parse media expression
1058
+	 *
1059
+	 * @param array $out
1060
+	 *
1061
+	 * @return boolean
1062
+	 */
1063
+	protected function mediaExpression(&$out)
1064
+	{
1065
+		$s = $this->seek();
1066
+		$value = null;
1067
+
1068
+		if ($this->literal('(') &&
1069
+			$this->expression($feature) &&
1070
+			($this->literal(':') && $this->expression($value) || true) &&
1071
+			$this->literal(')')
1072
+		) {
1073
+			$out = [Type::T_MEDIA_EXPRESSION, $feature];
1074
+
1075
+			if ($value) {
1076
+				$out[] = $value;
1077
+			}
1078
+
1079
+			return true;
1080
+		}
1081
+
1082
+		$this->seek($s);
1083
+
1084
+		return false;
1085
+	}
1086
+
1087
+	/**
1088
+	 * Parse argument values
1089
+	 *
1090
+	 * @param array $out
1091
+	 *
1092
+	 * @return boolean
1093
+	 */
1094
+	protected function argValues(&$out)
1095
+	{
1096
+		if ($this->genericList($list, 'argValue', ',', false)) {
1097
+			$out = $list[2];
1098
+
1099
+			return true;
1100
+		}
1101
+
1102
+		return false;
1103
+	}
1104
+
1105
+	/**
1106
+	 * Parse argument value
1107
+	 *
1108
+	 * @param array $out
1109
+	 *
1110
+	 * @return boolean
1111
+	 */
1112
+	protected function argValue(&$out)
1113
+	{
1114
+		$s = $this->seek();
1115
+
1116
+		$keyword = null;
1117
+
1118
+		if (! $this->variable($keyword) || ! $this->literal(':')) {
1119
+			$this->seek($s);
1120
+			$keyword = null;
1121
+		}
1122
+
1123
+		if ($this->genericList($value, 'expression')) {
1124
+			$out = [$keyword, $value, false];
1125
+			$s = $this->seek();
1126
+
1127
+			if ($this->literal('...')) {
1128
+				$out[2] = true;
1129
+			} else {
1130
+				$this->seek($s);
1131
+			}
1132
+
1133
+			return true;
1134
+		}
1135
+
1136
+		return false;
1137
+	}
1138
+
1139
+	/**
1140
+	 * Parse comma separated value list
1141
+	 *
1142
+	 * @param string $out
1143
+	 *
1144
+	 * @return boolean
1145
+	 */
1146
+	protected function valueList(&$out)
1147
+	{
1148
+		return $this->genericList($out, 'spaceList', ',');
1149
+	}
1150
+
1151
+	/**
1152
+	 * Parse space separated value list
1153
+	 *
1154
+	 * @param array $out
1155
+	 *
1156
+	 * @return boolean
1157
+	 */
1158
+	protected function spaceList(&$out)
1159
+	{
1160
+		return $this->genericList($out, 'expression');
1161
+	}
1162
+
1163
+	/**
1164
+	 * Parse generic list
1165
+	 *
1166
+	 * @param array    $out
1167
+	 * @param callable $parseItem
1168
+	 * @param string   $delim
1169
+	 * @param boolean  $flatten
1170
+	 *
1171
+	 * @return boolean
1172
+	 */
1173
+	protected function genericList(&$out, $parseItem, $delim = '', $flatten = true)
1174
+	{
1175
+		$s = $this->seek();
1176
+		$items = [];
1177
+
1178
+		while ($this->$parseItem($value)) {
1179
+			$items[] = $value;
1180
+
1181
+			if ($delim) {
1182
+				if (! $this->literal($delim)) {
1183
+					break;
1184
+				}
1185
+			}
1186
+		}
1187
+
1188
+		if (count($items) === 0) {
1189
+			$this->seek($s);
1190
+
1191
+			return false;
1192
+		}
1193
+
1194
+		if ($flatten && count($items) === 1) {
1195
+			$out = $items[0];
1196
+		} else {
1197
+			$out = [Type::T_LIST, $delim, $items];
1198
+		}
1199
+
1200
+		return true;
1201
+	}
1202
+
1203
+	/**
1204
+	 * Parse expression
1205
+	 *
1206
+	 * @param array $out
1207
+	 *
1208
+	 * @return boolean
1209
+	 */
1210
+	protected function expression(&$out)
1211
+	{
1212
+		$s = $this->seek();
1213
+
1214
+		if ($this->literal('(')) {
1215
+			if ($this->literal(')')) {
1216
+				$out = [Type::T_LIST, '', []];
1217
+
1218
+				return true;
1219
+			}
1220
+
1221
+			if ($this->valueList($out) && $this->literal(')') && $out[0] === Type::T_LIST) {
1222
+				return true;
1223
+			}
1224
+
1225
+			$this->seek($s);
1226
+
1227
+			if ($this->map($out)) {
1228
+				return true;
1229
+			}
1230
+
1231
+			$this->seek($s);
1232
+		}
1233
+
1234
+		if ($this->value($lhs)) {
1235
+			$out = $this->expHelper($lhs, 0);
1236
+
1237
+			return true;
1238
+		}
1239
+
1240
+		return false;
1241
+	}
1242
+
1243
+	/**
1244
+	 * Parse left-hand side of subexpression
1245
+	 *
1246
+	 * @param array   $lhs
1247
+	 * @param integer $minP
1248
+	 *
1249
+	 * @return array
1250
+	 */
1251
+	protected function expHelper($lhs, $minP)
1252
+	{
1253
+		$operators = static::$operatorPattern;
1254
+
1255
+		$ss = $this->seek();
1256
+		$whiteBefore = isset($this->buffer[$this->count - 1]) &&
1257
+			ctype_space($this->buffer[$this->count - 1]);
1258
+
1259
+		while ($this->match($operators, $m, false) && static::$precedence[$m[1]] >= $minP) {
1260
+			$whiteAfter = isset($this->buffer[$this->count]) &&
1261
+				ctype_space($this->buffer[$this->count]);
1262
+			$varAfter = isset($this->buffer[$this->count]) &&
1263
+				$this->buffer[$this->count] === '$';
1264
+
1265
+			$this->whitespace();
1266
+
1267
+			$op = $m[1];
1268
+
1269
+			// don't turn negative numbers into expressions
1270
+			if ($op === '-' && $whiteBefore && ! $whiteAfter && ! $varAfter) {
1271
+				break;
1272
+			}
1273
+
1274
+			if (! $this->value($rhs)) {
1275
+				break;
1276
+			}
1277
+
1278
+			// peek and see if rhs belongs to next operator
1279
+			if ($this->peek($operators, $next) && static::$precedence[$next[1]] > static::$precedence[$op]) {
1280
+				$rhs = $this->expHelper($rhs, static::$precedence[$next[1]]);
1281
+			}
1282
+
1283
+			$lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
1284
+			$ss = $this->seek();
1285
+			$whiteBefore = isset($this->buffer[$this->count - 1]) &&
1286
+				ctype_space($this->buffer[$this->count - 1]);
1287
+		}
1288
+
1289
+		$this->seek($ss);
1290
+
1291
+		return $lhs;
1292
+	}
1293
+
1294
+	/**
1295
+	 * Parse value
1296
+	 *
1297
+	 * @param array $out
1298
+	 *
1299
+	 * @return boolean
1300
+	 */
1301
+	protected function value(&$out)
1302
+	{
1303
+		$s = $this->seek();
1304
+
1305
+		if ($this->literal('not', false) && $this->whitespace() && $this->value($inner)) {
1306
+			$out = [Type::T_UNARY, 'not', $inner, $this->inParens];
1307
+
1308
+			return true;
1309
+		}
1310
+
1311
+		$this->seek($s);
1312
+
1313
+		if ($this->literal('not', false) && $this->parenValue($inner)) {
1314
+			$out = [Type::T_UNARY, 'not', $inner, $this->inParens];
1315
+
1316
+			return true;
1317
+		}
1318
+
1319
+		$this->seek($s);
1320
+
1321
+		if ($this->literal('+') && $this->value($inner)) {
1322
+			$out = [Type::T_UNARY, '+', $inner, $this->inParens];
1323
+
1324
+			return true;
1325
+		}
1326
+
1327
+		$this->seek($s);
1328
+
1329
+		// negation
1330
+		if ($this->literal('-', false) &&
1331
+			($this->variable($inner) ||
1332
+			$this->unit($inner) ||
1333
+			$this->parenValue($inner))
1334
+		) {
1335
+			$out = [Type::T_UNARY, '-', $inner, $this->inParens];
1336
+
1337
+			return true;
1338
+		}
1339
+
1340
+		$this->seek($s);
1341
+
1342
+		if ($this->parenValue($out) ||
1343
+			$this->interpolation($out) ||
1344
+			$this->variable($out) ||
1345
+			$this->color($out) ||
1346
+			$this->unit($out) ||
1347
+			$this->string($out) ||
1348
+			$this->func($out) ||
1349
+			$this->progid($out)
1350
+		) {
1351
+			return true;
1352
+		}
1353
+
1354
+		if ($this->keyword($keyword)) {
1355
+			if ($keyword === 'null') {
1356
+				$out = [Type::T_NULL];
1357
+			} else {
1358
+				$out = [Type::T_KEYWORD, $keyword];
1359
+			}
1360
+
1361
+			return true;
1362
+		}
1363
+
1364
+		return false;
1365
+	}
1366
+
1367
+	/**
1368
+	 * Parse parenthesized value
1369
+	 *
1370
+	 * @param array $out
1371
+	 *
1372
+	 * @return boolean
1373
+	 */
1374
+	protected function parenValue(&$out)
1375
+	{
1376
+		$s = $this->seek();
1377
+
1378
+		$inParens = $this->inParens;
1379
+
1380
+		if ($this->literal('(')) {
1381
+			if ($this->literal(')')) {
1382
+				$out = [Type::T_LIST, '', []];
1383
+
1384
+				return true;
1385
+			}
1386
+
1387
+			$this->inParens = true;
1388
+
1389
+			if ($this->expression($exp) && $this->literal(')')) {
1390
+				$out = $exp;
1391
+				$this->inParens = $inParens;
1392
+
1393
+				return true;
1394
+			}
1395
+		}
1396
+
1397
+		$this->inParens = $inParens;
1398
+		$this->seek($s);
1399
+
1400
+		return false;
1401
+	}
1402
+
1403
+	/**
1404
+	 * Parse "progid:"
1405
+	 *
1406
+	 * @param array $out
1407
+	 *
1408
+	 * @return boolean
1409
+	 */
1410
+	protected function progid(&$out)
1411
+	{
1412
+		$s = $this->seek();
1413
+
1414
+		if ($this->literal('progid:', false) &&
1415
+			$this->openString('(', $fn) &&
1416
+			$this->literal('(')
1417
+		) {
1418
+			$this->openString(')', $args, '(');
1419
+
1420
+			if ($this->literal(')')) {
1421
+				$out = [Type::T_STRING, '', [
1422
+					'progid:', $fn, '(', $args, ')'
1423
+				]];
1424
+
1425
+				return true;
1426
+			}
1427
+		}
1428
+
1429
+		$this->seek($s);
1430
+
1431
+		return false;
1432
+	}
1433
+
1434
+	/**
1435
+	 * Parse function call
1436
+	 *
1437
+	 * @param array $out
1438
+	 *
1439
+	 * @return boolean
1440
+	 */
1441
+	protected function func(&$func)
1442
+	{
1443
+		$s = $this->seek();
1444
+
1445
+		if ($this->keyword($name, false) &&
1446
+			$this->literal('(')
1447
+		) {
1448
+			if ($name === 'alpha' && $this->argumentList($args)) {
1449
+				$func = [Type::T_FUNCTION, $name, [Type::T_STRING, '', $args]];
1450
+
1451
+				return true;
1452
+			}
1453
+
1454
+			if ($name !== 'expression' && ! preg_match('/^(-[a-z]+-)?calc$/', $name)) {
1455
+				$ss = $this->seek();
1456
+
1457
+				if ($this->argValues($args) && $this->literal(')')) {
1458
+					$func = [Type::T_FUNCTION_CALL, $name, $args];
1459
+
1460
+					return true;
1461
+				}
1462
+
1463
+				$this->seek($ss);
1464
+			}
1465
+
1466
+			if (($this->openString(')', $str, '(') || true) &&
1467
+				$this->literal(')')
1468
+			) {
1469
+				$args = [];
1470
+
1471
+				if (! empty($str)) {
1472
+					$args[] = [null, [Type::T_STRING, '', [$str]]];
1473
+				}
1474
+
1475
+				$func = [Type::T_FUNCTION_CALL, $name, $args];
1476
+
1477
+				return true;
1478
+			}
1479
+		}
1480
+
1481
+		$this->seek($s);
1482 1482
 
1483
-        return false;
1484
-    }
1483
+		return false;
1484
+	}
1485 1485
 
1486
-    /**
1487
-     * Parse function call argument list
1488
-     *
1489
-     * @param array $out
1490
-     *
1491
-     * @return boolean
1492
-     */
1493
-    protected function argumentList(&$out)
1494
-    {
1495
-        $s = $this->seek();
1496
-        $this->literal('(');
1497
-
1498
-        $args = [];
1499
-
1500
-        while ($this->keyword($var)) {
1501
-            if ($this->literal('=') && $this->expression($exp)) {
1502
-                $args[] = [Type::T_STRING, '', [$var . '=']];
1503
-                $arg = $exp;
1504
-            } else {
1505
-                break;
1506
-            }
1507
-
1508
-            $args[] = $arg;
1509
-
1510
-            if (! $this->literal(',')) {
1511
-                break;
1512
-            }
1513
-
1514
-            $args[] = [Type::T_STRING, '', [', ']];
1515
-        }
1516
-
1517
-        if (! $this->literal(')') || ! count($args)) {
1518
-            $this->seek($s);
1519
-
1520
-            return false;
1521
-        }
1486
+	/**
1487
+	 * Parse function call argument list
1488
+	 *
1489
+	 * @param array $out
1490
+	 *
1491
+	 * @return boolean
1492
+	 */
1493
+	protected function argumentList(&$out)
1494
+	{
1495
+		$s = $this->seek();
1496
+		$this->literal('(');
1497
+
1498
+		$args = [];
1499
+
1500
+		while ($this->keyword($var)) {
1501
+			if ($this->literal('=') && $this->expression($exp)) {
1502
+				$args[] = [Type::T_STRING, '', [$var . '=']];
1503
+				$arg = $exp;
1504
+			} else {
1505
+				break;
1506
+			}
1507
+
1508
+			$args[] = $arg;
1509
+
1510
+			if (! $this->literal(',')) {
1511
+				break;
1512
+			}
1513
+
1514
+			$args[] = [Type::T_STRING, '', [', ']];
1515
+		}
1516
+
1517
+		if (! $this->literal(')') || ! count($args)) {
1518
+			$this->seek($s);
1519
+
1520
+			return false;
1521
+		}
1522 1522
 
1523
-        $out = $args;
1523
+		$out = $args;
1524 1524
 
1525
-        return true;
1526
-    }
1525
+		return true;
1526
+	}
1527 1527
 
1528
-    /**
1529
-     * Parse mixin/function definition  argument list
1530
-     *
1531
-     * @param array $out
1532
-     *
1533
-     * @return boolean
1534
-     */
1535
-    protected function argumentDef(&$out)
1536
-    {
1537
-        $s = $this->seek();
1538
-        $this->literal('(');
1539
-
1540
-        $args = [];
1541
-
1542
-        while ($this->variable($var)) {
1543
-            $arg = [$var[1], null, false];
1544
-
1545
-            $ss = $this->seek();
1546
-
1547
-            if ($this->literal(':') && $this->genericList($defaultVal, 'expression')) {
1548
-                $arg[1] = $defaultVal;
1549
-            } else {
1550
-                $this->seek($ss);
1551
-            }
1552
-
1553
-            $ss = $this->seek();
1554
-
1555
-            if ($this->literal('...')) {
1556
-                $sss = $this->seek();
1557
-
1558
-                if (! $this->literal(')')) {
1559
-                    $this->throwParseError('... has to be after the final argument');
1560
-                }
1561
-
1562
-                $arg[2] = true;
1563
-                $this->seek($sss);
1564
-            } else {
1565
-                $this->seek($ss);
1566
-            }
1567
-
1568
-            $args[] = $arg;
1569
-
1570
-            if (! $this->literal(',')) {
1571
-                break;
1572
-            }
1573
-        }
1574
-
1575
-        if (! $this->literal(')')) {
1576
-            $this->seek($s);
1577
-
1578
-            return false;
1579
-        }
1580
-
1581
-        $out = $args;
1582
-
1583
-        return true;
1584
-    }
1585
-
1586
-    /**
1587
-     * Parse map
1588
-     *
1589
-     * @param array $out
1590
-     *
1591
-     * @return boolean
1592
-     */
1593
-    protected function map(&$out)
1594
-    {
1595
-        $s = $this->seek();
1596
-
1597
-        if (! $this->literal('(')) {
1598
-            return false;
1599
-        }
1600
-
1601
-        $keys = [];
1602
-        $values = [];
1603
-
1604
-        while ($this->genericList($key, 'expression') && $this->literal(':') &&
1605
-            $this->genericList($value, 'expression')
1606
-        ) {
1607
-            $keys[] = $key;
1608
-            $values[] = $value;
1609
-
1610
-            if (! $this->literal(',')) {
1611
-                break;
1612
-            }
1613
-        }
1614
-
1615
-        if (! count($keys) || ! $this->literal(')')) {
1616
-            $this->seek($s);
1617
-
1618
-            return false;
1619
-        }
1620
-
1621
-        $out = [Type::T_MAP, $keys, $values];
1622
-
1623
-        return true;
1624
-    }
1625
-
1626
-    /**
1627
-     * Parse color
1628
-     *
1629
-     * @param array $out
1630
-     *
1631
-     * @return boolean
1632
-     */
1633
-    protected function color(&$out)
1634
-    {
1635
-        $color = [Type::T_COLOR];
1636
-
1637
-        if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
1638
-            if (isset($m[3])) {
1639
-                $num = hexdec($m[3]);
1640
-
1641
-                foreach ([3, 2, 1] as $i) {
1642
-                    $t = $num & 0xf;
1643
-                    $color[$i] = $t << 4 | $t;
1644
-                    $num >>= 4;
1645
-                }
1646
-            } else {
1647
-                $num = hexdec($m[2]);
1648
-
1649
-                foreach ([3, 2, 1] as $i) {
1650
-                    $color[$i] = $num & 0xff;
1651
-                    $num >>= 8;
1652
-                }
1653
-            }
1654
-
1655
-            $out = $color;
1656
-
1657
-            return true;
1658
-        }
1659
-
1660
-        return false;
1661
-    }
1662
-
1663
-    /**
1664
-     * Parse number with unit
1665
-     *
1666
-     * @param array $out
1667
-     *
1668
-     * @return boolean
1669
-     */
1670
-    protected function unit(&$unit)
1671
-    {
1672
-        if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?', $m)) {
1673
-            $unit = new Node\Number($m[1], empty($m[3]) ? '' : $m[3]);
1674
-
1675
-            return true;
1676
-        }
1677
-
1678
-        return false;
1679
-    }
1680
-
1681
-    /**
1682
-     * Parse string
1683
-     *
1684
-     * @param array $out
1685
-     *
1686
-     * @return boolean
1687
-     */
1688
-    protected function string(&$out)
1689
-    {
1690
-        $s = $this->seek();
1691
-
1692
-        if ($this->literal('"', false)) {
1693
-            $delim = '"';
1694
-        } elseif ($this->literal("'", false)) {
1695
-            $delim = "'";
1696
-        } else {
1697
-            return false;
1698
-        }
1699
-
1700
-        $content = [];
1701
-        $oldWhite = $this->eatWhiteDefault;
1702
-        $this->eatWhiteDefault = false;
1703
-        $hasInterpolation = false;
1704
-
1705
-        while ($this->matchString($m, $delim)) {
1706
-            if ($m[1] !== '') {
1707
-                $content[] = $m[1];
1708
-            }
1709
-
1710
-            if ($m[2] === '#{') {
1711
-                $this->count -= strlen($m[2]);
1712
-
1713
-                if ($this->interpolation($inter, false)) {
1714
-                    $content[] = $inter;
1715
-                    $hasInterpolation = true;
1716
-                } else {
1717
-                    $this->count += strlen($m[2]);
1718
-                    $content[] = '#{'; // ignore it
1719
-                }
1720
-            } elseif ($m[2] === '\\') {
1721
-                if ($this->literal('"', false)) {
1722
-                    $content[] = $m[2] . '"';
1723
-                } elseif ($this->literal("'", false)) {
1724
-                    $content[] = $m[2] . "'";
1725
-                } else {
1726
-                    $content[] = $m[2];
1727
-                }
1728
-            } else {
1729
-                $this->count -= strlen($delim);
1730
-                break; // delim
1731
-            }
1732
-        }
1733
-
1734
-        $this->eatWhiteDefault = $oldWhite;
1735
-
1736
-        if ($this->literal($delim)) {
1737
-            if ($hasInterpolation) {
1738
-                $delim = '"';
1739
-
1740
-                foreach ($content as &$string) {
1741
-                    if ($string === "\\'") {
1742
-                        $string = "'";
1743
-                    } elseif ($string === '\\"') {
1744
-                        $string = '"';
1745
-                    }
1746
-                }
1747
-            }
1748
-
1749
-            $out = [Type::T_STRING, $delim, $content];
1750
-
1751
-            return true;
1752
-        }
1753
-
1754
-        $this->seek($s);
1755
-
1756
-        return false;
1757
-    }
1758
-
1759
-    /**
1760
-     * Parse keyword or interpolation
1761
-     *
1762
-     * @param array $out
1763
-     *
1764
-     * @return boolean
1765
-     */
1766
-    protected function mixedKeyword(&$out)
1767
-    {
1768
-        $parts = [];
1769
-
1770
-        $oldWhite = $this->eatWhiteDefault;
1771
-        $this->eatWhiteDefault = false;
1772
-
1773
-        for (;;) {
1774
-            if ($this->keyword($key)) {
1775
-                $parts[] = $key;
1776
-                continue;
1777
-            }
1778
-
1779
-            if ($this->interpolation($inter)) {
1780
-                $parts[] = $inter;
1781
-                continue;
1782
-            }
1783
-
1784
-            break;
1785
-        }
1786
-
1787
-        $this->eatWhiteDefault = $oldWhite;
1788
-
1789
-        if (count($parts) === 0) {
1790
-            return false;
1791
-        }
1792
-
1793
-        if ($this->eatWhiteDefault) {
1794
-            $this->whitespace();
1795
-        }
1796
-
1797
-        $out = $parts;
1798
-
1799
-        return true;
1800
-    }
1801
-
1802
-    /**
1803
-     * Parse an unbounded string stopped by $end
1804
-     *
1805
-     * @param string $end
1806
-     * @param array  $out
1807
-     * @param string $nestingOpen
1808
-     *
1809
-     * @return boolean
1810
-     */
1811
-    protected function openString($end, &$out, $nestingOpen = null)
1812
-    {
1813
-        $oldWhite = $this->eatWhiteDefault;
1814
-        $this->eatWhiteDefault = false;
1815
-
1816
-        $patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . static::$commentPattern . ')';
1817
-
1818
-        $nestingLevel = 0;
1819
-
1820
-        $content = [];
1821
-
1822
-        while ($this->match($patt, $m, false)) {
1823
-            if (isset($m[1]) && $m[1] !== '') {
1824
-                $content[] = $m[1];
1825
-
1826
-                if ($nestingOpen) {
1827
-                    $nestingLevel += substr_count($m[1], $nestingOpen);
1828
-                }
1829
-            }
1830
-
1831
-            $tok = $m[2];
1832
-
1833
-            $this->count-= strlen($tok);
1834
-
1835
-            if ($tok === $end && ! $nestingLevel--) {
1836
-                break;
1837
-            }
1838
-
1839
-            if (($tok === "'" || $tok === '"') && $this->string($str)) {
1840
-                $content[] = $str;
1841
-                continue;
1842
-            }
1843
-
1844
-            if ($tok === '#{' && $this->interpolation($inter)) {
1845
-                $content[] = $inter;
1846
-                continue;
1847
-            }
1848
-
1849
-            $content[] = $tok;
1850
-            $this->count+= strlen($tok);
1851
-        }
1852
-
1853
-        $this->eatWhiteDefault = $oldWhite;
1854
-
1855
-        if (count($content) === 0) {
1856
-            return false;
1857
-        }
1858
-
1859
-        // trim the end
1860
-        if (is_string(end($content))) {
1861
-            $content[count($content) - 1] = rtrim(end($content));
1862
-        }
1863
-
1864
-        $out = [Type::T_STRING, '', $content];
1865
-
1866
-        return true;
1867
-    }
1868
-
1869
-    /**
1870
-     * Parser interpolation
1871
-     *
1872
-     * @param array   $out
1873
-     * @param boolean $lookWhite save information about whitespace before and after
1874
-     *
1875
-     * @return boolean
1876
-     */
1877
-    protected function interpolation(&$out, $lookWhite = true)
1878
-    {
1879
-        $oldWhite = $this->eatWhiteDefault;
1880
-        $this->eatWhiteDefault = true;
1881
-
1882
-        $s = $this->seek();
1883
-
1884
-        if ($this->literal('#{') && $this->valueList($value) && $this->literal('}', false)) {
1885
-            if ($lookWhite) {
1886
-                $left = preg_match('/\s/', $this->buffer[$s - 1]) ? ' ' : '';
1887
-                $right = preg_match('/\s/', $this->buffer[$this->count]) ? ' ': '';
1888
-            } else {
1889
-                $left = $right = false;
1890
-            }
1891
-
1892
-            $out = [Type::T_INTERPOLATE, $value, $left, $right];
1893
-            $this->eatWhiteDefault = $oldWhite;
1894
-
1895
-            if ($this->eatWhiteDefault) {
1896
-                $this->whitespace();
1897
-            }
1898
-
1899
-            return true;
1900
-        }
1901
-
1902
-        $this->seek($s);
1903
-        $this->eatWhiteDefault = $oldWhite;
1904
-
1905
-        return false;
1906
-    }
1907
-
1908
-    /**
1909
-     * Parse property name (as an array of parts or a string)
1910
-     *
1911
-     * @param array $out
1912
-     *
1913
-     * @return boolean
1914
-     */
1915
-    protected function propertyName(&$out)
1916
-    {
1917
-        $parts = [];
1918
-
1919
-        $oldWhite = $this->eatWhiteDefault;
1920
-        $this->eatWhiteDefault = false;
1921
-
1922
-        for (;;) {
1923
-            if ($this->interpolation($inter)) {
1924
-                $parts[] = $inter;
1925
-                continue;
1926
-            }
1927
-
1928
-            if ($this->keyword($text)) {
1929
-                $parts[] = $text;
1930
-                continue;
1931
-            }
1932
-
1933
-            if (count($parts) === 0 && $this->match('[:.#]', $m, false)) {
1934
-                // css hacks
1935
-                $parts[] = $m[0];
1936
-                continue;
1937
-            }
1938
-
1939
-            break;
1940
-        }
1941
-
1942
-        $this->eatWhiteDefault = $oldWhite;
1943
-
1944
-        if (count($parts) === 0) {
1945
-            return false;
1946
-        }
1947
-
1948
-        // match comment hack
1949
-        if (preg_match(
1950
-            static::$whitePattern,
1951
-            $this->buffer,
1952
-            $m,
1953
-            null,
1954
-            $this->count
1955
-        )) {
1956
-            if (! empty($m[0])) {
1957
-                $parts[] = $m[0];
1958
-                $this->count += strlen($m[0]);
1959
-            }
1960
-        }
1961
-
1962
-        $this->whitespace(); // get any extra whitespace
1963
-
1964
-        $out = [Type::T_STRING, '', $parts];
1965
-
1966
-        return true;
1967
-    }
1968
-
1969
-    /**
1970
-     * Parse comma separated selector list
1971
-     *
1972
-     * @param array $out
1973
-     *
1974
-     * @return boolean
1975
-     */
1976
-    protected function selectors(&$out)
1977
-    {
1978
-        $s = $this->seek();
1979
-        $selectors = [];
1980
-
1981
-        while ($this->selector($sel)) {
1982
-            $selectors[] = $sel;
1983
-
1984
-            if (! $this->literal(',')) {
1985
-                break;
1986
-            }
1987
-
1988
-            while ($this->literal(',')) {
1989
-                ; // ignore extra
1990
-            }
1991
-        }
1992
-
1993
-        if (count($selectors) === 0) {
1994
-            $this->seek($s);
1995
-
1996
-            return false;
1997
-        }
1998
-
1999
-        $out = $selectors;
2000
-
2001
-        return true;
2002
-    }
2003
-
2004
-    /**
2005
-     * Parse whitespace separated selector list
2006
-     *
2007
-     * @param array $out
2008
-     *
2009
-     * @return boolean
2010
-     */
2011
-    protected function selector(&$out)
2012
-    {
2013
-        $selector = [];
2014
-
2015
-        for (;;) {
2016
-            if ($this->match('[>+~]+', $m)) {
2017
-                $selector[] = [$m[0]];
2018
-                continue;
2019
-            }
2020
-
2021
-            if ($this->selectorSingle($part)) {
2022
-                $selector[] = $part;
2023
-                $this->match('\s+', $m);
2024
-                continue;
2025
-            }
2026
-
2027
-            if ($this->match('\/[^\/]+\/', $m)) {
2028
-                $selector[] = [$m[0]];
2029
-                continue;
2030
-            }
2031
-
2032
-            break;
2033
-        }
2034
-
2035
-        if (count($selector) === 0) {
2036
-            return false;
2037
-        }
2038
-
2039
-        $out = $selector;
2040
-        return true;
2041
-    }
2042
-
2043
-    /**
2044
-     * Parse the parts that make up a selector
2045
-     *
2046
-     * {@internal
2047
-     *     div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
2048
-     * }}
2049
-     *
2050
-     * @param array $out
2051
-     *
2052
-     * @return boolean
2053
-     */
2054
-    protected function selectorSingle(&$out)
2055
-    {
2056
-        $oldWhite = $this->eatWhiteDefault;
2057
-        $this->eatWhiteDefault = false;
2058
-
2059
-        $parts = [];
2060
-
2061
-        if ($this->literal('*', false)) {
2062
-            $parts[] = '*';
2063
-        }
2064
-
2065
-        for (;;) {
2066
-            // see if we can stop early
2067
-            if ($this->match('\s*[{,]', $m)) {
2068
-                $this->count--;
2069
-                break;
2070
-            }
2071
-
2072
-            $s = $this->seek();
2073
-
2074
-            // self
2075
-            if ($this->literal('&', false)) {
2076
-                $parts[] = Compiler::$selfSelector;
2077
-                continue;
2078
-            }
2079
-
2080
-            if ($this->literal('.', false)) {
2081
-                $parts[] = '.';
2082
-                continue;
2083
-            }
2084
-
2085
-            if ($this->literal('|', false)) {
2086
-                $parts[] = '|';
2087
-                continue;
2088
-            }
2089
-
2090
-            if ($this->match('\\\\\S', $m)) {
2091
-                $parts[] = $m[0];
2092
-                continue;
2093
-            }
2094
-
2095
-            // for keyframes
2096
-            if ($this->unit($unit)) {
2097
-                $parts[] = $unit;
2098
-                continue;
2099
-            }
2100
-
2101
-            if ($this->keyword($name)) {
2102
-                $parts[] = $name;
2103
-                continue;
2104
-            }
2105
-
2106
-            if ($this->interpolation($inter)) {
2107
-                $parts[] = $inter;
2108
-                continue;
2109
-            }
2110
-
2111
-            if ($this->literal('%', false) && $this->placeholder($placeholder)) {
2112
-                $parts[] = '%';
2113
-                $parts[] = $placeholder;
2114
-                continue;
2115
-            }
2116
-
2117
-            if ($this->literal('#', false)) {
2118
-                $parts[] = '#';
2119
-                continue;
2120
-            }
2121
-
2122
-            // a pseudo selector
2123
-            if ($this->match('::?', $m) && $this->mixedKeyword($nameParts)) {
2124
-                $parts[] = $m[0];
2125
-
2126
-                foreach ($nameParts as $sub) {
2127
-                    $parts[] = $sub;
2128
-                }
2129
-
2130
-                $ss = $this->seek();
2131
-
2132
-                if ($this->literal('(') &&
2133
-                    ($this->openString(')', $str, '(') || true) &&
2134
-                    $this->literal(')')
2135
-                ) {
2136
-                    $parts[] = '(';
2137
-
2138
-                    if (! empty($str)) {
2139
-                        $parts[] = $str;
2140
-                    }
2141
-
2142
-                    $parts[] = ')';
2143
-                } else {
2144
-                    $this->seek($ss);
2145
-                }
2146
-
2147
-                continue;
2148
-            }
2149
-
2150
-            $this->seek($s);
2151
-
2152
-            // attribute selector
2153
-            if ($this->literal('[') &&
2154
-               ($this->openString(']', $str, '[') || true) &&
2155
-               $this->literal(']')
2156
-            ) {
2157
-                $parts[] = '[';
2158
-
2159
-                if (! empty($str)) {
2160
-                    $parts[] = $str;
2161
-                }
2162
-
2163
-                $parts[] = ']';
2164
-
2165
-                continue;
2166
-            }
2167
-
2168
-            $this->seek($s);
2169
-
2170
-            break;
2171
-        }
2172
-
2173
-        $this->eatWhiteDefault = $oldWhite;
2174
-
2175
-        if (count($parts) === 0) {
2176
-            return false;
2177
-        }
2178
-
2179
-        $out = $parts;
2180
-
2181
-        return true;
2182
-    }
2183
-
2184
-    /**
2185
-     * Parse a variable
2186
-     *
2187
-     * @param array $out
2188
-     *
2189
-     * @return boolean
2190
-     */
2191
-    protected function variable(&$out)
2192
-    {
2193
-        $s = $this->seek();
2194
-
2195
-        if ($this->literal('$', false) && $this->keyword($name)) {
2196
-            $out = [Type::T_VARIABLE, $name];
2197
-
2198
-            return true;
2199
-        }
2200
-
2201
-        $this->seek($s);
2202
-
2203
-        return false;
2204
-    }
2205
-
2206
-    /**
2207
-     * Parse a keyword
2208
-     *
2209
-     * @param string  $word
2210
-     * @param boolean $eatWhitespace
2211
-     *
2212
-     * @return boolean
2213
-     */
2214
-    protected function keyword(&$word, $eatWhitespace = null)
2215
-    {
2216
-        if ($this->match(
2217
-            $this->utf8
2218
-                ? '(([\pL\w_\-\*!"\']|[\\\\].)([\pL\w\-_"\']|[\\\\].)*)'
2219
-                : '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
2220
-            $m,
2221
-            $eatWhitespace
2222
-        )) {
2223
-            $word = $m[1];
2224
-
2225
-            return true;
2226
-        }
2227
-
2228
-        return false;
2229
-    }
2230
-
2231
-    /**
2232
-     * Parse a placeholder
2233
-     *
2234
-     * @param string $placeholder
2235
-     *
2236
-     * @return boolean
2237
-     */
2238
-    protected function placeholder(&$placeholder)
2239
-    {
2240
-        if ($this->match(
2241
-            $this->utf8
2242
-                ? '([\pL\w\-_]+|#[{][$][\pL\w\-_]+[}])'
2243
-                : '([\w\-_]+|#[{][$][\w\-_]+[}])',
2244
-            $m
2245
-        )) {
2246
-            $placeholder = $m[1];
2247
-
2248
-            return true;
2249
-        }
2250
-
2251
-        return false;
2252
-    }
2253
-
2254
-    /**
2255
-     * Parse a url
2256
-     *
2257
-     * @param array $out
2258
-     *
2259
-     * @return boolean
2260
-     */
2261
-    protected function url(&$out)
2262
-    {
2263
-        if ($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))', $m)) {
2264
-            $out = [Type::T_STRING, '', ['url(' . $m[2] . $m[3] . $m[2] . ')']];
2265
-
2266
-            return true;
2267
-        }
2268
-
2269
-        return false;
2270
-    }
2271
-
2272
-    /**
2273
-     * Consume an end of statement delimiter
2274
-     *
2275
-     * @return boolean
2276
-     */
2277
-    protected function end()
2278
-    {
2279
-        if ($this->literal(';')) {
2280
-            return true;
2281
-        }
2282
-
2283
-        if ($this->count === strlen($this->buffer) || $this->buffer[$this->count] === '}') {
2284
-            // if there is end of file or a closing block next then we don't need a ;
2285
-            return true;
2286
-        }
2287
-
2288
-        return false;
2289
-    }
2290
-
2291
-    /**
2292
-     * Strip assignment flag from the list
2293
-     *
2294
-     * @param array $value
2295
-     *
2296
-     * @return array
2297
-     */
2298
-    protected function stripAssignmentFlags(&$value)
2299
-    {
2300
-        $flags = [];
2301
-
2302
-        for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) {
2303
-            $lastNode = &$token[2][$s - 1];
2304
-
2305
-            while ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global'])) {
2306
-                array_pop($token[2]);
2307
-
2308
-                $node = end($token[2]);
2309
-
2310
-                $token = $this->flattenList($token);
2311
-
2312
-                $flags[] = $lastNode[1];
2313
-
2314
-                $lastNode = $node;
2315
-            }
2316
-        }
2317
-
2318
-        return $flags;
2319
-    }
2320
-
2321
-    /**
2322
-     * Strip optional flag from selector list
2323
-     *
2324
-     * @param array $selectors
2325
-     *
2326
-     * @return string
2327
-     */
2328
-    protected function stripOptionalFlag(&$selectors)
2329
-    {
2330
-        $optional = false;
2331
-
2332
-        $selector = end($selectors);
2333
-        $part = end($selector);
2334
-
2335
-        if ($part === ['!optional']) {
2336
-            array_pop($selectors[count($selectors) - 1]);
2337
-
2338
-            $optional = true;
2339
-        }
2340
-
2341
-        return $optional;
2342
-    }
2343
-
2344
-    /**
2345
-     * Turn list of length 1 into value type
2346
-     *
2347
-     * @param array $value
2348
-     *
2349
-     * @return array
2350
-     */
2351
-    protected function flattenList($value)
2352
-    {
2353
-        if ($value[0] === Type::T_LIST && count($value[2]) === 1) {
2354
-            return $this->flattenList($value[2][0]);
2355
-        }
2356
-
2357
-        return $value;
2358
-    }
2359
-
2360
-    /**
2361
-     * @deprecated
2362
-     *
2363
-     * {@internal
2364
-     *     advance counter to next occurrence of $what
2365
-     *     $until - don't include $what in advance
2366
-     *     $allowNewline, if string, will be used as valid char set
2367
-     * }}
2368
-     */
2369
-    protected function to($what, &$out, $until = false, $allowNewline = false)
2370
-    {
2371
-        if (is_string($allowNewline)) {
2372
-            $validChars = $allowNewline;
2373
-        } else {
2374
-            $validChars = $allowNewline ? '.' : "[^\n]";
2375
-        }
2376
-
2377
-        if (! $this->match('(' . $validChars . '*?)' . $this->pregQuote($what), $m, ! $until)) {
2378
-            return false;
2379
-        }
2380
-
2381
-        if ($until) {
2382
-            $this->count -= strlen($what); // give back $what
2383
-        }
2384
-
2385
-        $out = $m[1];
2386
-
2387
-        return true;
2388
-    }
2389
-
2390
-    /**
2391
-     * @deprecated
2392
-     */
2393
-    protected function show()
2394
-    {
2395
-        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
2396
-            return $m[1];
2397
-        }
2398
-
2399
-        return '';
2400
-    }
2401
-
2402
-    /**
2403
-     * Quote regular expression
2404
-     *
2405
-     * @param string $what
2406
-     *
2407
-     * @return string
2408
-     */
2409
-    private function pregQuote($what)
2410
-    {
2411
-        return preg_quote($what, '/');
2412
-    }
2413
-
2414
-    /**
2415
-     * Extract line numbers from buffer
2416
-     *
2417
-     * @param string $buffer
2418
-     */
2419
-    private function extractLineNumbers($buffer)
2420
-    {
2421
-        $this->sourcePositions = [0 => 0];
2422
-        $prev = 0;
2423
-
2424
-        while (($pos = strpos($buffer, "\n", $prev)) !== false) {
2425
-            $this->sourcePositions[] = $pos;
2426
-            $prev = $pos + 1;
2427
-        }
2428
-
2429
-        $this->sourcePositions[] = strlen($buffer);
2430
-
2431
-        if (substr($buffer, -1) !== "\n") {
2432
-            $this->sourcePositions[] = strlen($buffer) + 1;
2433
-        }
2434
-    }
2435
-
2436
-    /**
2437
-     * Get source line number and column (given character position in the buffer)
2438
-     *
2439
-     * @param integer $pos
2440
-     *
2441
-     * @return integer
2442
-     */
2443
-    private function getSourcePosition($pos)
2444
-    {
2445
-        $low = 0;
2446
-        $high = count($this->sourcePositions);
2447
-
2448
-        while ($low < $high) {
2449
-            $mid = (int) (($high + $low) / 2);
2450
-
2451
-            if ($pos < $this->sourcePositions[$mid]) {
2452
-                $high = $mid - 1;
2453
-                continue;
2454
-            }
2455
-
2456
-            if ($pos >= $this->sourcePositions[$mid + 1]) {
2457
-                $low = $mid + 1;
2458
-                continue;
2459
-            }
2460
-
2461
-            return [$mid + 1, $pos - $this->sourcePositions[$mid]];
2462
-        }
2463
-
2464
-        return [$low + 1, $pos - $this->sourcePositions[$low]];
2465
-    }
2466
-
2467
-    /**
2468
-     * Save internal encoding
2469
-     */
2470
-    private function saveEncoding()
2471
-    {
2472
-        if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
2473
-            return;
2474
-        }
2475
-
2476
-        $iniDirective = 'mbstring' . '.func_overload'; // deprecated in PHP 7.2
2477
-
2478
-        if (ini_get($iniDirective) & 2) {
2479
-            $this->encoding = mb_internal_encoding();
2480
-
2481
-            mb_internal_encoding('iso-8859-1');
2482
-        }
2483
-    }
2484
-
2485
-    /**
2486
-     * Restore internal encoding
2487
-     */
2488
-    private function restoreEncoding()
2489
-    {
2490
-        if ($this->encoding) {
2491
-            mb_internal_encoding($this->encoding);
2492
-        }
2493
-    }
1528
+	/**
1529
+	 * Parse mixin/function definition  argument list
1530
+	 *
1531
+	 * @param array $out
1532
+	 *
1533
+	 * @return boolean
1534
+	 */
1535
+	protected function argumentDef(&$out)
1536
+	{
1537
+		$s = $this->seek();
1538
+		$this->literal('(');
1539
+
1540
+		$args = [];
1541
+
1542
+		while ($this->variable($var)) {
1543
+			$arg = [$var[1], null, false];
1544
+
1545
+			$ss = $this->seek();
1546
+
1547
+			if ($this->literal(':') && $this->genericList($defaultVal, 'expression')) {
1548
+				$arg[1] = $defaultVal;
1549
+			} else {
1550
+				$this->seek($ss);
1551
+			}
1552
+
1553
+			$ss = $this->seek();
1554
+
1555
+			if ($this->literal('...')) {
1556
+				$sss = $this->seek();
1557
+
1558
+				if (! $this->literal(')')) {
1559
+					$this->throwParseError('... has to be after the final argument');
1560
+				}
1561
+
1562
+				$arg[2] = true;
1563
+				$this->seek($sss);
1564
+			} else {
1565
+				$this->seek($ss);
1566
+			}
1567
+
1568
+			$args[] = $arg;
1569
+
1570
+			if (! $this->literal(',')) {
1571
+				break;
1572
+			}
1573
+		}
1574
+
1575
+		if (! $this->literal(')')) {
1576
+			$this->seek($s);
1577
+
1578
+			return false;
1579
+		}
1580
+
1581
+		$out = $args;
1582
+
1583
+		return true;
1584
+	}
1585
+
1586
+	/**
1587
+	 * Parse map
1588
+	 *
1589
+	 * @param array $out
1590
+	 *
1591
+	 * @return boolean
1592
+	 */
1593
+	protected function map(&$out)
1594
+	{
1595
+		$s = $this->seek();
1596
+
1597
+		if (! $this->literal('(')) {
1598
+			return false;
1599
+		}
1600
+
1601
+		$keys = [];
1602
+		$values = [];
1603
+
1604
+		while ($this->genericList($key, 'expression') && $this->literal(':') &&
1605
+			$this->genericList($value, 'expression')
1606
+		) {
1607
+			$keys[] = $key;
1608
+			$values[] = $value;
1609
+
1610
+			if (! $this->literal(',')) {
1611
+				break;
1612
+			}
1613
+		}
1614
+
1615
+		if (! count($keys) || ! $this->literal(')')) {
1616
+			$this->seek($s);
1617
+
1618
+			return false;
1619
+		}
1620
+
1621
+		$out = [Type::T_MAP, $keys, $values];
1622
+
1623
+		return true;
1624
+	}
1625
+
1626
+	/**
1627
+	 * Parse color
1628
+	 *
1629
+	 * @param array $out
1630
+	 *
1631
+	 * @return boolean
1632
+	 */
1633
+	protected function color(&$out)
1634
+	{
1635
+		$color = [Type::T_COLOR];
1636
+
1637
+		if ($this->match('(#([0-9a-f]{6})|#([0-9a-f]{3}))', $m)) {
1638
+			if (isset($m[3])) {
1639
+				$num = hexdec($m[3]);
1640
+
1641
+				foreach ([3, 2, 1] as $i) {
1642
+					$t = $num & 0xf;
1643
+					$color[$i] = $t << 4 | $t;
1644
+					$num >>= 4;
1645
+				}
1646
+			} else {
1647
+				$num = hexdec($m[2]);
1648
+
1649
+				foreach ([3, 2, 1] as $i) {
1650
+					$color[$i] = $num & 0xff;
1651
+					$num >>= 8;
1652
+				}
1653
+			}
1654
+
1655
+			$out = $color;
1656
+
1657
+			return true;
1658
+		}
1659
+
1660
+		return false;
1661
+	}
1662
+
1663
+	/**
1664
+	 * Parse number with unit
1665
+	 *
1666
+	 * @param array $out
1667
+	 *
1668
+	 * @return boolean
1669
+	 */
1670
+	protected function unit(&$unit)
1671
+	{
1672
+		if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?', $m)) {
1673
+			$unit = new Node\Number($m[1], empty($m[3]) ? '' : $m[3]);
1674
+
1675
+			return true;
1676
+		}
1677
+
1678
+		return false;
1679
+	}
1680
+
1681
+	/**
1682
+	 * Parse string
1683
+	 *
1684
+	 * @param array $out
1685
+	 *
1686
+	 * @return boolean
1687
+	 */
1688
+	protected function string(&$out)
1689
+	{
1690
+		$s = $this->seek();
1691
+
1692
+		if ($this->literal('"', false)) {
1693
+			$delim = '"';
1694
+		} elseif ($this->literal("'", false)) {
1695
+			$delim = "'";
1696
+		} else {
1697
+			return false;
1698
+		}
1699
+
1700
+		$content = [];
1701
+		$oldWhite = $this->eatWhiteDefault;
1702
+		$this->eatWhiteDefault = false;
1703
+		$hasInterpolation = false;
1704
+
1705
+		while ($this->matchString($m, $delim)) {
1706
+			if ($m[1] !== '') {
1707
+				$content[] = $m[1];
1708
+			}
1709
+
1710
+			if ($m[2] === '#{') {
1711
+				$this->count -= strlen($m[2]);
1712
+
1713
+				if ($this->interpolation($inter, false)) {
1714
+					$content[] = $inter;
1715
+					$hasInterpolation = true;
1716
+				} else {
1717
+					$this->count += strlen($m[2]);
1718
+					$content[] = '#{'; // ignore it
1719
+				}
1720
+			} elseif ($m[2] === '\\') {
1721
+				if ($this->literal('"', false)) {
1722
+					$content[] = $m[2] . '"';
1723
+				} elseif ($this->literal("'", false)) {
1724
+					$content[] = $m[2] . "'";
1725
+				} else {
1726
+					$content[] = $m[2];
1727
+				}
1728
+			} else {
1729
+				$this->count -= strlen($delim);
1730
+				break; // delim
1731
+			}
1732
+		}
1733
+
1734
+		$this->eatWhiteDefault = $oldWhite;
1735
+
1736
+		if ($this->literal($delim)) {
1737
+			if ($hasInterpolation) {
1738
+				$delim = '"';
1739
+
1740
+				foreach ($content as &$string) {
1741
+					if ($string === "\\'") {
1742
+						$string = "'";
1743
+					} elseif ($string === '\\"') {
1744
+						$string = '"';
1745
+					}
1746
+				}
1747
+			}
1748
+
1749
+			$out = [Type::T_STRING, $delim, $content];
1750
+
1751
+			return true;
1752
+		}
1753
+
1754
+		$this->seek($s);
1755
+
1756
+		return false;
1757
+	}
1758
+
1759
+	/**
1760
+	 * Parse keyword or interpolation
1761
+	 *
1762
+	 * @param array $out
1763
+	 *
1764
+	 * @return boolean
1765
+	 */
1766
+	protected function mixedKeyword(&$out)
1767
+	{
1768
+		$parts = [];
1769
+
1770
+		$oldWhite = $this->eatWhiteDefault;
1771
+		$this->eatWhiteDefault = false;
1772
+
1773
+		for (;;) {
1774
+			if ($this->keyword($key)) {
1775
+				$parts[] = $key;
1776
+				continue;
1777
+			}
1778
+
1779
+			if ($this->interpolation($inter)) {
1780
+				$parts[] = $inter;
1781
+				continue;
1782
+			}
1783
+
1784
+			break;
1785
+		}
1786
+
1787
+		$this->eatWhiteDefault = $oldWhite;
1788
+
1789
+		if (count($parts) === 0) {
1790
+			return false;
1791
+		}
1792
+
1793
+		if ($this->eatWhiteDefault) {
1794
+			$this->whitespace();
1795
+		}
1796
+
1797
+		$out = $parts;
1798
+
1799
+		return true;
1800
+	}
1801
+
1802
+	/**
1803
+	 * Parse an unbounded string stopped by $end
1804
+	 *
1805
+	 * @param string $end
1806
+	 * @param array  $out
1807
+	 * @param string $nestingOpen
1808
+	 *
1809
+	 * @return boolean
1810
+	 */
1811
+	protected function openString($end, &$out, $nestingOpen = null)
1812
+	{
1813
+		$oldWhite = $this->eatWhiteDefault;
1814
+		$this->eatWhiteDefault = false;
1815
+
1816
+		$patt = '(.*?)([\'"]|#\{|' . $this->pregQuote($end) . '|' . static::$commentPattern . ')';
1817
+
1818
+		$nestingLevel = 0;
1819
+
1820
+		$content = [];
1821
+
1822
+		while ($this->match($patt, $m, false)) {
1823
+			if (isset($m[1]) && $m[1] !== '') {
1824
+				$content[] = $m[1];
1825
+
1826
+				if ($nestingOpen) {
1827
+					$nestingLevel += substr_count($m[1], $nestingOpen);
1828
+				}
1829
+			}
1830
+
1831
+			$tok = $m[2];
1832
+
1833
+			$this->count-= strlen($tok);
1834
+
1835
+			if ($tok === $end && ! $nestingLevel--) {
1836
+				break;
1837
+			}
1838
+
1839
+			if (($tok === "'" || $tok === '"') && $this->string($str)) {
1840
+				$content[] = $str;
1841
+				continue;
1842
+			}
1843
+
1844
+			if ($tok === '#{' && $this->interpolation($inter)) {
1845
+				$content[] = $inter;
1846
+				continue;
1847
+			}
1848
+
1849
+			$content[] = $tok;
1850
+			$this->count+= strlen($tok);
1851
+		}
1852
+
1853
+		$this->eatWhiteDefault = $oldWhite;
1854
+
1855
+		if (count($content) === 0) {
1856
+			return false;
1857
+		}
1858
+
1859
+		// trim the end
1860
+		if (is_string(end($content))) {
1861
+			$content[count($content) - 1] = rtrim(end($content));
1862
+		}
1863
+
1864
+		$out = [Type::T_STRING, '', $content];
1865
+
1866
+		return true;
1867
+	}
1868
+
1869
+	/**
1870
+	 * Parser interpolation
1871
+	 *
1872
+	 * @param array   $out
1873
+	 * @param boolean $lookWhite save information about whitespace before and after
1874
+	 *
1875
+	 * @return boolean
1876
+	 */
1877
+	protected function interpolation(&$out, $lookWhite = true)
1878
+	{
1879
+		$oldWhite = $this->eatWhiteDefault;
1880
+		$this->eatWhiteDefault = true;
1881
+
1882
+		$s = $this->seek();
1883
+
1884
+		if ($this->literal('#{') && $this->valueList($value) && $this->literal('}', false)) {
1885
+			if ($lookWhite) {
1886
+				$left = preg_match('/\s/', $this->buffer[$s - 1]) ? ' ' : '';
1887
+				$right = preg_match('/\s/', $this->buffer[$this->count]) ? ' ': '';
1888
+			} else {
1889
+				$left = $right = false;
1890
+			}
1891
+
1892
+			$out = [Type::T_INTERPOLATE, $value, $left, $right];
1893
+			$this->eatWhiteDefault = $oldWhite;
1894
+
1895
+			if ($this->eatWhiteDefault) {
1896
+				$this->whitespace();
1897
+			}
1898
+
1899
+			return true;
1900
+		}
1901
+
1902
+		$this->seek($s);
1903
+		$this->eatWhiteDefault = $oldWhite;
1904
+
1905
+		return false;
1906
+	}
1907
+
1908
+	/**
1909
+	 * Parse property name (as an array of parts or a string)
1910
+	 *
1911
+	 * @param array $out
1912
+	 *
1913
+	 * @return boolean
1914
+	 */
1915
+	protected function propertyName(&$out)
1916
+	{
1917
+		$parts = [];
1918
+
1919
+		$oldWhite = $this->eatWhiteDefault;
1920
+		$this->eatWhiteDefault = false;
1921
+
1922
+		for (;;) {
1923
+			if ($this->interpolation($inter)) {
1924
+				$parts[] = $inter;
1925
+				continue;
1926
+			}
1927
+
1928
+			if ($this->keyword($text)) {
1929
+				$parts[] = $text;
1930
+				continue;
1931
+			}
1932
+
1933
+			if (count($parts) === 0 && $this->match('[:.#]', $m, false)) {
1934
+				// css hacks
1935
+				$parts[] = $m[0];
1936
+				continue;
1937
+			}
1938
+
1939
+			break;
1940
+		}
1941
+
1942
+		$this->eatWhiteDefault = $oldWhite;
1943
+
1944
+		if (count($parts) === 0) {
1945
+			return false;
1946
+		}
1947
+
1948
+		// match comment hack
1949
+		if (preg_match(
1950
+			static::$whitePattern,
1951
+			$this->buffer,
1952
+			$m,
1953
+			null,
1954
+			$this->count
1955
+		)) {
1956
+			if (! empty($m[0])) {
1957
+				$parts[] = $m[0];
1958
+				$this->count += strlen($m[0]);
1959
+			}
1960
+		}
1961
+
1962
+		$this->whitespace(); // get any extra whitespace
1963
+
1964
+		$out = [Type::T_STRING, '', $parts];
1965
+
1966
+		return true;
1967
+	}
1968
+
1969
+	/**
1970
+	 * Parse comma separated selector list
1971
+	 *
1972
+	 * @param array $out
1973
+	 *
1974
+	 * @return boolean
1975
+	 */
1976
+	protected function selectors(&$out)
1977
+	{
1978
+		$s = $this->seek();
1979
+		$selectors = [];
1980
+
1981
+		while ($this->selector($sel)) {
1982
+			$selectors[] = $sel;
1983
+
1984
+			if (! $this->literal(',')) {
1985
+				break;
1986
+			}
1987
+
1988
+			while ($this->literal(',')) {
1989
+				; // ignore extra
1990
+			}
1991
+		}
1992
+
1993
+		if (count($selectors) === 0) {
1994
+			$this->seek($s);
1995
+
1996
+			return false;
1997
+		}
1998
+
1999
+		$out = $selectors;
2000
+
2001
+		return true;
2002
+	}
2003
+
2004
+	/**
2005
+	 * Parse whitespace separated selector list
2006
+	 *
2007
+	 * @param array $out
2008
+	 *
2009
+	 * @return boolean
2010
+	 */
2011
+	protected function selector(&$out)
2012
+	{
2013
+		$selector = [];
2014
+
2015
+		for (;;) {
2016
+			if ($this->match('[>+~]+', $m)) {
2017
+				$selector[] = [$m[0]];
2018
+				continue;
2019
+			}
2020
+
2021
+			if ($this->selectorSingle($part)) {
2022
+				$selector[] = $part;
2023
+				$this->match('\s+', $m);
2024
+				continue;
2025
+			}
2026
+
2027
+			if ($this->match('\/[^\/]+\/', $m)) {
2028
+				$selector[] = [$m[0]];
2029
+				continue;
2030
+			}
2031
+
2032
+			break;
2033
+		}
2034
+
2035
+		if (count($selector) === 0) {
2036
+			return false;
2037
+		}
2038
+
2039
+		$out = $selector;
2040
+		return true;
2041
+	}
2042
+
2043
+	/**
2044
+	 * Parse the parts that make up a selector
2045
+	 *
2046
+	 * {@internal
2047
+	 *     div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
2048
+	 * }}
2049
+	 *
2050
+	 * @param array $out
2051
+	 *
2052
+	 * @return boolean
2053
+	 */
2054
+	protected function selectorSingle(&$out)
2055
+	{
2056
+		$oldWhite = $this->eatWhiteDefault;
2057
+		$this->eatWhiteDefault = false;
2058
+
2059
+		$parts = [];
2060
+
2061
+		if ($this->literal('*', false)) {
2062
+			$parts[] = '*';
2063
+		}
2064
+
2065
+		for (;;) {
2066
+			// see if we can stop early
2067
+			if ($this->match('\s*[{,]', $m)) {
2068
+				$this->count--;
2069
+				break;
2070
+			}
2071
+
2072
+			$s = $this->seek();
2073
+
2074
+			// self
2075
+			if ($this->literal('&', false)) {
2076
+				$parts[] = Compiler::$selfSelector;
2077
+				continue;
2078
+			}
2079
+
2080
+			if ($this->literal('.', false)) {
2081
+				$parts[] = '.';
2082
+				continue;
2083
+			}
2084
+
2085
+			if ($this->literal('|', false)) {
2086
+				$parts[] = '|';
2087
+				continue;
2088
+			}
2089
+
2090
+			if ($this->match('\\\\\S', $m)) {
2091
+				$parts[] = $m[0];
2092
+				continue;
2093
+			}
2094
+
2095
+			// for keyframes
2096
+			if ($this->unit($unit)) {
2097
+				$parts[] = $unit;
2098
+				continue;
2099
+			}
2100
+
2101
+			if ($this->keyword($name)) {
2102
+				$parts[] = $name;
2103
+				continue;
2104
+			}
2105
+
2106
+			if ($this->interpolation($inter)) {
2107
+				$parts[] = $inter;
2108
+				continue;
2109
+			}
2110
+
2111
+			if ($this->literal('%', false) && $this->placeholder($placeholder)) {
2112
+				$parts[] = '%';
2113
+				$parts[] = $placeholder;
2114
+				continue;
2115
+			}
2116
+
2117
+			if ($this->literal('#', false)) {
2118
+				$parts[] = '#';
2119
+				continue;
2120
+			}
2121
+
2122
+			// a pseudo selector
2123
+			if ($this->match('::?', $m) && $this->mixedKeyword($nameParts)) {
2124
+				$parts[] = $m[0];
2125
+
2126
+				foreach ($nameParts as $sub) {
2127
+					$parts[] = $sub;
2128
+				}
2129
+
2130
+				$ss = $this->seek();
2131
+
2132
+				if ($this->literal('(') &&
2133
+					($this->openString(')', $str, '(') || true) &&
2134
+					$this->literal(')')
2135
+				) {
2136
+					$parts[] = '(';
2137
+
2138
+					if (! empty($str)) {
2139
+						$parts[] = $str;
2140
+					}
2141
+
2142
+					$parts[] = ')';
2143
+				} else {
2144
+					$this->seek($ss);
2145
+				}
2146
+
2147
+				continue;
2148
+			}
2149
+
2150
+			$this->seek($s);
2151
+
2152
+			// attribute selector
2153
+			if ($this->literal('[') &&
2154
+			   ($this->openString(']', $str, '[') || true) &&
2155
+			   $this->literal(']')
2156
+			) {
2157
+				$parts[] = '[';
2158
+
2159
+				if (! empty($str)) {
2160
+					$parts[] = $str;
2161
+				}
2162
+
2163
+				$parts[] = ']';
2164
+
2165
+				continue;
2166
+			}
2167
+
2168
+			$this->seek($s);
2169
+
2170
+			break;
2171
+		}
2172
+
2173
+		$this->eatWhiteDefault = $oldWhite;
2174
+
2175
+		if (count($parts) === 0) {
2176
+			return false;
2177
+		}
2178
+
2179
+		$out = $parts;
2180
+
2181
+		return true;
2182
+	}
2183
+
2184
+	/**
2185
+	 * Parse a variable
2186
+	 *
2187
+	 * @param array $out
2188
+	 *
2189
+	 * @return boolean
2190
+	 */
2191
+	protected function variable(&$out)
2192
+	{
2193
+		$s = $this->seek();
2194
+
2195
+		if ($this->literal('$', false) && $this->keyword($name)) {
2196
+			$out = [Type::T_VARIABLE, $name];
2197
+
2198
+			return true;
2199
+		}
2200
+
2201
+		$this->seek($s);
2202
+
2203
+		return false;
2204
+	}
2205
+
2206
+	/**
2207
+	 * Parse a keyword
2208
+	 *
2209
+	 * @param string  $word
2210
+	 * @param boolean $eatWhitespace
2211
+	 *
2212
+	 * @return boolean
2213
+	 */
2214
+	protected function keyword(&$word, $eatWhitespace = null)
2215
+	{
2216
+		if ($this->match(
2217
+			$this->utf8
2218
+				? '(([\pL\w_\-\*!"\']|[\\\\].)([\pL\w\-_"\']|[\\\\].)*)'
2219
+				: '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
2220
+			$m,
2221
+			$eatWhitespace
2222
+		)) {
2223
+			$word = $m[1];
2224
+
2225
+			return true;
2226
+		}
2227
+
2228
+		return false;
2229
+	}
2230
+
2231
+	/**
2232
+	 * Parse a placeholder
2233
+	 *
2234
+	 * @param string $placeholder
2235
+	 *
2236
+	 * @return boolean
2237
+	 */
2238
+	protected function placeholder(&$placeholder)
2239
+	{
2240
+		if ($this->match(
2241
+			$this->utf8
2242
+				? '([\pL\w\-_]+|#[{][$][\pL\w\-_]+[}])'
2243
+				: '([\w\-_]+|#[{][$][\w\-_]+[}])',
2244
+			$m
2245
+		)) {
2246
+			$placeholder = $m[1];
2247
+
2248
+			return true;
2249
+		}
2250
+
2251
+		return false;
2252
+	}
2253
+
2254
+	/**
2255
+	 * Parse a url
2256
+	 *
2257
+	 * @param array $out
2258
+	 *
2259
+	 * @return boolean
2260
+	 */
2261
+	protected function url(&$out)
2262
+	{
2263
+		if ($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))', $m)) {
2264
+			$out = [Type::T_STRING, '', ['url(' . $m[2] . $m[3] . $m[2] . ')']];
2265
+
2266
+			return true;
2267
+		}
2268
+
2269
+		return false;
2270
+	}
2271
+
2272
+	/**
2273
+	 * Consume an end of statement delimiter
2274
+	 *
2275
+	 * @return boolean
2276
+	 */
2277
+	protected function end()
2278
+	{
2279
+		if ($this->literal(';')) {
2280
+			return true;
2281
+		}
2282
+
2283
+		if ($this->count === strlen($this->buffer) || $this->buffer[$this->count] === '}') {
2284
+			// if there is end of file or a closing block next then we don't need a ;
2285
+			return true;
2286
+		}
2287
+
2288
+		return false;
2289
+	}
2290
+
2291
+	/**
2292
+	 * Strip assignment flag from the list
2293
+	 *
2294
+	 * @param array $value
2295
+	 *
2296
+	 * @return array
2297
+	 */
2298
+	protected function stripAssignmentFlags(&$value)
2299
+	{
2300
+		$flags = [];
2301
+
2302
+		for ($token = &$value; $token[0] === Type::T_LIST && ($s = count($token[2])); $token = &$lastNode) {
2303
+			$lastNode = &$token[2][$s - 1];
2304
+
2305
+			while ($lastNode[0] === Type::T_KEYWORD && in_array($lastNode[1], ['!default', '!global'])) {
2306
+				array_pop($token[2]);
2307
+
2308
+				$node = end($token[2]);
2309
+
2310
+				$token = $this->flattenList($token);
2311
+
2312
+				$flags[] = $lastNode[1];
2313
+
2314
+				$lastNode = $node;
2315
+			}
2316
+		}
2317
+
2318
+		return $flags;
2319
+	}
2320
+
2321
+	/**
2322
+	 * Strip optional flag from selector list
2323
+	 *
2324
+	 * @param array $selectors
2325
+	 *
2326
+	 * @return string
2327
+	 */
2328
+	protected function stripOptionalFlag(&$selectors)
2329
+	{
2330
+		$optional = false;
2331
+
2332
+		$selector = end($selectors);
2333
+		$part = end($selector);
2334
+
2335
+		if ($part === ['!optional']) {
2336
+			array_pop($selectors[count($selectors) - 1]);
2337
+
2338
+			$optional = true;
2339
+		}
2340
+
2341
+		return $optional;
2342
+	}
2343
+
2344
+	/**
2345
+	 * Turn list of length 1 into value type
2346
+	 *
2347
+	 * @param array $value
2348
+	 *
2349
+	 * @return array
2350
+	 */
2351
+	protected function flattenList($value)
2352
+	{
2353
+		if ($value[0] === Type::T_LIST && count($value[2]) === 1) {
2354
+			return $this->flattenList($value[2][0]);
2355
+		}
2356
+
2357
+		return $value;
2358
+	}
2359
+
2360
+	/**
2361
+	 * @deprecated
2362
+	 *
2363
+	 * {@internal
2364
+	 *     advance counter to next occurrence of $what
2365
+	 *     $until - don't include $what in advance
2366
+	 *     $allowNewline, if string, will be used as valid char set
2367
+	 * }}
2368
+	 */
2369
+	protected function to($what, &$out, $until = false, $allowNewline = false)
2370
+	{
2371
+		if (is_string($allowNewline)) {
2372
+			$validChars = $allowNewline;
2373
+		} else {
2374
+			$validChars = $allowNewline ? '.' : "[^\n]";
2375
+		}
2376
+
2377
+		if (! $this->match('(' . $validChars . '*?)' . $this->pregQuote($what), $m, ! $until)) {
2378
+			return false;
2379
+		}
2380
+
2381
+		if ($until) {
2382
+			$this->count -= strlen($what); // give back $what
2383
+		}
2384
+
2385
+		$out = $m[1];
2386
+
2387
+		return true;
2388
+	}
2389
+
2390
+	/**
2391
+	 * @deprecated
2392
+	 */
2393
+	protected function show()
2394
+	{
2395
+		if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
2396
+			return $m[1];
2397
+		}
2398
+
2399
+		return '';
2400
+	}
2401
+
2402
+	/**
2403
+	 * Quote regular expression
2404
+	 *
2405
+	 * @param string $what
2406
+	 *
2407
+	 * @return string
2408
+	 */
2409
+	private function pregQuote($what)
2410
+	{
2411
+		return preg_quote($what, '/');
2412
+	}
2413
+
2414
+	/**
2415
+	 * Extract line numbers from buffer
2416
+	 *
2417
+	 * @param string $buffer
2418
+	 */
2419
+	private function extractLineNumbers($buffer)
2420
+	{
2421
+		$this->sourcePositions = [0 => 0];
2422
+		$prev = 0;
2423
+
2424
+		while (($pos = strpos($buffer, "\n", $prev)) !== false) {
2425
+			$this->sourcePositions[] = $pos;
2426
+			$prev = $pos + 1;
2427
+		}
2428
+
2429
+		$this->sourcePositions[] = strlen($buffer);
2430
+
2431
+		if (substr($buffer, -1) !== "\n") {
2432
+			$this->sourcePositions[] = strlen($buffer) + 1;
2433
+		}
2434
+	}
2435
+
2436
+	/**
2437
+	 * Get source line number and column (given character position in the buffer)
2438
+	 *
2439
+	 * @param integer $pos
2440
+	 *
2441
+	 * @return integer
2442
+	 */
2443
+	private function getSourcePosition($pos)
2444
+	{
2445
+		$low = 0;
2446
+		$high = count($this->sourcePositions);
2447
+
2448
+		while ($low < $high) {
2449
+			$mid = (int) (($high + $low) / 2);
2450
+
2451
+			if ($pos < $this->sourcePositions[$mid]) {
2452
+				$high = $mid - 1;
2453
+				continue;
2454
+			}
2455
+
2456
+			if ($pos >= $this->sourcePositions[$mid + 1]) {
2457
+				$low = $mid + 1;
2458
+				continue;
2459
+			}
2460
+
2461
+			return [$mid + 1, $pos - $this->sourcePositions[$mid]];
2462
+		}
2463
+
2464
+		return [$low + 1, $pos - $this->sourcePositions[$low]];
2465
+	}
2466
+
2467
+	/**
2468
+	 * Save internal encoding
2469
+	 */
2470
+	private function saveEncoding()
2471
+	{
2472
+		if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
2473
+			return;
2474
+		}
2475
+
2476
+		$iniDirective = 'mbstring' . '.func_overload'; // deprecated in PHP 7.2
2477
+
2478
+		if (ini_get($iniDirective) & 2) {
2479
+			$this->encoding = mb_internal_encoding();
2480
+
2481
+			mb_internal_encoding('iso-8859-1');
2482
+		}
2483
+	}
2484
+
2485
+	/**
2486
+	 * Restore internal encoding
2487
+	 */
2488
+	private function restoreEncoding()
2489
+	{
2490
+		if ($this->encoding) {
2491
+			mb_internal_encoding($this->encoding);
2492
+		}
2493
+	}
2494 2494
 }
Please login to merge, or discard this patch.